diff --git a/src/components/appMediaViewer.ts b/src/components/appMediaViewer.ts index 4c9905af..2c026d6f 100644 --- a/src/components/appMediaViewer.ts +++ b/src/components/appMediaViewer.ts @@ -11,7 +11,6 @@ import { isMobileSafari, isSafari } from "../helpers/userAgent"; import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager"; import appImManager from "../lib/appManagers/appImManager"; import appMessagesManager from "../lib/appManagers/appMessagesManager"; -import appPeersManager from "../lib/appManagers/appPeersManager"; import appPhotosManager from "../lib/appManagers/appPhotosManager"; import { logger } from "../lib/logger"; import VideoPlayer from "../lib/mediaPlayer"; @@ -44,6 +43,8 @@ import appDownloadManager from "../lib/appManagers/appDownloadManager"; import { cancelEvent } from "../helpers/dom/cancelEvent"; import fillPropertyValue from "../helpers/fillPropertyValue"; import generatePathData from "../helpers/generatePathData"; +import replaceContent from "../helpers/dom/replaceContent"; +import PeerTitle from "./peerTitle"; // TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию // TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода) @@ -851,8 +852,12 @@ class AppMediaViewerBase { - this.divAndCaption.title.innerHTML = title; - this.divAndCaption.subtitle.innerHTML = subtitle; + constructor(protected topbar: ChatTopbar, protected chat: Chat, protected appMessagesManager: AppMessagesManager) { + super(topbar, chat, topbar.listenerSetter, 'audio', new DivAndCaption('pinned-audio', (title: string | HTMLElement, subtitle: string | HTMLElement) => { + replaceContent(this.divAndCaption.title, title); + replaceContent(this.divAndCaption.subtitle, subtitle); }), () => { if(this.toggleEl.classList.contains('flip-icon')) { appMediaPlaybackController.toggle(); @@ -44,10 +45,14 @@ export default class ChatAudio extends PinnedContainer { this.topbar.listenerSetter.add(rootScope, 'audio_play', (e) => { const {doc, mid, peerId} = e; - let title: string, subtitle: string; + let title: string | HTMLElement, subtitle: string; const message = appMessagesManager.getMessageByPeer(peerId, mid); if(doc.type === 'voice' || doc.type === 'round') { - title = appPeersManager.getPeerTitle(message.fromId, false, true); + title = new PeerTitle({ + peerId: message.fromId, + onlyFirstName: true + }).element; + //subtitle = 'Voice message'; subtitle = formatDate(message.date, false, false); } else { @@ -64,4 +69,4 @@ export default class ChatAudio extends PinnedContainer { this.toggleEl.classList.remove('flip-icon'); }); } -} \ No newline at end of file +} diff --git a/src/components/chat/messageRender.ts b/src/components/chat/messageRender.ts index 0abe533f..027cb603 100644 --- a/src/components/chat/messageRender.ts +++ b/src/components/chat/messageRender.ts @@ -6,8 +6,10 @@ import { getFullDate } from "../../helpers/date"; import { formatNumber } from "../../helpers/number"; +import { i18n } from "../../lib/langPack"; import RichTextProcessor from "../../lib/richtextprocessor"; import { LazyLoadQueueIntersector } from "../lazyLoadQueue"; +import PeerTitle from "../peerTitle"; import { wrapReply } from "../wrappers"; import Chat from "./chat"; import RepliesElement from "./replies"; @@ -110,7 +112,7 @@ export namespace MessageRender { const replyToPeerId = message.reply_to.reply_to_peer_id ? chat.appPeersManager.getPeerId(message.reply_to.reply_to_peer_id) : chat.peerId; let originalMessage = chat.appMessagesManager.getMessageByPeer(replyToPeerId, message.reply_to_mid); - let originalPeerTitle: string; + let originalPeerTitle: string | HTMLElement; /////////this.log('message to render reply', originalMessage, originalPeerTitle, bubble, message); @@ -120,9 +122,14 @@ export namespace MessageRender { chat.appMessagesManager.wrapSingleMessage(replyToPeerId, message.reply_to_mid); chat.bubbles.needUpdate.push({replyToPeerId, replyMid: message.reply_to_mid, mid: message.mid}); - originalPeerTitle = 'Loading...'; + originalPeerTitle = i18n('Loading'); } else { - originalPeerTitle = chat.appPeersManager.getPeerTitle(originalMessage.fromId || originalMessage.fwdFromId, true) || ''; + originalPeerTitle = new PeerTitle({ + peerId: originalMessage.fromId || originalMessage.fwdFromId, + dialog: false, + onlyFirstName: false, + plainText: false + }).element; } const wrapped = wrapReply(originalPeerTitle, originalMessage.message || '', originalMessage); diff --git a/src/components/chat/pinnedContainer.ts b/src/components/chat/pinnedContainer.ts index 36350f8c..8272c976 100644 --- a/src/components/chat/pinnedContainer.ts +++ b/src/components/chat/pinnedContainer.ts @@ -22,7 +22,7 @@ export default class PinnedContainer { private close: HTMLElement; protected wrapper: HTMLElement; - constructor(protected topbar: ChatTopbar, protected chat: Chat, public listenerSetter: ListenerSetter, protected className: string, public divAndCaption: DivAndCaption<(title: string, subtitle: string, message?: any) => void>, onClose?: () => void | Promise) { + constructor(protected topbar: ChatTopbar, protected chat: Chat, public listenerSetter: ListenerSetter, protected className: string, public divAndCaption: DivAndCaption<(title: string | HTMLElement, subtitle: string | HTMLElement, message?: any) => void>, onClose?: () => void | Promise) { /* const prev = this.divAndCaption.fill; this.divAndCaption.fill = (mid, title, subtitle) => { this.divAndCaption.container.dataset.mid = '' + mid; @@ -87,7 +87,7 @@ export default class PinnedContainer { this.topbar.setUtilsWidth(); } - public fill(title: string, subtitle: string, message: any) { + public fill(title: string | HTMLElement, subtitle: string | HTMLElement, message: any) { this.divAndCaption.container.dataset.peerId = '' + message.peerId; this.divAndCaption.container.dataset.mid = '' + message.mid; this.divAndCaption.fill(title, subtitle, message); diff --git a/src/components/chat/replyContainer.ts b/src/components/chat/replyContainer.ts index 51ae8c07..91ad1c6a 100644 --- a/src/components/chat/replyContainer.ts +++ b/src/components/chat/replyContainer.ts @@ -5,6 +5,7 @@ */ import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl"; +import replaceContent from "../../helpers/dom/replaceContent"; import { limitSymbols } from "../../helpers/string"; import appDownloadManager from "../../lib/appManagers/appDownloadManager"; import appImManager, { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager"; @@ -15,19 +16,21 @@ import DivAndCaption from "../divAndCaption"; import { wrapSticker } from "../wrappers"; export function wrapReplyDivAndCaption(options: { - title: string, + title: string | HTMLElement, titleEl: HTMLElement, - subtitle: string, + subtitle: string | HTMLElement, subtitleEl: HTMLElement, message: any, mediaEl: HTMLElement }) { let {title, titleEl, subtitle, subtitleEl, mediaEl, message} = options; if(title !== undefined) { - limitSymbols(title, 140); + if(typeof(title) === 'string') { + title = limitSymbols(title, 140); + title = RichTextProcessor.wrapEmojiText(title); + } - title = title ? RichTextProcessor.wrapEmojiText(title) : ''; - titleEl.innerHTML = title; + replaceContent(titleEl, title); } let media = message && message.media; @@ -92,19 +95,22 @@ export function wrapReplyDivAndCaption(options: { } } } else { - subtitle = limitSymbols(subtitle, 140); - subtitle = subtitle ? RichTextProcessor.wrapEmojiText(subtitle) : ''; - subtitleEl.innerHTML = subtitle; + if(typeof(subtitle) === 'string') { + subtitle = limitSymbols(subtitle, 140); + subtitle = RichTextProcessor.wrapEmojiText(subtitle); + } + + replaceContent(subtitleEl, subtitle); } return setMedia; } -export default class ReplyContainer extends DivAndCaption<(title: string, subtitle: string, message?: any) => void> { +export default class ReplyContainer extends DivAndCaption<(title: string | HTMLElement, subtitle: string | HTMLElement, message?: any) => void> { private mediaEl: HTMLElement; constructor(protected className: string) { - super(className, (title: string, subtitle: string = '', message?: any) => { + super(className, (title: string | HTMLElement, subtitle: string | HTMLElement = '', message?: any) => { if(!this.mediaEl) { this.mediaEl = document.createElement('div'); this.mediaEl.classList.add(this.className + '-media'); @@ -127,4 +133,4 @@ export default class ReplyContainer extends DivAndCaption<(title: string, subtit } }); } -} \ No newline at end of file +} diff --git a/src/components/chat/topbar.ts b/src/components/chat/topbar.ts index 560eb843..0f65d925 100644 --- a/src/components/chat/topbar.ts +++ b/src/components/chat/topbar.ts @@ -107,7 +107,7 @@ export default class ChatTopbar { this.chatUtils = document.createElement('div'); this.chatUtils.classList.add('chat-utils'); - this.chatAudio = new ChatAudio(this, this.chat, this.appMessagesManager, this.appPeersManager); + this.chatAudio = new ChatAudio(this, this.chat, this.appMessagesManager); if(this.menuButtons.length) { this.btnMore = ButtonMenuToggle({listenerSetter: this.listenerSetter}, 'bottom-left', this.menuButtons, (e) => { diff --git a/src/components/peerTitle.ts b/src/components/peerTitle.ts index 9a17812b..8a94bcad 100644 --- a/src/components/peerTitle.ts +++ b/src/components/peerTitle.ts @@ -9,6 +9,7 @@ import appPeersManager from "../lib/appManagers/appPeersManager"; import rootScope from "../lib/rootScope"; import { i18n } from "../lib/langPack"; import replaceContent from "../helpers/dom/replaceContent"; +import appUsersManager from "../lib/appManagers/appUsersManager"; export type PeerTitleOptions = { peerId: number, @@ -60,7 +61,11 @@ export default class PeerTitle { } if(this.peerId !== rootScope.myId || !this.dialog) { - this.element.innerHTML = appPeersManager.getPeerTitle(this.peerId, this.plainText, this.onlyFirstName); + if(this.peerId > 0 && appUsersManager.getUser(this.peerId).pFlags.deleted) { + replaceContent(this.element, i18n(this.onlyFirstName ? 'Deleted' : 'HiddenName')); + } else { + this.element.innerHTML = appPeersManager.getPeerTitle(this.peerId, this.plainText, this.onlyFirstName); + } } else { replaceContent(this.element, i18n(this.onlyFirstName ? 'Saved' : 'SavedMessages')); } diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 7eb56ff4..ef3e373e 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -1195,7 +1195,7 @@ export function wrapLocalSticker({emoji, width, height}: { return {container}; } -export function wrapReply(title: string, subtitle: string, message?: any) { +export function wrapReply(title: string | HTMLElement, subtitle: string | HTMLElement, message?: any) { const replyContainer = new ReplyContainer('reply'); replyContainer.fill(title, subtitle, message); /////////console.log('wrapReply', title, subtitle, media); diff --git a/src/helpers/dom/replaceContent.ts b/src/helpers/dom/replaceContent.ts index c6cba8be..fc524b84 100644 --- a/src/helpers/dom/replaceContent.ts +++ b/src/helpers/dom/replaceContent.ts @@ -5,6 +5,11 @@ */ export default function replaceContent(elem: HTMLElement, node: string | Node) { + if(typeof(node) === 'string') { + elem.innerHTML = node; + return; + } + // * children.length doesn't count text nodes const firstChild = elem.firstChild; if(firstChild) { diff --git a/src/helpers/sequentialDom.ts b/src/helpers/sequentialDom.ts index 31f23379..1d2f3b1f 100644 --- a/src/helpers/sequentialDom.ts +++ b/src/helpers/sequentialDom.ts @@ -1,3 +1,9 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + import { fastRaf } from "./schedulers"; import { CancellablePromise, deferredPromise } from "./cancellablePromise"; import { MOUNT_CLASS_TO } from "../config/debug"; diff --git a/src/helpers/string.ts b/src/helpers/string.ts index 5c901fbf..a4cb8158 100644 --- a/src/helpers/string.ts +++ b/src/helpers/string.ts @@ -84,13 +84,13 @@ export function splitStringByLength(str: string, maxLength: number) { } // https://stackoverflow.com/a/14824756 -export const checkRTL = (s: string) => { +/* export const checkRTL = (s: string) => { const ltrChars = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF'+'\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF', rtlChars = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC', rtlDirCheck = new RegExp('^[^'+ltrChars+']*['+rtlChars+']'); return rtlDirCheck.test(s); -}; +}; */ //(window as any).checkRTL = checkRTL; diff --git a/src/lang.ts b/src/lang.ts index a248fc8b..ca8ac449 100644 --- a/src/lang.ts +++ b/src/lang.ts @@ -50,7 +50,6 @@ const lang = { "ConnectionStatus.Waiting": "Waiting for network...", "Deactivated.Title": "Too many tabs...", "Deactivated.Subtitle": "Telegram supports only one active tab with the app.\nClick anywhere to continue using this tab.", - //"Saved": "Saved", "General.Keyboard": "Keyboard", "General.SendShortcut.Enter": "Send by Enter", "General.SendShortcut.CtrlEnter": "Send by %s + Enter", @@ -107,6 +106,7 @@ const lang = { "MediaViewer.Context.Download": "Download", "Profile": "Profile", "Saved": "Saved", + "Deleted": "Deleted", "ReportBug": "Report Bug", "Notifications.Count": { "one_value": "%d notification", @@ -440,6 +440,7 @@ const lang = { "Updating": "Updating...", "Emoji": "Emoji", "AddContactTitle": "Add Contact", + "HiddenName": "Deleted Account", // * macos "AccountSettings.Filters": "Chat Folders", diff --git a/src/lib/appManagers/appPeersManager.ts b/src/lib/appManagers/appPeersManager.ts index f04e5124..fa194f49 100644 --- a/src/lib/appManagers/appPeersManager.ts +++ b/src/lib/appManagers/appPeersManager.ts @@ -17,6 +17,7 @@ import { RichTextProcessor } from "../richtextprocessor"; import rootScope from "../rootScope"; import appChatsManager from "./appChatsManager"; import appUsersManager from "./appUsersManager"; +import I18n from '../langPack'; // https://github.com/eelcohn/Telegram-API/wiki/Calculating-color-for-a-Telegram-user-on-IRC /* @@ -88,7 +89,7 @@ export class AppPeersManager { if(peer.first_name) title += peer.first_name; if(peer.last_name) title += ' ' + peer.last_name; - if(!title) title = peer.pFlags.deleted ? 'Deleted Account' : peer.username; + if(!title) title = peer.pFlags.deleted ? I18n.format('HiddenName', true) : peer.username; else title = title.trim(); } else { title = peer.title; diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index cf135e59..5681b632 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -1284,6 +1284,7 @@ $bubble-margin: .25rem; &-icon { margin-left: 2px; + pointer-events: none; } i.edited {