diff --git a/src/components/appMediaViewer.ts b/src/components/appMediaViewer.ts index d0069548..0f444134 100644 --- a/src/components/appMediaViewer.ts +++ b/src/components/appMediaViewer.ts @@ -205,7 +205,12 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet } } - appImManager.setInnerPeer(message.peerId, mid, threadId ? 'discussion' : undefined, threadId); + appImManager.setInnerPeer({ + peerId: message.peerId, + lastMsgId: mid, + type: threadId ? 'discussion' : undefined, + threadId + }); }); } }; diff --git a/src/components/appSearch.ts b/src/components/appSearch.ts index e4f19510..f5a72f39 100644 --- a/src/components/appSearch.ts +++ b/src/components/appSearch.ts @@ -184,20 +184,20 @@ export default class AppSearch { const searchGroup = this.searchGroups.messages; history.forEach((message) => { - const peerId = this.peerId ? message.fromId : message.peerId; - const {dialog, dom} = appDialogsManager.addDialogNew({ - dialog: peerId, - container: this.scrollable/* searchGroup.list */, - drawStatus: false, - avatarSize: 54, - meAsSaved: false - }); - - if(message.peerId !== peerId) { - dom.listEl.dataset.peerId = '' + message.peerId; + try { + const peerId = this.peerId ? message.fromId : message.peerId; + appDialogsManager.addDialogAndSetLastMessage({ + peerId, + container: this.scrollable/* searchGroup.list */, + drawStatus: false, + avatarSize: 54, + meAsSaved: false, + message, + query + }); + } catch(err) { + console.error('[appSearch] render search result', err); } - - appDialogsManager.setLastMessage(dialog, message, dom, query); }); searchGroup.toggle(); diff --git a/src/components/appSearchSuper..ts b/src/components/appSearchSuper..ts index 8407dd49..83ae60ac 100644 --- a/src/components/appSearchSuper..ts +++ b/src/components/appSearchSuper..ts @@ -28,7 +28,7 @@ import I18n, { LangPackKey, i18n } from "../lib/langPack"; import findUpClassName from "../helpers/dom/findUpClassName"; import { getMiddleware } from "../helpers/middleware"; import appProfileManager from "../lib/appManagers/appProfileManager"; -import { ChannelParticipant, ChatFull, ChatParticipant, ChatParticipants } from "../layer"; +import { ChannelParticipant, ChatFull, ChatParticipant, ChatParticipants, Message, MessageMedia, Photo, WebPage } from "../layer"; import SortedUserList from "./sortedUserList"; import findUpTag from "../helpers/dom/findUpTag"; import appSidebarRight from "./sidebarRight"; @@ -222,6 +222,15 @@ class SearchContextMenu { }; } +export type ProcessSearchSuperResult = { + message: Message.message, + middleware: () => boolean, + promises: Promise[], + elemsToAppend: {element: HTMLElement, message: any}[], + inputFilter: MyInputMessagesFilter, + searchGroup?: SearchGroup +}; + export default class AppSearchSuper { public tabs: {[t in SearchSuperType]: HTMLDivElement} = {} as any; @@ -655,6 +664,217 @@ export default class AppSearchSuper { return filtered; } + + private processEmptyFilter({message, searchGroup}: ProcessSearchSuperResult) { + const {dialog, dom} = appDialogsManager.addDialogNew({ + dialog: message.peerId, + container: searchGroup.list, + drawStatus: false, + avatarSize: 54 + }); + + appDialogsManager.setLastMessage(dialog, message, dom, this.searchContext.query); + } + + private processPhotoVideoFilter({message, promises, middleware, elemsToAppend}: ProcessSearchSuperResult) { + const media = appMessagesManager.getMediaFromMessage(message); + + const div = document.createElement('div'); + div.classList.add('grid-item'); + //this.log(message, photo); + + let wrapped: ReturnType; + const size = appPhotosManager.choosePhotoSize(media, 200, 200); + if(media._ !== 'photo') { + wrapped = wrapVideo({ + doc: media, + message, + container: div, + boxWidth: 0, + boxHeight: 0, + lazyLoadQueue: this.lazyLoadQueue, + middleware, + onlyPreview: true, + withoutPreloader: true, + noPlayButton: true, + size + }).thumb; + } else { + wrapped = wrapPhoto({ + photo: media, + message, + container: div, + boxWidth: 0, + boxHeight: 0, + lazyLoadQueue: this.lazyLoadQueue, + middleware, + withoutPreloader: true, + noBlur: true, + size + }); + } + + [wrapped.images.thumb, wrapped.images.full].filter(Boolean).forEach(image => { + image.classList.add('grid-item-media'); + }); + + promises.push(wrapped.loadPromises.thumb); + + elemsToAppend.push({element: div, message}); + } + + private processDocumentFilter({message, elemsToAppend, inputFilter}: ProcessSearchSuperResult) { + const document = appMessagesManager.getMediaFromMessage(message); + const showSender = this.showSender || (['voice', 'round'] as MyDocument['type'][]).includes(document.type); + const div = wrapDocument({ + message, + withTime: !showSender, + fontWeight: 400, + voiceAsMusic: true, + showSender, + searchContext: this.copySearchContext(inputFilter), + lazyLoadQueue: this.lazyLoadQueue, + noAutoDownload: true + }); + + if((['audio', 'voice', 'round'] as MyDocument['type'][]).includes(document.type)) { + div.classList.add('audio-48'); + } + + elemsToAppend.push({element: div, message}); + } + + private processUrlFilter({message, promises, middleware, elemsToAppend}: ProcessSearchSuperResult) { + let webpage = (message.media as MessageMedia.messageMediaWebPage)?.webpage as WebPage.webPage; + + if(!webpage) { + const entity = message.totalEntities ? message.totalEntities.find((e: any) => e._ === 'messageEntityUrl' || e._ === 'messageEntityTextUrl') : null; + let url: string, display_url: string, sliced: string; + + if(!entity) { + //this.log.error('NO ENTITY:', message); + const match = RichTextProcessor.matchUrl(message.message); + if(!match) { + //this.log.error('NO ENTITY AND NO MATCH:', message); + return; + } + + url = match[0]; + } else { + sliced = message.message.slice(entity.offset, entity.offset + entity.length); + } + + if(entity?._ === 'messageEntityTextUrl') { + url = entity.url; + //display_url = sliced; + } else { + url = url || sliced; + } + + display_url = url; + + const same = message.message === url; + if(!url.match(/^(ftp|http|https):\/\//)) { + display_url = 'https://' + url; + url = url.includes('@') ? url : 'https://' + url; + } + + display_url = new URL(display_url).hostname; + + webpage = { + _: 'webPage', + url, + display_url, + id: '', + hash: 0 + }; + + if(!same) { + webpage.description = message.message; + webpage.rDescription = RichTextProcessor.wrapRichText(limitSymbols(message.message, 150, 180)); + } + } + + let previewDiv = document.createElement('div'); + previewDiv.classList.add('preview', 'row-media'); + + //this.log('wrapping webpage', webpage); + + if(webpage.photo) { + const res = wrapPhoto({ + container: previewDiv, + message: null, + photo: webpage.photo as Photo.photo, + boxWidth: 0, + boxHeight: 0, + withoutPreloader: true, + lazyLoadQueue: this.lazyLoadQueue, + middleware, + size: appPhotosManager.choosePhotoSize(webpage.photo as Photo.photo, 60, 60, false), + loadPromises: promises, + noBlur: true + }); + } else { + previewDiv.classList.add('empty'); + previewDiv.innerHTML = RichTextProcessor.getAbbreviation(webpage.title || webpage.display_url || webpage.description || webpage.url, true); + } + + let title = webpage.rTitle || ''; + let subtitle = webpage.rDescription || ''; + + const subtitleFragment = htmlToDocumentFragment(subtitle); + const aFragment = htmlToDocumentFragment(RichTextProcessor.wrapRichText(webpage.url || '')); + const a = aFragment.firstElementChild; + if(a instanceof HTMLAnchorElement) { + try { // can have 'URIError: URI malformed' + a.innerText = decodeURIComponent(a.href); + } catch(err) { + + } + } + + if(subtitleFragment.firstChild) { + subtitleFragment.append('\n'); + } + + subtitleFragment.append(a); + + if(this.showSender) { + subtitleFragment.append('\n', appMessagesManager.wrapSenderToPeer(message)); + } + + if(!title) { + //title = new URL(webpage.url).hostname; + title = RichTextProcessor.wrapPlainText(webpage.display_url.split('/', 1)[0]); + } + + const row = new Row({ + title, + titleRight: appMessagesManager.wrapSentTime(message), + subtitle: subtitleFragment, + havePadding: true, + clickable: true, + noRipple: true + }); + + /* const mediaDiv = document.createElement('div'); + mediaDiv.classList.add('row-media'); */ + + row.container.append(previewDiv); + + /* ripple(div); + div.append(previewDiv); + div.insertAdjacentHTML('beforeend', ` +
${title}${titleAdditionHTML}
+
${subtitle}
+
${url}
+ ${sender} + `); */ + + if(row.container.innerText.trim().length) { + elemsToAppend.push({element: row.container, message}); + } + } public async performSearchResult(messages: any[], mediaTab: SearchSuperMediaTab, append = true) { const elemsToAppend: {element: HTMLElement, message: any}[] = []; @@ -674,73 +894,26 @@ export default class AppSearchSuper { searchGroup = this.searchGroups.messages; } + const options: ProcessSearchSuperResult = { + elemsToAppend, + inputFilter, + message: undefined, + middleware, + promises, + searchGroup + }; + + let processCallback: (options: ProcessSearchSuperResult) => any; + // https://core.telegram.org/type/MessagesFilter switch(inputFilter) { case 'inputMessagesFilterEmpty': { - for(const message of messages) { - const {dialog, dom} = appDialogsManager.addDialogNew({ - dialog: message.peerId, - container: searchGroup.list, - drawStatus: false, - avatarSize: 54 - }); - appDialogsManager.setLastMessage(dialog, message, dom, this.searchContext.query); - } - - if(searchGroup.list.childElementCount) { - searchGroup.setActive(); - } + processCallback = this.processEmptyFilter; break; } case 'inputMessagesFilterPhotoVideo': { - for(const message of messages) { - const media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document); - - const div = document.createElement('div'); - div.classList.add('grid-item'); - //this.log(message, photo); - - let wrapped: ReturnType; - const size = appPhotosManager.choosePhotoSize(media, 200, 200); - if(media._ !== 'photo') { - wrapped = wrapVideo({ - doc: media, - message, - container: div, - boxWidth: 0, - boxHeight: 0, - lazyLoadQueue: this.lazyLoadQueue, - middleware, - onlyPreview: true, - withoutPreloader: true, - noPlayButton: true, - size - }).thumb; - } else { - wrapped = wrapPhoto({ - photo: media, - message, - container: div, - boxWidth: 0, - boxHeight: 0, - lazyLoadQueue: this.lazyLoadQueue, - middleware, - withoutPreloader: true, - noBlur: true, - size - }); - } - - [wrapped.images.thumb, wrapped.images.full].filter(Boolean).forEach(image => { - image.classList.add('grid-item-media'); - }); - - promises.push(wrapped.loadPromises.thumb); - - elemsToAppend.push({element: div, message}); - } - + processCallback = this.processPhotoVideoFilter; break; } @@ -748,158 +921,12 @@ export default class AppSearchSuper { case 'inputMessagesFilterRoundVoice': case 'inputMessagesFilterMusic': case 'inputMessagesFilterDocument': { - for(const message of messages) { - const showSender = this.showSender || (['voice', 'round'] as MyDocument['type'][]).includes(message.media.document.type); - const div = wrapDocument({ - message, - withTime: !showSender, - fontWeight: 400, - voiceAsMusic: true, - showSender, - searchContext: this.copySearchContext(inputFilter), - lazyLoadQueue: this.lazyLoadQueue, - noAutoDownload: true - }); - - if((['audio', 'voice', 'round'] as MyDocument['type'][]).includes(message.media.document.type)) { - div.classList.add('audio-48'); - } - - elemsToAppend.push({element: div, message}); - } + processCallback = this.processDocumentFilter; break; } case 'inputMessagesFilterUrl': { - for(let message of messages) { - let webpage: any; - - if(message.media?.webpage && message.media.webpage._ !== 'webPageEmpty') { - webpage = message.media.webpage; - } else { - const entity = message.totalEntities ? message.totalEntities.find((e: any) => e._ === 'messageEntityUrl' || e._ === 'messageEntityTextUrl') : null; - let url: string, display_url: string, sliced: string; - - if(!entity) { - //this.log.error('NO ENTITY:', message); - const match = RichTextProcessor.matchUrl(message.message); - if(!match) { - //this.log.error('NO ENTITY AND NO MATCH:', message); - continue; - } - - url = match[0]; - } else { - sliced = message.message.slice(entity.offset, entity.offset + entity.length); - } - - if(entity?._ === 'messageEntityTextUrl') { - url = entity.url; - //display_url = sliced; - } else { - url = url || sliced; - } - - display_url = url; - - const same = message.message === url; - if(!url.match(/^(ftp|http|https):\/\//)) { - display_url = 'https://' + url; - url = url.includes('@') ? url : 'https://' + url; - } - - display_url = new URL(display_url).hostname; - - webpage = { - url, - display_url - }; - - if(!same) { - webpage.description = message.message; - webpage.rDescription = RichTextProcessor.wrapRichText(limitSymbols(message.message, 150, 180)); - } - } - - let div = document.createElement('div'); - - let previewDiv = document.createElement('div'); - previewDiv.classList.add('preview', 'row-media'); - - //this.log('wrapping webpage', webpage); - - if(webpage.photo) { - const res = wrapPhoto({ - container: previewDiv, - message: null, - photo: webpage.photo, - boxWidth: 0, - boxHeight: 0, - withoutPreloader: true, - lazyLoadQueue: this.lazyLoadQueue, - middleware, - size: appPhotosManager.choosePhotoSize(webpage.photo, 60, 60, false), - loadPromises: promises, - noBlur: true - }); - } else { - previewDiv.classList.add('empty'); - previewDiv.innerHTML = RichTextProcessor.getAbbreviation(webpage.title || webpage.display_url || webpage.description || webpage.url, true); - } - - let title = webpage.rTitle || ''; - let subtitle = webpage.rDescription || ''; - - const subtitleFragment = htmlToDocumentFragment(subtitle); - const aFragment = htmlToDocumentFragment(RichTextProcessor.wrapRichText(webpage.url || '')); - const a = aFragment.firstElementChild; - if(a instanceof HTMLAnchorElement) { - a.innerText = decodeURIComponent(a.href); - } - - if(subtitleFragment.firstChild) { - subtitleFragment.append('\n'); - } - - subtitleFragment.append(a); - - if(this.showSender) { - subtitleFragment.append('\n', appMessagesManager.wrapSenderToPeer(message)); - } - - if(!title) { - //title = new URL(webpage.url).hostname; - title = RichTextProcessor.wrapPlainText(webpage.display_url.split('/', 1)[0]); - } - - const row = new Row({ - title, - titleRight: appMessagesManager.wrapSentTime(message), - subtitle: subtitleFragment, - havePadding: true, - clickable: true, - noRipple: true - }); - - /* const mediaDiv = document.createElement('div'); - mediaDiv.classList.add('row-media'); */ - - row.container.append(previewDiv); - - /* ripple(div); - div.append(previewDiv); - div.insertAdjacentHTML('beforeend', ` -
${title}${titleAdditionHTML}
-
${subtitle}
-
${url}
- ${sender} - `); */ - - if(row.container.innerText.trim().length) { - elemsToAppend.push({element: row.container, message}); - } - } - + processCallback = this.processUrlFilter; break; } @@ -908,6 +935,23 @@ export default class AppSearchSuper { break; } + if(processCallback) { + processCallback = processCallback.bind(this); + + for(const message of messages) { + try { + options.message = message; + processCallback(options); + } catch(err) { + this.log.error('error rendering filter', inputFilter, options, message, err); + } + } + } + + if(searchGroup && searchGroup.list.childElementCount) { + searchGroup.setActive(); + } + if(this.loadMutex) { promises.push(this.loadMutex); } @@ -1163,7 +1207,7 @@ export default class AppSearchSuper { } promise.then(() => { - appImManager.setInnerPeer(peerId); + appImManager.setInnerPeer({peerId}); }); }); mediaTab.contentTab.append(this.membersList.list); diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index e7fc7e5f..4e924e4a 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -1011,7 +1011,9 @@ export default class ChatBubbles { const contactDiv: HTMLElement = findUpClassName(target, 'contact'); if(contactDiv) { - this.chat.appImManager.setInnerPeer(contactDiv.dataset.peerId.toPeerId()); + this.chat.appImManager.setInnerPeer({ + peerId: contactDiv.dataset.peerId.toPeerId() + }); return; } @@ -1035,7 +1037,11 @@ export default class ChatBubbles { const replies = message.replies; if(replies) { this.appMessagesManager.getDiscussionMessage(this.peerId, message.mid).then(message => { - this.chat.appImManager.setInnerPeer(replies.channel_id.toPeerId(true), undefined, 'discussion', (message as MyMessage).mid); + this.chat.appImManager.setInnerPeer({ + peerId: replies.channel_id.toPeerId(true), + type: 'discussion', + threadId: (message as MyMessage).mid + }); }); } } @@ -1064,11 +1070,14 @@ export default class ChatBubbles { if(savedFrom) { const [peerId, mid] = savedFrom.split('_'); - this.chat.appImManager.setInnerPeer(peerId.toPeerId(), +mid); + this.chat.appImManager.setInnerPeer({ + peerId: peerId.toPeerId(), + lastMsgId: +mid + }); } else { const peerId = peerIdStr.toPeerId(); if(peerId !== NULL_PEER_ID) { - this.chat.appImManager.setInnerPeer(peerId); + this.chat.appImManager.setInnerPeer({peerId}); } else { toast(I18n.format('HidAccount', true)); } @@ -1213,7 +1222,10 @@ export default class ChatBubbles { const savedFrom = bubble.dataset.savedFrom; const [peerId, mid] = savedFrom.split('_'); ////this.log('savedFrom', peerId, msgID); - this.chat.appImManager.setInnerPeer(peerId.toPeerId(), +mid); + this.chat.appImManager.setInnerPeer({ + peerId: peerId.toPeerId(), + lastMsgId: +mid + }); return; } else if(target.classList.contains('forward')) { const mid = +bubble.dataset.mid; @@ -1240,7 +1252,12 @@ export default class ChatBubbles { const replyToPeerId = message.reply_to.reply_to_peer_id ? this.appPeersManager.getPeerId(message.reply_to.reply_to_peer_id) : this.peerId; const replyToMid = message.reply_to.reply_to_msg_id; - this.chat.appImManager.setInnerPeer(replyToPeerId, replyToMid, this.chat.type, this.chat.threadId); + this.chat.appImManager.setInnerPeer({ + peerId: replyToPeerId, + lastMsgId: replyToMid, + type: this.chat.type, + threadId: this.chat.threadId + }); /* if(this.chat.type === 'discussion') { this.chat.appImManager.setMessageId(, originalMessageId); diff --git a/src/components/chat/chat.ts b/src/components/chat/chat.ts index 969b55ad..8b7e3c02 100644 --- a/src/components/chat/chat.ts +++ b/src/components/chat/chat.ts @@ -223,7 +223,7 @@ export default class Chat extends EventListenerBase<{ this.bubbles.listenerSetter.add(rootScope)('dialog_drop', (e) => { if(e.peerId === this.peerId) { - this.appImManager.setPeer(NULL_PEER_ID); + this.appImManager.setPeer(); } }); } @@ -271,8 +271,6 @@ export default class Chat extends EventListenerBase<{ if(!samePeer) { rootScope.dispatchEvent('peer_changing', this); this.peerId = peerId; - this.noForwards = this.appPeersManager.noForwards(peerId); - this.container.classList.toggle('no-forwards', this.noForwards); } else if(this.setPeerPromise) { return; } @@ -297,6 +295,9 @@ export default class Chat extends EventListenerBase<{ searchTab.close(); } + this.noForwards = this.appPeersManager.noForwards(peerId); + this.container.classList.toggle('no-forwards', this.noForwards); + appSidebarRight.sharedMediaTab.setPeer(peerId, this.threadId); this.input.clearHelper(); // костыль this.selection.cleanup(); // TODO: REFACTOR !!!!!! diff --git a/src/components/chat/input.ts b/src/components/chat/input.ts index a2d1235b..20f56be5 100644 --- a/src/components/chat/input.ts +++ b/src/components/chat/input.ts @@ -805,7 +805,7 @@ export default class ChatInput { const peerId = this.chat.peerId; new PopupPinMessage(peerId, 0, true, () => { - this.chat.appImManager.setPeer(NULL_PEER_ID); // * close tab + this.chat.appImManager.setPeer(); // * close tab // ! костыль, это скроет закреплённые сообщения сразу, вместо того, чтобы ждать пока анимация перехода закончится const originalChat = this.chat.appImManager.chat; diff --git a/src/components/chat/topbar.ts b/src/components/chat/topbar.ts index c2f297fc..a30135a9 100644 --- a/src/components/chat/topbar.ts +++ b/src/components/chat/topbar.ts @@ -202,12 +202,12 @@ export default class ChatTopbar { } else { const peerId = container.dataset.peerId.toPeerId(); const searchContext = appMediaPlaybackController.getSearchContext(); - this.chat.appImManager.setInnerPeer( + this.chat.appImManager.setInnerPeer({ peerId, - mid, - searchContext.isScheduled ? 'scheduled' : (searchContext.threadId ? 'discussion' : undefined), - searchContext.threadId - ); + lastMsgId: mid, + type: searchContext.isScheduled ? 'scheduled' : (searchContext.threadId ? 'discussion' : undefined), + threadId: searchContext.threadId + }); } } else { if(mediaSizes.activeScreen === ScreenSize.medium && document.body.classList.contains(LEFT_COLUMN_ACTIVE_CLASSNAME)) { @@ -228,7 +228,7 @@ export default class ChatTopbar { //const item = appNavigationController.findItemByType('chat'); // * return manually to chat by arrow, since can't get back to if(mediaSizes.activeScreen === ScreenSize.medium && document.body.classList.contains(LEFT_COLUMN_ACTIVE_CLASSNAME)) { - this.chat.appImManager.setPeer(this.peerId); + this.chat.appImManager.setPeer({peerId: this.peerId}); } else { const isFirstChat = this.chat.appImManager.chats.indexOf(this.chat) === 0; appNavigationController.back(isFirstChat ? 'im' : 'chat'); @@ -337,7 +337,9 @@ export default class ChatTopbar { const middleware = this.chat.bubbles.getMiddleware(); this.appProfileManager.getChannelFull(this.peerId.toChatId()).then(channelFull => { if(middleware() && channelFull.linked_chat_id) { - this.chat.appImManager.setInnerPeer(channelFull.linked_chat_id.toPeerId(true)); + this.chat.appImManager.setInnerPeer({ + peerId: channelFull.linked_chat_id.toPeerId(true) + }); } }); }, @@ -426,7 +428,7 @@ export default class ChatTopbar { resolve(); this.appMessagesManager.sendOther(peerId, this.appUsersManager.getContactMediaInput(contactPeerId)); - this.chat.appImManager.setInnerPeer(peerId); + this.chat.appImManager.setInnerPeer({peerId}); } }, { langKey: 'Cancel', @@ -640,7 +642,11 @@ export default class ChatTopbar { } public openPinned(byCurrent: boolean) { - this.chat.appImManager.setInnerPeer(this.peerId, byCurrent ? +this.pinnedMessage.pinnedMessageContainer.divAndCaption.container.dataset.mid : 0, 'pinned'); + this.chat.appImManager.setInnerPeer({ + peerId: this.peerId, + lastMsgId: byCurrent ? +this.pinnedMessage.pinnedMessageContainer.divAndCaption.container.dataset.mid : 0, + type: 'pinned' + }); } private onResize = () => { @@ -748,7 +754,7 @@ export default class ChatTopbar { // ! костыль х2, это нужно делать в другом месте if(!count) { - this.chat.appImManager.setPeer(NULL_PEER_ID); // * close tab + this.chat.appImManager.setPeer(); // * close tab // ! костыль, это скроет закреплённые сообщения сразу, вместо того, чтобы ждать пока анимация перехода закончится const originalChat = this.chat.appImManager.chat; diff --git a/src/components/peerTitle.ts b/src/components/peerTitle.ts index 357e997c..6ef5d3aa 100644 --- a/src/components/peerTitle.ts +++ b/src/components/peerTitle.ts @@ -10,9 +10,12 @@ import rootScope from "../lib/rootScope"; import { i18n } from "../lib/langPack"; import replaceContent from "../helpers/dom/replaceContent"; import appUsersManager from "../lib/appManagers/appUsersManager"; +import RichTextProcessor from "../lib/richtextprocessor"; +import { NULL_PEER_ID } from "../lib/mtproto/mtproto_config"; export type PeerTitleOptions = { - peerId: PeerId, + peerId?: PeerId, + fromName?: string, plainText?: boolean, onlyFirstName?: boolean, dialog?: boolean @@ -37,6 +40,7 @@ rootScope.addEventListener('peer_title_edit', (peerId) => { export default class PeerTitle { public element: HTMLElement; public peerId: PeerId; + public fromName: string; public plainText = false; public onlyFirstName = false; public dialog = false; @@ -54,12 +58,21 @@ export default class PeerTitle { if(options) { for(let i in options) { // @ts-ignore - this.element.dataset[i] = options[i] ? '' + (typeof(options[i]) === 'boolean' ? +options[i] : options[i]) : '0'; + // this.element.dataset[i] = options[i] ? '' + (typeof(options[i]) === 'boolean' ? +options[i] : options[i]) : '0'; // @ts-ignore this[i] = options[i]; } } + if(this.fromName !== undefined) { + this.element.innerHTML = RichTextProcessor.wrapEmojiText(this.fromName); + return; + } + + if(this.peerId === undefined) { + this.peerId = NULL_PEER_ID; + } + if(this.peerId !== rootScope.myId || !this.dialog) { if(this.peerId.isUser() && appUsersManager.getUser(this.peerId).pFlags.deleted) { replaceContent(this.element, i18n(this.onlyFirstName ? 'Deleted' : 'HiddenName')); diff --git a/src/components/popups/forward.ts b/src/components/popups/forward.ts index 95716580..588ab88b 100644 --- a/src/components/popups/forward.ts +++ b/src/components/popups/forward.ts @@ -23,7 +23,7 @@ export default class PopupForward extends PopupPickUser { } } - appImManager.setInnerPeer(peerId); + appImManager.setInnerPeer({peerId}); appImManager.chat.input.initMessagesForward(peerIdMids); }, placeholder: 'ShareModal.Search.ForwardPlaceholder', diff --git a/src/components/sidebarLeft/index.ts b/src/components/sidebarLeft/index.ts index 2d25a9dd..c8392359 100644 --- a/src/components/sidebarLeft/index.ts +++ b/src/components/sidebarLeft/index.ts @@ -122,7 +122,9 @@ export class AppSidebarLeft extends SidebarSlider { text: 'SavedMessages', onClick: () => { setTimeout(() => { // menu doesn't close if no timeout (lol) - appImManager.setPeer(appImManager.myId); + appImManager.setPeer({ + peerId: appImManager.myId + }); }, 0); } }, btnArchive, { diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 2e2cc90d..50923365 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -1358,9 +1358,11 @@ export class AppDialogsManager { const peerId = elem.dataset.peerId.toPeerId(); const lastMsgId = +elem.dataset.mid || undefined; - setPeerFunc(peerId, lastMsgId); + setPeerFunc({ + peerId, lastMsgId + }); } else { - setPeerFunc(NULL_PEER_ID); + setPeerFunc(); } }, {capture: true}); @@ -1641,12 +1643,13 @@ export class AppDialogsManager { } private getDialog(dialog: Dialog | PeerId): Dialog { - if(typeof(dialog) !== 'object' && dialog) { + if(typeof(dialog) !== 'object') { const originalDialog = appMessagesManager.getDialogOnly(dialog); if(!originalDialog) { + const peerId = dialog || NULL_PEER_ID; return { - peerId: dialog, - peer: appPeersManager.getOutputPeer(dialog), + peerId, + peer: appPeersManager.getOutputPeer(peerId), pFlags: {} } as any; } @@ -1712,6 +1715,30 @@ export class AppDialogsManager { this.setCallStatus(dom, !!(chat.pFlags.call_active && chat.pFlags.call_not_empty)); } + /** + * use for rendering search result + */ + public addDialogAndSetLastMessage(options: Omit[0], 'dialog'> & { + message: MyMessage, + peerId: PeerId, + query?: string + }) { + const {peerId, message, query} = options; + const ret = appDialogsManager.addDialogNew({ + ...options, + ...appMessagesManager.getMessageSenderPeerIdOrName(message), + dialog: this.getDialog(peerId), + }); + + this.setLastMessage(ret.dialog, message, ret.dom, query); + + if(message.peerId !== peerId) { + ret.dom.listEl.dataset.peerId = '' + message.peerId; + } + + return ret; + } + public addDialogNew(options: { dialog: Parameters[0], container?: Parameters[1], @@ -1723,12 +1750,14 @@ export class AppDialogsManager { avatarSize?: number, autonomous?: boolean, lazyLoadQueue?: LazyLoadQueueIntersector, - loadPromises?: Promise[] + loadPromises?: Promise[], + fromName?: string }) { - return this.addDialog(options.dialog, options.container, options.drawStatus, options.rippleEnabled, options.onlyFirstName, options.meAsSaved, options.append, options.avatarSize, options.autonomous, options.lazyLoadQueue, options.loadPromises); + return this.addDialog(options.dialog, options.container, options.drawStatus, options.rippleEnabled, options.onlyFirstName, options.meAsSaved, options.append, options.avatarSize, options.autonomous, options.lazyLoadQueue, options.loadPromises, options.fromName); } - public addDialog(_dialog: Parameters[0], + public addDialog( + _dialog: Parameters[0], container?: HTMLElement | Scrollable | DocumentFragment | false, drawStatus = true, rippleEnabled = true, @@ -1738,7 +1767,9 @@ export class AppDialogsManager { avatarSize = 54, autonomous = !!container, lazyLoadQueue?: LazyLoadQueueIntersector, - loadPromises?: Promise[]) { + loadPromises?: Promise[], + fromName?: string + ) { const dialog = this.getDialog(_dialog); const peerId = dialog.peerId; @@ -1746,6 +1777,7 @@ export class AppDialogsManager { avatarEl.loadPromises = loadPromises; avatarEl.lazyLoadQueue = lazyLoadQueue; avatarEl.setAttribute('dialog', meAsSaved ? '1' : '0'); + if(fromName !== undefined) avatarEl.setAttribute('peer-title', fromName); avatarEl.setAttribute('peer', '' + peerId); avatarEl.classList.add('dialog-avatar', 'avatar-' + avatarSize); @@ -1764,6 +1796,7 @@ export class AppDialogsManager { const peerTitle = new PeerTitle({ peerId, + fromName, dialog: meAsSaved, onlyFirstName, plainText: false diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 56aa12a1..b30cdac4 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -43,7 +43,7 @@ import appNavigationController from '../../components/appNavigationController'; import appNotificationsManager from './appNotificationsManager'; import AppPrivateSearchTab from '../../components/sidebarRight/tabs/search'; import I18n, { i18n, join, LangPackKey } from '../langPack'; -import { ChatInvite, Dialog, SendMessageAction } from '../../layer'; +import { ChatInvite, Dialog, Message, SendMessageAction } from '../../layer'; import { hslaStringToHex } from '../../helpers/color'; import { copy, getObjectKeysAndSort } from '../../helpers/object'; import { getFilesFromEvent } from '../../helpers/files'; @@ -79,6 +79,7 @@ import IS_GROUP_CALL_SUPPORTED from '../../environment/groupCallSupport'; import appAvatarsManager from './appAvatarsManager'; import IS_CALL_SUPPORTED from '../../environment/callSupport'; import { CallType } from '../calls/types'; +import { Modify } from '../../types'; //console.log('appImManager included33!'); @@ -92,6 +93,19 @@ export type ChatSavedPosition = { top: number }; +export type ChatSetPeerOptions = { + peerId?: PeerId, + lastMsgId?: number, + threadId?: number, + startParam?: string +}; + +export type ChatSetInnerPeerOptions = Modify & { + type?: ChatType +}; + export class AppImManager { public columnEl = document.getElementById('column-center') as HTMLDivElement; public chatsContainer: HTMLElement; @@ -197,7 +211,12 @@ export class AppImManager { if(threadId) threadId = appMessagesIdsManager.generateMessageId(threadId); if(mid) mid = appMessagesIdsManager.generateMessageId(mid); // because mid can come from notification, i.e. server message id - this.setInnerPeer(peerId, mid, threadId ? 'discussion' : undefined, threadId); + this.setInnerPeer({ + peerId, + lastMsgId: mid, + type: threadId ? 'discussion' : undefined, + threadId + }); }); rootScope.addEventListener('peer_changing', (chat) => { @@ -374,7 +393,7 @@ export class AppImManager { // pathnameParams: [string, string?], // uriParams: {comment?: number} pathnameParams: ['c', string, string] | [string, string?], - uriParams: {thread?: string, comment?: string} | {comment?: string} + uriParams: {thread?: string, comment?: string} | {comment?: string, start?: string} }>({ name: 'im', callback: async({pathnameParams, uriParams}) => { @@ -384,7 +403,7 @@ export class AppImManager { _: INTERNAL_LINK_TYPE.PRIVATE_POST, channel: pathnameParams[1], post: pathnameParams[2], - thread: 'thread' in uriParams ? uriParams.thread : undefined, + thread: 'thread' in uriParams && uriParams.thread, comment: uriParams.comment }; } else { @@ -392,7 +411,8 @@ export class AppImManager { _: INTERNAL_LINK_TYPE.MESSAGE, domain: pathnameParams[0], post: pathnameParams[1], - comment: uriParams.comment + comment: uriParams.comment, + start: 'start' in uriParams && uriParams.start }; } @@ -514,7 +534,7 @@ export class AppImManager { } if(nextDialog) { - this.setPeer(nextDialog.peerId); + this.setPeer({peerId: nextDialog.peerId}); } } else if(key === 'ArrowUp') { if(!chat.input.editMsgId && chat.input.isInputEmpty()) { @@ -606,7 +626,11 @@ export class AppImManager { const threadId = link.thread ? appMessagesIdsManager.generateMessageId(+link.thread) : undefined; if(threadId) this.openThread(peerId, postId, threadId); - else this.setInnerPeer(peerId, postId); + else this.setInnerPeer({ + peerId, + lastMsgId: postId, + threadId + }); break; } @@ -627,7 +651,9 @@ export class AppImManager { if(chatInvite._ === 'chatInviteAlready' || chatInvite._ === 'chatInvitePeek'/* && chatInvite.expires > tsNow(true) */) { - this.setInnerPeer(chatInvite.chat.id.toPeerId(true)); + this.setInnerPeer({ + peerId: chatInvite.chat.id.toPeerId(true) + }); return; } @@ -719,7 +745,10 @@ export class AppImManager { } default: { // peerId - this.setInnerPeer(postId ? p.toPeerId(true) : p.toPeerId(), postId); + this.setInnerPeer({ + peerId: postId ? p.toPeerId(true) : p.toPeerId(), + lastMsgId: postId + }); break; } } @@ -730,14 +759,21 @@ export class AppImManager { //location.hash = ''; }; - public openUsername(username: string, msgId?: number, threadId?: number, commentId?: number) { + public openUsername(username: string, lastMsgId?: number, threadId?: number, commentId?: number) { return appUsersManager.resolveUsername(username).then(peer => { const isUser = peer._ === 'user'; - const peerId = isUser ? peer.id.toPeerId() : peer.id.toPeerId(true); + const peerId = peer.id.toPeerId(!isUser); - if(threadId) return this.openThread(peerId, msgId, threadId); - else if(commentId) return this.openComment(peerId, msgId, commentId); - else return this.setInnerPeer(peerId, msgId); + if(threadId) { + return this.openThread(peerId, lastMsgId, threadId); + } else if(commentId) { + return this.openComment(peerId, lastMsgId, commentId); + } + + return this.setInnerPeer({ + peerId, + lastMsgId + }); }, (err) => { if(err.type === 'USERNAME_NOT_OCCUPIED') { toastNew({langPackKey: 'NoUsernameFound'}); @@ -752,10 +788,19 @@ export class AppImManager { */ public openThread(peerId: PeerId, lastMsgId: number, threadId: number) { return appMessagesManager.wrapSingleMessage(peerId, threadId).then(() => { - const message = appMessagesManager.getMessageByPeer(peerId, threadId); - appMessagesManager.generateThreadServiceStartMessage(message); + const message: Message = appMessagesManager.getMessageByPeer(peerId, threadId); + if(message._ === 'messageEmpty') { + lastMsgId = undefined; + } else { + appMessagesManager.generateThreadServiceStartMessage(message); + } - return this.setInnerPeer(peerId, lastMsgId, 'discussion', threadId); + return this.setInnerPeer({ + peerId, + lastMsgId, + threadId, + type: 'discussion' + }); }); } @@ -1044,7 +1089,7 @@ export class AppImManager { appNavigationController.pushItem({ type: 'chat', onPop: (canAnimate) => { - this.setPeer(NULL_PEER_ID, undefined, canAnimate); + this.setPeer({}, canAnimate); blurActiveElement(); } }); @@ -1270,7 +1315,7 @@ export class AppImManager { type: 'im', onPop: (canAnimate) => { //this.selectTab(prevTabId, !isSafari); - this.setPeer(NULL_PEER_ID, undefined, canAnimate); + this.setPeer({}, canAnimate); } }); } @@ -1382,12 +1427,14 @@ export class AppImManager { }, 250 + 100); } - public setPeer(peerId: PeerId, lastMsgId?: number, animate?: boolean): boolean { + public setPeer(options: ChatSetPeerOptions = {}, animate?: boolean): boolean { if(this.init) { this.init(); this.init = null; } + const {peerId, lastMsgId} = options; + const chat = this.chat; const chatIndex = this.chats.indexOf(chat); @@ -1411,7 +1458,7 @@ export class AppImManager { this.spliceChats(0, true, true, spliced); return; } else { - const ret = this.setPeer(peerId, lastMsgId); + const ret = this.setPeer(options); this.spliceChats(0, false, false, spliced); return ret; } @@ -1452,16 +1499,19 @@ export class AppImManager { } } - public setInnerPeer(peerId: PeerId, lastMsgId?: number, type: ChatType = 'chat', threadId?: number) { + public setInnerPeer(options: ChatSetInnerPeerOptions) { + const {peerId} = options; if(peerId === NULL_PEER_ID || !peerId) { return; } + const type = options.type ??= 'chat'; + // * prevent opening already opened peer const existingIndex = this.chats.findIndex(chat => chat.peerId === peerId && chat.type === type); if(existingIndex !== -1) { this.spliceChats(existingIndex + 1); - return this.setPeer(peerId, lastMsgId); + return this.setPeer(options); } const oldChat = this.chat; @@ -1473,8 +1523,8 @@ export class AppImManager { if(type) { chat.setType(type); - if(threadId) { - chat.threadId = threadId; + if(options.threadId) { + chat.threadId = options.threadId; } } @@ -1482,11 +1532,14 @@ export class AppImManager { //this.chatsSelectTab(chat.container); - return this.setPeer(peerId, lastMsgId); + return this.setPeer(options); } public openScheduled(peerId: PeerId) { - this.setInnerPeer(peerId, undefined, 'scheduled'); + this.setInnerPeer({ + peerId, + type: 'scheduled' + }); } private getTypingElement(action: SendMessageAction) { diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index b9c66796..fffa88d7 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -2357,8 +2357,8 @@ export class AppMessagesManager { //return Object.keys(this.groupedMessagesStorage[grouped_id]).map(id => +id).sort((a, b) => a - b); } - public getMidsByMessage(message: Message.message) { - if(message?.grouped_id) return this.getMidsByAlbum(message.grouped_id); + public getMidsByMessage(message: Message) { + if((message as Message.message)?.grouped_id) return this.getMidsByAlbum((message as Message.message).grouped_id); else return [message.mid]; } @@ -2949,7 +2949,7 @@ export class AppMessagesManager { fromMe ? i18n('FromYou') : new PeerTitle({ - peerId: message.fromId, + ...this.getMessageSenderPeerIdOrName(message), dialog: message.peerId === rootScope.myId }).element ); @@ -2962,6 +2962,18 @@ export class AppMessagesManager { return senderTitle; } + public getMessageSenderPeerIdOrName(message: MyMessage) { + if(message.fromId) { + return { + peerId: message.fromId + }; + } else { + return { + fromName: (message as Message.message).fwd_from?.from_name + }; + } + } + public wrapSentTime(message: MyMessage) { const el: HTMLElement = document.createElement('span'); el.classList.add('sent-time'); @@ -3844,7 +3856,7 @@ export class AppMessagesManager { this.getDiscussionMessage(peerId, mid); } - public generateThreadServiceStartMessage(message: Message.message) { + public generateThreadServiceStartMessage(message: Message.message | Message.messageService) { const threadKey = message.peerId + '_' + message.mid; if(this.threadsServiceMessagesIdsStorage[threadKey]) return; diff --git a/src/lib/appManagers/internalLink.ts b/src/lib/appManagers/internalLink.ts index ca967334..58a6a3bf 100644 --- a/src/lib/appManagers/internalLink.ts +++ b/src/lib/appManagers/internalLink.ts @@ -19,7 +19,8 @@ export namespace InternalLink { _: INTERNAL_LINK_TYPE.MESSAGE, domain: string, post?: string, - comment?: string + comment?: string, + start?: string } export interface InternalLinkPrivatePost {