Browse Source

Private search in RS & search highlight query & sidebar stripe animation

master
morethanwords 4 years ago
parent
commit
dd877bee83
  1. 236
      src/components/appSearch.ts
  2. 8
      src/components/emoticonsDropdown.ts
  3. 20
      src/components/misc.ts
  4. 58
      src/lib/appManagers/appDialogsManager.ts
  5. 10
      src/lib/appManagers/appImManager.ts
  6. 272
      src/lib/appManagers/appSidebarLeft.ts
  7. 36
      src/lib/appManagers/appSidebarRight.ts
  8. 27
      src/lib/richtextprocessor.js
  9. 7
      src/lib/utils.js
  10. 33
      src/scss/partials/_chatlist.scss
  11. 2
      src/scss/partials/_emojiDropdown.scss
  12. 45
      src/scss/partials/_leftSidebar.scss
  13. 27
      src/scss/partials/_rightSIdebar.scss
  14. 53
      src/scss/partials/_sidebar.scss
  15. 73
      src/scss/style.scss

236
src/components/appSearch.ts

@ -0,0 +1,236 @@ @@ -0,0 +1,236 @@
import appDialogsManager from "../lib/appManagers/appDialogsManager";
import Scrollable from "./scrollable";
import appMessagesIDsManager from "../lib/appManagers/appMessagesIDsManager";
import appUsersManager from "../lib/appManagers/appUsersManager";
import appPeersManager from '../lib/appManagers/appPeersManager';
import appMessagesManager from "../lib/appManagers/appMessagesManager";
import { numberWithCommas, escapeRegExp } from "../lib/utils";
import { formatPhoneNumber } from "./misc";
export class SearchGroup {
container: HTMLDivElement;
nameEl: HTMLDivElement;
list: HTMLUListElement;
constructor(public name: string, public type: string) {
this.list = document.createElement('ul');
this.container = document.createElement('div');
this.nameEl = document.createElement('div');
this.nameEl.classList.add('search-group__name');
this.nameEl.innerText = name;
this.container.classList.add('search-group');
this.container.append(this.nameEl, this.list);
this.container.style.display = 'none';
appDialogsManager.setListClickListener(this.list);
}
clear() {
this.container.style.display = 'none';
this.list.innerHTML = '';
}
setActive() {
this.container.style.display = '';
}
}
export default class AppSearch {
private minMsgID = 0;
private loadedCount = 0;
private foundCount = 0;
private offsetRate = 0;
private searchPromise: Promise<void> = null;
private searchTimeout: number = 0;
private query = '';
private listsContainer: HTMLDivElement = null;
private peerID = 0; // 0 - means global
private scrollable: Scrollable;
constructor(public container: HTMLDivElement, public searchInput: HTMLInputElement, public searchGroups: {[group: string]: SearchGroup}) {
this.scrollable = new Scrollable(this.container);
this.listsContainer = this.scrollable.container;
for(let i in this.searchGroups) {
this.listsContainer.append(this.searchGroups[i].container);
}
this.searchInput.addEventListener('input', (e) => {
let value = this.searchInput.value;
if(!value.trim()) {
//this.peerID = 0;
return;
}
this.query = value;
this.reset(false);
this.searchMore();
});
this.scrollable.onScrolledBottom = () => {
if(!this.query.trim()) return;
if(!this.searchTimeout) {
this.searchTimeout = setTimeout(() => {
this.searchMore();
this.searchTimeout = 0;
}, 0);
}
};
}
public reset(all = true) {
if(all) {
this.searchInput.value = '';
this.query = '';
this.peerID = 0;
}
this.minMsgID = 0;
this.loadedCount = 0;
this.foundCount = 0;
this.offsetRate = 0;
for(let i in this.searchGroups) {
this.searchGroups[i].clear();
}
this.searchPromise = null;
}
public beginSearch(peerID?: number) {
if(peerID) {
this.peerID = peerID;
}
this.searchInput.focus();
}
private searchMore() {
if(this.searchPromise) return this.searchPromise;
let query = this.query;
if(!query.trim()) return;
if(this.loadedCount != 0 && this.loadedCount >= this.foundCount) {
return Promise.resolve();
}
let maxID = appMessagesIDsManager.getMessageIDInfo(this.minMsgID)[0];
if(!this.peerID && !maxID) {
appUsersManager.searchContacts(query, 20).then((contacts: any) => {
if(this.searchInput.value != query) {
return;
}
///////this.log('input search contacts result:', contacts);
let setResults = (results: any, group: SearchGroup, showMembersCount = false) => {
results.forEach((inputPeer: any) => {
let peerID = appPeersManager.getPeerID(inputPeer);
let peer = appPeersManager.getPeer(peerID);
let originalDialog = appMessagesManager.getDialogByPeerID(peerID)[0];
//////////this.log('contacts peer', peer);
if(!originalDialog) {
/////////this.log('no original dialog by peerID:', peerID);
originalDialog = {
peerID: peerID,
pFlags: {},
peer: peer
};
}
let {dialog, dom} = appDialogsManager.addDialog(originalDialog, group.list, false);
if(showMembersCount && (peer.participants_count || peer.participants)) {
let regExp = new RegExp(`(${escapeRegExp(query)})`, 'gi');
dom.titleSpan.innerHTML = dom.titleSpan.innerHTML.replace(regExp, '<i>$1</i>');
let isChannel = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID);
let participants_count = peer.participants_count || peer.participants.participants.length;
let subtitle = numberWithCommas(participants_count) + ' ' + (isChannel ? 'subscribers' : 'members');
dom.lastMessageSpan.innerText = subtitle;
} else {
let username = appPeersManager.getPeerUsername(peerID);
if(!username) {
let user = appUsersManager.getUser(peerID);
if(user && user.phone) {
username = '+' + formatPhoneNumber(user.phone).formatted;
}
} else {
username = '@' + username;
}
dom.lastMessageSpan.innerHTML = '<i>' + username + '</i>';
}
});
if(results.length) {
group.setActive();
}
};
setResults(contacts.my_results, this.searchGroups.contacts, true);
setResults(contacts.results, this.searchGroups.globalContacts);
});
}
return this.searchPromise = appMessagesManager.getSearch(this.peerID, query, null, maxID, 20, this.offsetRate).then(res => {
this.searchPromise = null;
if(this.searchInput.value != query) {
return;
}
/////////this.log('input search result:', this.peerID, query, null, maxID, 20, res);
let {count, history, next_rate} = res;
if(history[0] == this.minMsgID) {
history.shift();
}
let searchGroup = this.searchGroups['messages'];
searchGroup.setActive();
history.forEach((msgID: number) => {
let message = appMessagesManager.getMessage(msgID);
let originalDialog = appMessagesManager.getDialogByPeerID(message.peerID)[0];
if(!originalDialog) {
////////this.log('no original dialog by message:', message);
originalDialog = {
peerID: message.peerID,
pFlags: {},
peer: message.to_id
};
}
let {dialog, dom} = appDialogsManager.addDialog(originalDialog, searchGroup.list, false);
appDialogsManager.setLastMessage(dialog, message, dom, query);
});
this.minMsgID = history[history.length - 1];
this.offsetRate = next_rate;
this.loadedCount += history.length;
if(!this.foundCount) {
this.foundCount = count;
}
}).catch(err => {
console.error('search error', err);
this.searchPromise = null;
});
}
}

8
src/components/emoticonsDropdown.ts

@ -34,7 +34,9 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -34,7 +34,9 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
lottieLoader.checkAnimations(false, EMOTICONSSTICKERGROUP);
lazyLoadQueue.check(); // for stickers
});
(tabs.children[0] as HTMLLIElement).click(); // set media
(tabs.firstElementChild.children[0] as HTMLLIElement).click(); // set emoji tab
(tabs.lastElementChild as HTMLSpanElement).style.cssText = 'width: 44.1719px; transform: translateX(88.5781px);'; // мы снова встретились))))))
let emoticonsMenuOnClick = (menu: HTMLUListElement, heights: number[], scroll: Scrollable, menuScroll?: Scrollable) => {
menu.addEventListener('click', function(e) {
@ -151,7 +153,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -151,7 +153,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
let heights: number[] = [0];
let prevCategoryIndex = 1;
let menu = contentEmojiDiv.nextElementSibling as HTMLUListElement;
let menu = contentEmojiDiv.nextElementSibling.firstElementChild as HTMLUListElement;
let emojiScroll = new Scrollable(contentEmojiDiv, 'y', 500, 'EMOJI', null);
emojiScroll.container.addEventListener('scroll', (e) => {
prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, emojiScroll.container);
@ -205,7 +207,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, @@ -205,7 +207,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
//let stickersDiv = contentStickersDiv.querySelector('.os-content') as HTMLDivElement;
let menuWrapper = contentStickersDiv.nextElementSibling as HTMLDivElement;
let menu = menuWrapper.firstElementChild as HTMLUListElement;
let menu = menuWrapper.firstElementChild.firstElementChild as HTMLUListElement;
let menuScroll = new Scrollable(menuWrapper, 'x');

20
src/components/misc.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { whichChild, findUpTag, cancelEvent } from "../lib/utils";
import { whichChild, findUpTag } from "../lib/utils";
let rippleClickID = 0;
export function ripple(elem: HTMLElement, callback: (id: number) => Promise<boolean | void> = () => Promise.resolve(), onEnd: (id: number) => void = null) {
@ -141,13 +141,13 @@ export function putPreloader(elem: Element, returnDiv = false) { @@ -141,13 +141,13 @@ export function putPreloader(elem: Element, returnDiv = false) {
elem.innerHTML += html;
}
export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement, onClick?: (id: number, tabContent: HTMLDivElement) => void, onTransitionEnd?: () => void, transitionTime = 300) {
export function horizontalMenu(tabs: HTMLElement, content: HTMLDivElement, onClick?: (id: number, tabContent: HTMLDivElement) => void, onTransitionEnd?: () => void, transitionTime = 300) {
let hideTimeout: number = 0;
let prevTabContent: HTMLDivElement = null;
let prevId = -1;
let children = Array.from(content.children);
let tabsChildren = tabs ? Array.from(tabs.children) : [];
let tabsChildren = tabs ? Array.from(tabs.firstElementChild.children) : [];
let activeInSlide: Set<Element> = new Set();
let selectTab = (id: number) => {
@ -227,6 +227,11 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement, @@ -227,6 +227,11 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement,
};
if(tabs) {
let activeStripe = document.createElement('span');
activeStripe.classList.add('menu-horizontal__stripe');
tabs.append(activeStripe);
tabs.addEventListener('click', function(e) {
let target = e.target as HTMLLIElement;
@ -242,7 +247,6 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement, @@ -242,7 +247,6 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement,
let tabContent = content.children[id] as HTMLDivElement;
if(activeInSlide.size >= 2 && !activeInSlide.has(tabContent)) {
cancelEvent(e);
return false;
}
@ -253,7 +257,13 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement, @@ -253,7 +257,13 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement,
let prev = tabs.querySelector('li.active') as HTMLLIElement;
prev && prev.classList.remove('active');
let tabsRect = tabs.getBoundingClientRect();
let textRect = target.firstElementChild.getBoundingClientRect();
activeStripe.style.cssText = `width: ${textRect.width + (2 * 2)}px; transform: translateX(${textRect.left - tabsRect.left}px);`;
//activeStripe.style.transform = `scaleX(${textRect.width}) translateX(${(textRect.left - tabsRect.left) / textRect.width + 0.5}px)`;
console.log('tabs click:', tabsRect, textRect);
target.classList.add('active');
selectTab(id);
});

58
src/lib/appManagers/appDialogsManager.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { langPack, findUpClassName, $rootScope } from "../utils";
import { langPack, findUpClassName, $rootScope, escapeRegExp } from "../utils";
import appImManager, { AppImManager } from "./appImManager";
import appPeersManager from './appPeersManager';
import appMessagesManager, { AppMessagesManager } from "./appMessagesManager";
@ -420,7 +420,7 @@ export class AppDialogsManager { @@ -420,7 +420,7 @@ export class AppDialogsManager {
chatsHidden.down = inBottom;
}
public setLastMessage(dialog: any, lastMessage?: any, dom?: DialogDom) {
public setLastMessage(dialog: any, lastMessage?: any, dom?: DialogDom, highlightWord?: string) {
if(!lastMessage) {
lastMessage = appMessagesManager.getMessage(dialog.top_message);
}
@ -497,12 +497,58 @@ export class AppDialogsManager { @@ -497,12 +497,58 @@ export class AppDialogsManager {
}
if(lastMessage.action) {
let action = lastMessage.action;
console.log('lastMessage action:', action);
let suffix = '';
let _ = action._;
if(_ == "messageActionPhoneCall") {
_ += '.' + action.type;
let duration = action.duration;
if(duration) {
let d = [];
d.push(duration % 60 + ' s');
if(duration > 60) d.push((duration / 60 | 0) + ' min');
if(duration > 3600) d.push((duration / 3600 | 0) + ' h');
suffix = ' (' + d.reverse().join(' ') + ')';
}
}
// @ts-ignore
lastMessageText = langPack[lastMessage.action._];
lastMessageText = '<i>' + langPack[_] + suffix + '</i>';
}
dom.lastMessageSpan.innerHTML = lastMessageText +
(lastMessage.message ? RichTextProcessor.wrapRichText(lastMessage.message.replace(/\n/g, ' '), {noLinebreakers: true}) : '');
let messageText = lastMessage.message;
let messageWrapped = '';
if(messageText) {
let entities = RichTextProcessor.parseEntities(messageText, {noLinebreakers: true});
if(highlightWord) {
let regExp = new RegExp(escapeRegExp(highlightWord), 'gi');
let match: any;
if(!entities) entities = [];
let found = false;
while((match = regExp.exec(messageText)) !== null) {
entities.push({_: 'messageEntityHighlight', length: highlightWord.length, offset: match.index});
found = true;
}
if(found) {
entities.sort((a: any, b: any) => a.offset - b.offset);
}
}
messageWrapped = RichTextProcessor.wrapRichText(messageText.replace(/\n/g, ' '), {
noLinebreakers: true,
entities: entities,
noTextFormat: true
});
}
dom.lastMessageSpan.innerHTML = lastMessageText + messageWrapped;
/* if(lastMessage.from_id == auth.id) { // You: */
if(peer._ != 'peerUser' && peerID != -lastMessage.from_id) {

10
src/lib/appManagers/appImManager.ts

@ -26,6 +26,8 @@ import LazyLoadQueue from '../../components/lazyLoadQueue'; @@ -26,6 +26,8 @@ import LazyLoadQueue from '../../components/lazyLoadQueue';
console.log('appImManager included!');
appSidebarLeft; // just to include
let testScroll = false;
export class AppImManager {
@ -343,7 +345,7 @@ export class AppImManager { @@ -343,7 +345,7 @@ export class AppImManager {
}
let ids = Object.keys(this.bubbles).map(k => +k).filter(id => {
if(!this.scrollable.visibleElements.find(e => e.element == this.bubbles[id])) return false;
//if(!this.scrollable.visibleElements.find(e => e.element == this.bubbles[id])) return false;
let message = appMessagesManager.getMessage(id);
@ -370,7 +372,9 @@ export class AppImManager { @@ -370,7 +372,9 @@ export class AppImManager {
this.searchBtn.addEventListener('click', (e) => {
if(this.peerID) {
appSidebarLeft.beginSearch(this.peerID);
appSidebarRight.beginSearch();
//appSidebarLeft.archivedCount;
//appSidebarLeft.beginSearch(this.peerID);
}
});
@ -912,6 +916,8 @@ export class AppImManager { @@ -912,6 +916,8 @@ export class AppImManager {
return true;
}
} else {
appSidebarRight.searchCloseBtn.click();
}
// clear

272
src/lib/appManagers/appSidebarLeft.ts

@ -1,87 +1,32 @@ @@ -1,87 +1,32 @@
import { logger } from "../polyfill";
import { formatPhoneNumber } from "../../components/misc";
import Scrollable from '../../components/scrollable';
import appMessagesManager from "./appMessagesManager";
//import { logger } from "../polyfill";
import appDialogsManager from "./appDialogsManager";
import { isElementInViewport, numberWithCommas, $rootScope } from "../utils";
import appMessagesIDsManager from "./appMessagesIDsManager";
import { $rootScope } from "../utils";
import appImManager from "./appImManager";
import appUsersManager from "./appUsersManager";
import apiManager from "../mtproto/apiManager";
import appPeersManager from './appPeersManager';
class SearchGroup {
container: HTMLDivElement;
nameEl: HTMLDivElement;
list: HTMLUListElement;
constructor(public name: string, public type: string) {
this.list = document.createElement('ul');
this.container = document.createElement('div');
this.nameEl = document.createElement('div');
this.nameEl.classList.add('search-group__name');
this.nameEl.innerText = name;
this.container.classList.add('search-group');
this.container.append(this.nameEl, this.list);
this.container.style.display = 'none';
appDialogsManager.setListClickListener(this.list);
}
clear() {
this.container.style.display = 'none';
this.list.innerHTML = '';
}
setActive() {
this.container.style.display = '';
}
}
import AppSearch, { SearchGroup } from "../../components/appSearch";
class AppSidebarLeft {
private sidebarEl = document.querySelector('.page-chats .chats-container') as HTMLDivElement;
private searchInput = document.getElementById('global-search') as HTMLInputElement;
private sidebarEl = document.getElementById('column-left') as HTMLDivElement;
private toolsBtn = this.sidebarEl.querySelector('.sidebar-tools-button') as HTMLButtonElement;
private backBtn = this.sidebarEl.querySelector('.sidebar-back-button') as HTMLButtonElement;
private searchContainer = this.sidebarEl.querySelector('#search-container') as HTMLDivElement;
private searchInput = document.getElementById('global-search') as HTMLInputElement;
private menuEl = this.toolsBtn.querySelector('.btn-menu');
private savedBtn = this.menuEl.querySelector('.menu-saved');
private archivedBtn = this.menuEl.querySelector('.menu-archive');
private logOutBtn = this.menuEl.querySelector('.menu-logout');
public archivedCount = this.archivedBtn.querySelector('.archived-count') as HTMLSpanElement;
private listsContainer: HTMLDivElement = null;
private log = logger('SL');
private peerID = 0;
private minMsgID = 0;
private loadedCount = 0;
private foundCount = 0;
private offsetRate = 0;
private searchPromise: Promise<void> = null;
private searchTimeout: number = 0;
private query = '';
public searchGroups: {[group: string]: SearchGroup} = {};
//private log = logger('SL');
private globalSearch = new AppSearch(this.searchContainer, this.searchInput, {
contacts: new SearchGroup('Contacts and Chats', 'contacts'),
globalContacts: new SearchGroup('Global Search', 'contacts'),
messages: new SearchGroup('Global Search', 'messages')
});
constructor() {
this.searchGroups = {
contacts: new SearchGroup('Contacts and Chats', 'contacts'),
globalContacts: new SearchGroup('Global Search', 'contacts'),
globalMessages: new SearchGroup('Global Search', 'messages'),
privateMessages: new SearchGroup('Private Search', 'messages')
};
this.listsContainer = new Scrollable(this.searchContainer).container;
for(let i in this.searchGroups) {
this.listsContainer.append(this.searchGroups[i].container);
}
this.savedBtn.addEventListener('click', (e) => {
///////this.log('savedbtn click');
setTimeout(() => { // menu doesn't close if no timeout (lol)
@ -101,19 +46,17 @@ class AppSidebarLeft { @@ -101,19 +46,17 @@ class AppSidebarLeft {
this.logOutBtn.addEventListener('click', (e) => {
apiManager.logOut();
});
this.listsContainer.addEventListener('scroll', this.onSidebarScroll.bind(this));
this.searchInput.addEventListener('focus', (e) => {
this.toolsBtn.classList.remove('active');
this.backBtn.classList.add('active');
this.searchContainer.classList.add('active');
if(!this.searchInput.value) {
for(let i in this.searchGroups) {
this.searchGroups[i].clear();
/* if(!this.globalSearch.searchInput.value) {
for(let i in this.globalSearch.searchGroups) {
this.globalSearch.searchGroups[i].clear();
}
}
} */
this.searchInput.addEventListener('blur', (e) => {
if(!this.searchInput.value) {
@ -128,46 +71,13 @@ class AppSidebarLeft { @@ -128,46 +71,13 @@ class AppSidebarLeft {
this.minMsgID = 0; */
}, {once: true});
});
this.searchInput.addEventListener('input', (e) => {
//console.log('messageInput input', this.innerText, serializeNodes(Array.from(messageInput.childNodes)));
let value = this.searchInput.value;
////////this.log('input', value);
if(!value.trim()) {
//this.peerID = 0;
return;
}
this.query = value;
this.minMsgID = 0;
this.loadedCount = 0;
this.foundCount = 0;
this.offsetRate = 0;
for(let i in this.searchGroups) {
this.searchGroups[i].clear();
}
this.searchPromise = null;
this.searchMore();
});
this.backBtn.addEventListener('click', (e) => {
appDialogsManager.chatsArchivedContainer.classList.remove('active');
this.toolsBtn.classList.add('active');
this.backBtn.classList.remove('active');
this.searchInput.value = '';
this.searchContainer.classList.remove('active');
this.peerID = 0;
});
window.addEventListener('resize', () => {
//this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
setTimeout(() => {
this.onSidebarScroll();
}, 0);
this.globalSearch.reset();
});
$rootScope.$on('dialogs_archived_unread', (e: CustomEvent) => {
@ -178,154 +88,6 @@ class AppSidebarLeft { @@ -178,154 +88,6 @@ class AppSidebarLeft {
this.log('got top categories:', categories);
}); */
}
public onSidebarScroll() {
if(!this.query.trim()) return;
let elements = Array.from(this.searchGroups[this.peerID ? 'privateMessages' : 'globalMessages'].list.childNodes).slice(-5);
for(let li of elements) {
if(isElementInViewport(li)) {
this.log('Will load more search');
if(!this.searchTimeout) {
this.searchTimeout = setTimeout(() => {
this.searchMore();
this.searchTimeout = 0;
}, 0);
}
break;
}
}
}
public beginSearch(peerID?: number) {
if(peerID) {
this.peerID = peerID;
}
this.searchInput.focus();
}
private searchMore() {
if(this.searchPromise) return this.searchPromise;
let query = this.query;
if(!query.trim()) return;
if(this.loadedCount != 0 && this.loadedCount >= this.foundCount) {
return Promise.resolve();
}
let maxID = appMessagesIDsManager.getMessageIDInfo(this.minMsgID)[0];
if(!this.peerID && !maxID) {
appUsersManager.searchContacts(query, 20).then((contacts: any) => {
if(this.searchInput.value != query) {
return;
}
///////this.log('input search contacts result:', contacts);
let setResults = (results: any, group: SearchGroup, showMembersCount = false) => {
results.forEach((inputPeer: any) => {
let peerID = appPeersManager.getPeerID(inputPeer);
let peer = appPeersManager.getPeer(peerID);
let originalDialog = appMessagesManager.getDialogByPeerID(peerID)[0];
//////////this.log('contacts peer', peer);
if(!originalDialog) {
/////////this.log('no original dialog by peerID:', peerID);
originalDialog = {
peerID: peerID,
pFlags: {},
peer: peer
};
}
let {dialog, dom} = appDialogsManager.addDialog(originalDialog, group.list, false);
if(showMembersCount && (peer.participants_count || peer.participants)) {
let isChannel = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID);
let participants_count = peer.participants_count || peer.participants.participants.length;
let subtitle = numberWithCommas(participants_count) + ' ' + (isChannel ? 'subscribers' : 'members');
dom.lastMessageSpan.innerText = subtitle;
} else {
let username = appPeersManager.getPeerUsername(peerID);
if(!username) {
let user = appUsersManager.getUser(peerID);
if(user && user.phone) {
username = '+' + formatPhoneNumber(user.phone).formatted;
}
} else {
username = '@' + username;
}
dom.lastMessageSpan.innerText = username;
}
});
if(results.length) {
group.setActive();
}
};
setResults(contacts.my_results, this.searchGroups.contacts, true);
setResults(contacts.results, this.searchGroups.globalContacts);
});
}
return this.searchPromise = appMessagesManager.getSearch(this.peerID, query, null, maxID, 20, this.offsetRate).then(res => {
this.searchPromise = null;
if(this.searchInput.value != query) {
return;
}
/////////this.log('input search result:', this.peerID, query, null, maxID, 20, res);
let {count, history, next_rate} = res;
if(history[0] == this.minMsgID) {
history.shift();
}
let searchGroup = this.searchGroups[this.peerID ? 'privateMessages' : 'globalMessages'];
searchGroup.setActive();
history.forEach((msgID: number) => {
let message = appMessagesManager.getMessage(msgID);
let originalDialog = appMessagesManager.getDialogByPeerID(message.peerID)[0];
if(!originalDialog) {
////////this.log('no original dialog by message:', message);
originalDialog = {
peerID: message.peerID,
pFlags: {},
peer: message.to_id
};
}
let {dialog, dom} = appDialogsManager.addDialog(originalDialog, searchGroup.list, false);
appDialogsManager.setLastMessage(dialog, message, dom);
});
this.minMsgID = history[history.length - 1];
this.offsetRate = next_rate;
this.loadedCount += history.length;
if(!this.foundCount) {
this.foundCount = count;
}
}).catch(err => {
this.log.error('search error', err);
this.searchPromise = null;
});
}
}
const appSidebarLeft = new AppSidebarLeft();

36
src/lib/appManagers/appSidebarRight.ts

@ -12,12 +12,14 @@ import appImManager from "./appImManager"; @@ -12,12 +12,14 @@ import appImManager from "./appImManager";
import appMediaViewer from "./appMediaViewer";
import LazyLoadQueue from "../../components/lazyLoadQueue";
import { wrapDocument, wrapAudio } from "../../components/wrappers";
import AppSearch, { SearchGroup } from "../../components/appSearch";
const testScroll = false;
class AppSidebarRight {
public sidebarEl = document.querySelector('.profile-container') as HTMLDivElement;
public profileContentEl = document.querySelector('.profile-content') as HTMLDivElement;
public sidebarEl = document.getElementById('column-right') as HTMLDivElement;
public profileContainer = this.sidebarEl.querySelector('.profile-container') as HTMLDivElement;
public profileContentEl = this.sidebarEl.querySelector('.profile-content') as HTMLDivElement;
public profileElements = {
avatar: this.profileContentEl.querySelector('.profile-avatar') as HTMLDivElement,
name: this.profileContentEl.querySelector('.profile-name') as HTMLDivElement,
@ -88,12 +90,19 @@ class AppSidebarRight { @@ -88,12 +90,19 @@ class AppSidebarRight {
} = {};
public urlsToRevoke: string[] = [];
private searchContainer = this.sidebarEl.querySelector('#search-private-container') as HTMLDivElement;
public searchCloseBtn = this.searchContainer.querySelector('.sidebar-close-button') as HTMLButtonElement;
private searchInput = document.getElementById('private-search') as HTMLInputElement;
public privateSearch = new AppSearch(this.searchContainer.querySelector('.chats-container') as HTMLDivElement, this.searchInput, {
messages: new SearchGroup('Private Search', 'messages')
});
constructor() {
let container = this.profileContentEl.querySelector('.profile-tabs-content') as HTMLDivElement;
this.profileTabs = this.profileContentEl.querySelector('.profile-tabs') as HTMLUListElement;
this.scroll = new Scrollable(this.sidebarEl, 'y', 1200, 'SR');
this.scroll = new Scrollable(this.profileContainer, 'y', 1200, 'SR');
this.scroll.container.addEventListener('scroll', this.onSidebarScroll.bind(this));
this.scroll.onScrolledBottom = () => {
if(this.sharedMediaSelected && !this.scroll.hiddenElements.down.length && this.sharedMediaSelected.childElementCount/* && false */) {
@ -131,6 +140,11 @@ class AppSidebarRight { @@ -131,6 +140,11 @@ class AppSidebarRight {
sidebarCloseBtn.addEventListener('click', () => {
this.toggleSidebar(false);
});
this.searchCloseBtn.addEventListener('click', () => {
this.searchContainer.classList.remove('active');
this.privateSearch.reset();
});
this.sharedMedia.contentMedia.addEventListener('click', (e) => {
let target = e.target as HTMLDivElement;
@ -175,6 +189,12 @@ class AppSidebarRight { @@ -175,6 +189,12 @@ class AppSidebarRight {
(this.profileTabs.children[1] as HTMLLIElement).click(); // set media
}
}
public beginSearch() {
this.toggleSidebar(true);
this.searchContainer.classList.add('active');
this.privateSearch.beginSearch(this.peerID);
}
public onSidebarScroll() {
this.lazyLoadQueueSidebar.check();
@ -191,12 +211,8 @@ class AppSidebarRight { @@ -191,12 +211,8 @@ class AppSidebarRight {
return;
}
if(this.sidebarEl.classList.contains('active')) {
this.sidebarEl.classList.remove('active');
} else {
this.sidebarEl.classList.add('active');
}
this.sidebarEl.classList.toggle('active');
}
public performSearchResult(ids: number[], type: string) {
@ -533,7 +549,7 @@ class AppSidebarRight { @@ -533,7 +549,7 @@ class AppSidebarRight {
this.savedVirtualStates = {};
this.prevTabID = -1;
this.scroll.setVirtualContainer(null);
(this.profileTabs.children[1] as HTMLLIElement).click(); // set media
(this.profileTabs.firstElementChild.children[1] as HTMLLIElement).click(); // set media
//this.scroll.getScrollTopOffset();

27
src/lib/richtextprocessor.js

@ -547,6 +547,11 @@ function wrapRichText (text, options = {}) { @@ -547,6 +547,11 @@ function wrapRichText (text, options = {}) {
)
break
case 'messageEntityBold':
if(options.noTextFormat) {
html.push(wrapRichNestedText(entityText, entity.nested, options));
break;
}
html.push(
'<strong>',
wrapRichNestedText(entityText, entity.nested, options),
@ -554,13 +559,30 @@ function wrapRichText (text, options = {}) { @@ -554,13 +559,30 @@ function wrapRichText (text, options = {}) {
)
break
case 'messageEntityItalic':
if(options.noTextFormat) {
html.push(wrapRichNestedText(entityText, entity.nested, options));
break;
}
html.push(
'<em>',
wrapRichNestedText(entityText, entity.nested, options),
'</em>'
)
break
case 'messageEntityHighlight':
html.push(
'<i>',
wrapRichNestedText(entityText, entity.nested, options),
'</i>'
)
break;
case 'messageEntityCode':
if(options.noTextFormat) {
html.push(encodeEntities(entityText));
break;
}
html.push(
'<code>',
encodeEntities(entityText),
@ -568,6 +590,11 @@ function wrapRichText (text, options = {}) { @@ -568,6 +590,11 @@ function wrapRichText (text, options = {}) {
)
break
case 'messageEntityPre':
if(options.noTextFormat) {
html.push(encodeEntities(entityText));
break;
}
html.push(
'<pre><code', (entity.language ? ' class="language-' + encodeEntities(entity.language) + '"' : ''), '>',
encodeEntities(entityText),

7
src/lib/utils.js

@ -615,6 +615,13 @@ export function listUniqSorted (list) { @@ -615,6 +615,13 @@ export function listUniqSorted (list) {
return resultList
}
// credits to https://github.com/sindresorhus/escape-string-regexp/blob/master/index.js
export function escapeRegExp(str) {
return str
.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
.replace(/-/g, '\\x2d');
}
export function encodeEntities (value) {
return value.replace(/&/g, '&amp;').replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, function (value) {
var hi = value.charCodeAt(0)

33
src/scss/partials/_chatlist.scss

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
.chats-container {
position: relative;
.input-search {
position: relative;
width: 100%;
@ -155,8 +157,6 @@ @@ -155,8 +157,6 @@
}
.user-title {
max-width: 80%;
img.emoji {
vertical-align: top;
width: 18px;
@ -174,13 +174,6 @@ @@ -174,13 +174,6 @@
}
.user-last-message {
max-width: 80%;
i {
font-style: normal;
color: $darkblue;
}
img.emoji {
width: 20px;
height: 20px;
@ -194,6 +187,15 @@ @@ -194,6 +187,15 @@
}
}
.user-title, .user-last-message {
max-width: 80%;
i {
font-style: normal;
color: $darkblue;
}
}
.message-status {
margin-right: .1rem;
//margin-top: .3rem;
@ -241,4 +243,17 @@ @@ -241,4 +243,17 @@
.unread-muted, .tgico-pinnedchat {
background: #c5c9cc;
}
.search-group {
width: 100%;
border-bottom: 1px solid #DADCE0;
padding: 1rem 0 .5rem;
margin-bottom: .5rem;
&__name {
color: $color-gray;
padding: 0 1.85rem;
padding-bottom: 1rem;
}
}
}

2
src/scss/partials/_emojiDropdown.scss

@ -195,7 +195,7 @@ @@ -195,7 +195,7 @@
}
.emoji-padding, .stickers-padding {
.menu-horizontal > li {
.menu-horizontal li {
font-size: 1.65rem;
}
}

45
src/scss/partials/_leftSidebar.scss

@ -1,43 +1,13 @@ @@ -1,43 +1,13 @@
.chats-container {
#column-left {
display: flex;
flex-direction: column;
.sidebar-content {
width: 100%;
max-height: 100%;
height: 100%;
overflow: hidden;
display: flex; /* idk why but need */
position: relative;
> div {
width: 100%;
}
}
#chats-container {
max-height: 100%;
overflow: hidden;
position: relative;
}
#search-container, #chats-archived-container {
display: none;
width: 100%;
max-height: 100%;
height: 100%;
overflow: hidden;
position: absolute;
left: 0;
top: 0;
z-index: 3;
background: #fff;
&.active {
display: flex;
}
}
.sidebar-header__btn-container {
position: relative;
width: 39.75px;
@ -97,16 +67,5 @@ @@ -97,16 +67,5 @@
}
}
.search-group {
width: 100%;
border-bottom: 1px solid #DADCE0;
padding: 1rem 0 .5rem;
margin-bottom: .5rem;
&__name {
color: $color-gray;
padding: 0 1.85rem;
padding-bottom: 1rem;
}
}
}

27
src/scss/partials/_rightSIdebar.scss

@ -1,18 +1,20 @@ @@ -1,18 +1,20 @@
.profile-container {
#column-right {
width: 0%;
/* grid-column: 3; */
position: relative;
transition: .2s ease-in-out;
> .scrollable {
min-width: 25vw;
display: flex;
flex-direction: column;
}
@media (min-width: $large-screen) {
.profile-container {
> .scrollable {
min-width: calc(#{$large-screen} / 4 - 1px);
min-width: 25vw;
display: flex;
flex-direction: column;
}
@media (min-width: $large-screen) {
> .scrollable {
min-width: calc(#{$large-screen} / 4 - 1px);
}
}
}
@ -27,6 +29,13 @@ @@ -27,6 +29,13 @@
.sidebar-header {
flex: 0 0 auto;
}
#search-private-container {
.chats-container {
position: relative;
flex: 1 1 auto;
}
}
}
.profile-content {

53
src/scss/partials/_sidebar.scss

@ -1,31 +1,44 @@ @@ -1,31 +1,44 @@
.sidebar {
background-color: #fff;
overflow: hidden;
}
.sidebar-left {
border-right: 1px solid #DADCE0;
}
&-left {
border-right: 1px solid #DADCE0;
}
.sidebar-right {
border-left: 1px solid #DADCE0;
}
&-right {
border-left: 1px solid #DADCE0;
}
&-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 7.5px 20px 7.5px 15px;
min-height: 60px;
.sidebar-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 7.5px 20px 7.5px 15px;
min-height: 60px;
&__title {
flex: 1;
padding-left: 2rem;
font-weight: 500;
font-size: 1.4rem;
}
.sidebar-title {
flex: 1;
padding-left: 2rem;
font-weight: 500;
font-size: 1.4rem;
.btn-icon + .btn-icon {
margin-left: .5rem;
}
}
.btn-icon + .btn-icon {
margin-left: .5rem;
&-content {
width: 100%;
max-height: 100%;
height: 100%;
overflow: hidden;
display: flex; /* idk why but need */
position: relative;
> div {
width: 100%;
}
}
}

73
src/scss/style.scss

@ -1422,16 +1422,22 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -1422,16 +1422,22 @@ div.scrollable::-webkit-scrollbar-thumb {
}
.menu-horizontal {
width: 100%;
color: $darkgrey;
margin-top: 0px;
margin-bottom: 0;
border-bottom: 1px solid $lightgrey;
display: flex;
justify-content: space-around;
align-items: center;
position: relative;
ul {
width: 100%;
height: 100%;
margin: 0;
display: flex;
justify-content: space-around;
align-items: center;
position: relative;
z-index: 2;
}
> li {
li {
display: inline-block;
padding: .75rem 1rem;
cursor: pointer;
@ -1439,24 +1445,27 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -1439,24 +1445,27 @@ div.scrollable::-webkit-scrollbar-thumb {
flex: 1;
user-select: none;
font-size: 1rem;
&.active {
position: relative;
color: $blue;
&:after {
content: '';
position: absolute;
background: $blue;
left: 24px;
right: 24px;
bottom: -1px;
height: 4px;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
}
}
&__stripe {
position: absolute;
background: $blue;
//left: 0;
left: -2px;
transition: .3s transform, .3s width;
//transition: .3s transform;
bottom: -1px;
height: 4px;
width: 1px; // need if using transform
transform: scaleX(1) translateX(0px);
border-top-left-radius: 2px;
border-top-right-radius: 2px;
z-index: 1;
}
}
.tabs-container {
@ -1493,7 +1502,7 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -1493,7 +1502,7 @@ div.scrollable::-webkit-scrollbar-thumb {
}
.justify-start {
justify-content: flex-start;
justify-content: flex-start!important;
}
.popup-send-photo {
@ -1609,7 +1618,7 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -1609,7 +1618,7 @@ div.scrollable::-webkit-scrollbar-thumb {
max-height: 100%;
}
.chats-container {
#column-left {
width: 25%;
/* grid-column: 1; */
}
@ -1635,6 +1644,24 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -1635,6 +1644,24 @@ div.scrollable::-webkit-scrollbar-thumb {
}
}
#search-container, #chats-archived-container, .sidebar-search {
display: none;
flex-direction: column;
width: 100%;
max-height: 100%;
height: 100%;
overflow: hidden;
position: absolute;
left: 0;
top: 0;
z-index: 3;
background: #fff;
&.active {
display: flex;
}
}
@media (min-width: $large-screen) {
border-top-width: 0;
border-bottom-width: 0;

Loading…
Cancel
Save