Chatlist media preview

This commit is contained in:
Eduard Kuzmenko 2021-09-03 19:33:10 +03:00
parent caa4e63189
commit 35d8775e77
12 changed files with 302 additions and 169 deletions

View File

@ -803,7 +803,7 @@ export default class AppSearchSuper {
} }
}), }),
appMessagesManager.getConversations(query, 0, 20, 0) appMessagesManager.getConversations(query, 0, 20, 0).promise
.then(onLoad) .then(onLoad)
.then(value => { .then(value => {
if(value) { if(value) {

View File

@ -261,7 +261,7 @@ export default class AppSelectPeers {
const pageCount = windowSize.windowH / 72 * 1.25 | 0; const pageCount = windowSize.windowH / 72 * 1.25 | 0;
const tempId = this.getTempId('dialogs'); const tempId = this.getTempId('dialogs');
const promise = appMessagesManager.getConversations(this.query, this.offsetIndex, pageCount, this.folderId, true); const promise = appMessagesManager.getConversations(this.query, this.offsetIndex, pageCount, this.folderId, true).promise;
this.promise = promise; this.promise = promise;
const value = await promise; const value = await promise;
if(this.tempIds.dialogs !== tempId) { if(this.tempIds.dialogs !== tempId) {

View File

@ -212,11 +212,11 @@ export default class Scrollable extends ScrollableBase {
} }
}; };
public prepend(...elements: HTMLElement[]) { public prepend(...elements: (HTMLElement | DocumentFragment)[]) {
(this.splitUp || this.padding || this.container).prepend(...elements); (this.splitUp || this.padding || this.container).prepend(...elements);
} }
public append(...elements: HTMLElement[]) { public append(...elements: (HTMLElement | DocumentFragment)[]) {
(this.splitUp || this.padding || this.container).append(...elements); (this.splitUp || this.padding || this.container).append(...elements);
} }

View File

@ -6,7 +6,14 @@
import rootScope from "../lib/rootScope"; import rootScope from "../lib/rootScope";
const SetTransition = (element: HTMLElement, className: string, forwards: boolean, duration: number, onTransitionEnd?: () => void, useRafs?: number) => { const SetTransition = (
element: HTMLElement,
className: string,
forwards: boolean,
duration: number,
onTransitionEnd?: () => void,
useRafs?: number
) => {
const {timeout, raf} = element.dataset; const {timeout, raf} = element.dataset;
if(timeout !== undefined) { if(timeout !== undefined) {
clearTimeout(+timeout); clearTimeout(+timeout);
@ -19,7 +26,7 @@ const SetTransition = (element: HTMLElement, className: string, forwards: boolea
} }
} }
if(useRafs) { if(useRafs && rootScope.settings.animationsEnabled && duration) {
element.dataset.raf = '' + window.requestAnimationFrame(() => { element.dataset.raf = '' + window.requestAnimationFrame(() => {
delete element.dataset.raf; delete element.dataset.raf;
SetTransition(element, className, forwards, duration, onTransitionEnd, useRafs - 1); SetTransition(element, className, forwards, duration, onTransitionEnd, useRafs - 1);
@ -43,7 +50,7 @@ const SetTransition = (element: HTMLElement, className: string, forwards: boolea
onTransitionEnd && onTransitionEnd(); onTransitionEnd && onTransitionEnd();
}; };
if(!rootScope.settings.animationsEnabled) { if(!rootScope.settings.animationsEnabled || !duration) {
element.classList.remove('animating', 'backwards'); element.classList.remove('animating', 'backwards');
afterTimeout(); afterTimeout();
return; return;

View File

@ -16,7 +16,7 @@ export const MAIN_DOMAIN = 'web.telegram.org';
const App = { const App = {
id: 1025907, id: 1025907,
hash: '452b0359b988148995f22ff0f4229750', hash: '452b0359b988148995f22ff0f4229750',
version: '0.8.0', version: '0.8.1',
langPackVersion: '0.3.3', langPackVersion: '0.3.3',
langPack: 'macos', langPack: 'macos',
langPackCode: 'en', langPackCode: 'en',

View File

@ -4,6 +4,8 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
// import { getHeavyAnimationPromise } from "../../hooks/useHeavyAnimationCheck";
export const loadedURLs: {[url: string]: boolean} = {}; export const loadedURLs: {[url: string]: boolean} = {};
const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => { const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => {
if(elem instanceof HTMLImageElement || elem instanceof HTMLVideoElement) elem.src = url; if(elem instanceof HTMLImageElement || elem instanceof HTMLVideoElement) elem.src = url;
@ -12,8 +14,12 @@ const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoE
}; };
// проблема функции в том, что она не подходит для ссылок, пригодна только для blob'ов, потому что обычным ссылкам нужен 'load' каждый раз. // проблема функции в том, что она не подходит для ссылок, пригодна только для blob'ов, потому что обычным ссылкам нужен 'load' каждый раз.
export default function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, export default function renderImageFromUrl(
url: string, callback?: (err?: Event) => void, useCache = true) { elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement,
url: string,
callback?: (err?: Event) => void,
useCache = true
) {
if(!url) { if(!url) {
console.error('renderImageFromUrl: no url?', elem, url); console.error('renderImageFromUrl: no url?', elem, url);
callback && callback(); callback && callback();
@ -26,6 +32,7 @@ export default function renderImageFromUrl(elem: HTMLElement | HTMLImageElement
} }
callback && callback(); callback && callback();
// callback && getHeavyAnimationPromise().then(() => callback());
} else { } else {
const isImage = elem instanceof HTMLImageElement; const isImage = elem instanceof HTMLImageElement;
const loader = isImage ? elem as HTMLImageElement : new Image(); const loader = isImage ? elem as HTMLImageElement : new Image();
@ -39,16 +46,10 @@ export default function renderImageFromUrl(elem: HTMLElement | HTMLImageElement
loadedURLs[url] = true; loadedURLs[url] = true;
//console.log('onload:', url, performance.now() - perf); //console.log('onload:', url, performance.now() - perf);
if(callback) { // TODO: переделать прогрузки аватаров до начала анимации, иначе с этим ожиданием они неприятно появляются
// TODO: переделать прогрузки аватаров до начала анимации, иначе с этим ожиданием они неприятно появляются // callback && getHeavyAnimationPromise().then(() => callback());
/* getHeavyAnimationPromise().then(() => { callback && callback();
callback(); }, {once: true});
}); */
callback();
}
//callback && callback();
});
if(callback) { if(callback) {
loader.addEventListener('error', callback); loader.addEventListener('error', callback);

View File

@ -4,6 +4,9 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import type DialogsStorage from "../storages/dialogs";
import type {MyDialogFilter as DialogFilter, MyDialogFilter} from "../storages/filters";
import type { LazyLoadQueueIntersector } from "../../components/lazyLoadQueue";
import AvatarElement from "../../components/avatar"; import AvatarElement from "../../components/avatar";
import DialogsContextMenu from "../../components/dialogsContextMenu"; import DialogsContextMenu from "../../components/dialogsContextMenu";
import { horizontalMenu } from "../../components/horizontalMenu"; import { horizontalMenu } from "../../components/horizontalMenu";
@ -20,7 +23,6 @@ import apiUpdatesManager from "./apiUpdatesManager";
import appPeersManager from './appPeersManager'; import appPeersManager from './appPeersManager';
import appImManager from "./appImManager"; import appImManager from "./appImManager";
import appMessagesManager, { Dialog } from "./appMessagesManager"; import appMessagesManager, { Dialog } from "./appMessagesManager";
import {MyDialogFilter as DialogFilter, MyDialogFilter} from "../storages/filters";
import appStateManager, { State } from "./appStateManager"; import appStateManager, { State } from "./appStateManager";
import appUsersManager from "./appUsersManager"; import appUsersManager from "./appUsersManager";
import Button from "../../components/button"; import Button from "../../components/button";
@ -31,7 +33,6 @@ import appNotificationsManager from "./appNotificationsManager";
import PeerTitle from "../../components/peerTitle"; import PeerTitle from "../../components/peerTitle";
import I18n, { FormatterArguments, i18n, LangPackKey, _i18n } from "../langPack"; import I18n, { FormatterArguments, i18n, LangPackKey, _i18n } from "../langPack";
import findUpTag from "../../helpers/dom/findUpTag"; import findUpTag from "../../helpers/dom/findUpTag";
import { LazyLoadQueueIntersector } from "../../components/lazyLoadQueue";
import lottieLoader from "../lottieLoader"; import lottieLoader from "../lottieLoader";
import { wrapLocalSticker, wrapPhoto } from "../../components/wrappers"; import { wrapLocalSticker, wrapPhoto } from "../../components/wrappers";
import AppEditFolderTab from "../../components/sidebarLeft/tabs/editFolder"; import AppEditFolderTab from "../../components/sidebarLeft/tabs/editFolder";
@ -48,8 +49,8 @@ import { isTouchSupported } from "../../helpers/touchSupport";
import handleTabSwipe from "../../helpers/dom/handleTabSwipe"; import handleTabSwipe from "../../helpers/dom/handleTabSwipe";
import windowSize from "../../helpers/windowSize"; import windowSize from "../../helpers/windowSize";
import isInDOM from "../../helpers/dom/isInDOM"; import isInDOM from "../../helpers/dom/isInDOM";
import appPhotosManager from "./appPhotosManager"; import appPhotosManager, { MyPhoto } from "./appPhotosManager";
import DialogsStorage from "../storages/dialogs"; import { MyDocument } from "./appDocsManager";
export type DialogDom = { export type DialogDom = {
avatarEl: AvatarElement, avatarEl: AvatarElement,
@ -515,7 +516,7 @@ export class AppDialogsManager {
if(this.isDialogMustBeInViewport(dialog)) { if(this.isDialogMustBeInViewport(dialog)) {
if(!this.doms.hasOwnProperty(dialog.peerId)) { if(!this.doms.hasOwnProperty(dialog.peerId)) {
const ret = this.addDialogNew({dialog}); const ret = this.addListDialog({dialog});
if(ret) { if(ret) {
const idx = appMessagesManager.getDialogByPeerId(dialog.peerId)[1]; const idx = appMessagesManager.getDialogByPeerId(dialog.peerId)[1];
positionElementByIndex(ret.dom.listEl, this.chatList, idx); positionElementByIndex(ret.dom.listEl, this.chatList, idx);
@ -749,11 +750,12 @@ export class AppDialogsManager {
try { try {
//console.time('getDialogs time'); //console.time('getDialogs time');
const getConversationPromise = (this.filterId > 1 ? appUsersManager.getContacts() as Promise<any> : Promise.resolve()).then(() => { const getConversationsResult = appMessagesManager.getConversations('', offsetIndex, loadCount, filterId, true);
return appMessagesManager.getConversations('', offsetIndex, loadCount, filterId, true); if(getConversationsResult.cached) {
}); this.chatsPreloader.remove();
}
const result = await getConversationPromise; const result = await getConversationsResult.promise;
if(this.loadDialogsPromise !== promise) { if(this.loadDialogsPromise !== promise) {
return; return;
@ -779,12 +781,26 @@ export class AppDialogsManager {
if(result.dialogs.length) { if(result.dialogs.length) {
const dialogs = side === 'top' ? result.dialogs.slice().reverse() : result.dialogs; const dialogs = side === 'top' ? result.dialogs.slice().reverse() : result.dialogs;
const container = document.createDocumentFragment();
const loadPromises: Promise<any>[] = [];
const append = side === 'bottom';
dialogs.forEach((dialog) => { dialogs.forEach((dialog) => {
this.addDialogNew({ this.addListDialog({
dialog, dialog,
append: side === 'bottom' container,
append,
loadPromises,
isBatch: true
}); });
}); });
if(container.childElementCount) {
if(loadPromises.length) {
await Promise.all(loadPromises).finally();
}
this.scroll[append ? 'append' : 'prepend'](container);
}
} }
const offsetDialog = result.dialogs[side === 'top' ? 0 : result.dialogs.length - 1]; const offsetDialog = result.dialogs[side === 'top' ? 0 : result.dialogs.length - 1];
@ -1215,11 +1231,28 @@ export class AppDialogsManager {
private reorderDialogs() { private reorderDialogs() {
//const perf = performance.now(); //const perf = performance.now();
if(this.reorderDialogsTimeout) { if(this.reorderDialogsTimeout) {
window.cancelAnimationFrame(this.reorderDialogsTimeout); return;
}
if(this.loadDialogsPromise) {
this.loadDialogsPromise.then(() => {
this.reorderDialogs();
});
return;
} }
this.reorderDialogsTimeout = window.requestAnimationFrame(() => { this.reorderDialogsTimeout = window.requestAnimationFrame(() => {
this.reorderDialogsTimeout = 0; this.reorderDialogsTimeout = 0;
if(this.loadDialogsPromise) {
this.loadDialogsPromise.then(() => {
this.reorderDialogs();
});
return;
}
const dialogs = appMessagesManager.dialogsStorage.getFolder(this.filterId); const dialogs = appMessagesManager.dialogsStorage.getFolder(this.filterId);
const currentOrder = (Array.from(this.chatList.children) as HTMLElement[]).map(el => +el.dataset.peerId); const currentOrder = (Array.from(this.chatList.children) as HTMLElement[]).map(el => +el.dataset.peerId);
@ -1253,7 +1286,14 @@ export class AppDialogsManager {
}); });
} }
public setLastMessage(dialog: Dialog, lastMessage?: any, dom?: DialogDom, highlightWord?: string) { public setLastMessage(
dialog: Dialog,
lastMessage?: any,
dom?: DialogDom,
highlightWord?: string,
loadPromises?: Promise<any>[],
isBatch = false
) {
///////console.log('setlastMessage:', lastMessage); ///////console.log('setlastMessage:', lastMessage);
if(!dom) { if(!dom) {
dom = this.getDialogDom(dialog.peerId); dom = this.getDialogDom(dialog.peerId);
@ -1280,7 +1320,6 @@ export class AppDialogsManager {
return; return;
} }
const peer = dialog.peer;
const peerId = dialog.peerId; const peerId = dialog.peerId;
//let peerId = appMessagesManager.getMessagePeer(lastMessage); //let peerId = appMessagesManager.getMessagePeer(lastMessage);
@ -1288,35 +1327,49 @@ export class AppDialogsManager {
/* if(!dom.lastMessageSpan.classList.contains('user-typing')) */ { /* if(!dom.lastMessageSpan.classList.contains('user-typing')) */ {
let mediaContainer: HTMLElement;
if(!lastMessage.deleted && !draftMessage) {
const media: MyDocument | MyPhoto = appMessagesManager.getMediaFromMessage(lastMessage);
if(media && (media._ === 'photo' || (['video', 'gif'] as MyDocument['type'][]).includes(media.type))) {
const size = appPhotosManager.choosePhotoSize(media, 20, 20);
if(size._ !== 'photoSizeEmpty') {
mediaContainer = document.createElement('div');
mediaContainer.classList.add('dialog-subtitle-media');
wrapPhoto({
photo: media,
message: lastMessage,
container: mediaContainer,
withoutPreloader: true,
size,
loadPromises
});
if((media as MyDocument).type === 'video') {
const playIcon = document.createElement('span');
playIcon.classList.add('tgico-play');
mediaContainer.append(playIcon);
}
}
}
}
const withoutMediaType = !!mediaContainer && !!lastMessage?.message;
let fragment: DocumentFragment; let fragment: DocumentFragment;
if(highlightWord && lastMessage.message) { if(highlightWord && lastMessage.message) {
fragment = appMessagesManager.wrapMessageForReply(lastMessage, undefined, undefined, false, highlightWord); fragment = appMessagesManager.wrapMessageForReply(lastMessage, undefined, undefined, false, highlightWord, withoutMediaType);
} else if(draftMessage) { } else if(draftMessage) {
fragment = appMessagesManager.wrapMessageForReply(draftMessage); fragment = appMessagesManager.wrapMessageForReply(draftMessage);
} else if(!lastMessage.deleted) { } else if(!lastMessage.deleted) {
fragment = appMessagesManager.wrapMessageForReply(lastMessage); fragment = appMessagesManager.wrapMessageForReply(lastMessage, undefined, undefined, false, undefined, withoutMediaType);
} }
/* if(!lastMessage.deleted && !draftMessage) { if(mediaContainer) {
const photo = lastMessage.media?.photo; fragment.prepend(mediaContainer);
if(photo) { }
const div = document.createElement('div');
div.classList.add('dialog-subtitle-media');
const size = appPhotosManager.choosePhotoSize(photo, 20, 20);
wrapPhoto({
photo,
message: lastMessage,
container: div,
withoutPreloader: true,
size
});
fragment.prepend(div);
// dom.subtitleEl.prepend(div);
}
} */
replaceContent(dom.lastMessageSpan, fragment); replaceContent(dom.lastMessageSpan, fragment);
@ -1326,7 +1379,7 @@ export class AppDialogsManager {
bold.classList.add('danger'); bold.classList.add('danger');
bold.append(i18n('Draft'), ': '); bold.append(i18n('Draft'), ': ');
dom.lastMessageSpan.prepend(bold); dom.lastMessageSpan.prepend(bold);
} else if(peer._ !== 'peerUser' && peerId !== lastMessage.fromId && !lastMessage.action) { } else if(peerId < 0 && peerId !== lastMessage.fromId && !lastMessage.action) {
const sender = appPeersManager.getPeer(lastMessage.fromId); const sender = appPeersManager.getPeer(lastMessage.fromId);
if(sender && sender.id) { if(sender && sender.id) {
const senderBold = document.createElement('b'); const senderBold = document.createElement('b');
@ -1354,13 +1407,13 @@ export class AppDialogsManager {
} else dom.lastTimeSpan.textContent = ''; } else dom.lastTimeSpan.textContent = '';
if(this.doms[peerId] === dom) { if(this.doms[peerId] === dom) {
this.setUnreadMessages(dialog); this.setUnreadMessages(dialog, isBatch);
} else { // means search } else { // means search
dom.listEl.dataset.mid = lastMessage.mid; dom.listEl.dataset.mid = lastMessage.mid;
} }
} }
private setUnreadMessages(dialog: Dialog) { private setUnreadMessages(dialog: Dialog, isBatch = false) {
const dom = this.getDialogDom(dialog.peerId); const dom = this.getDialogDom(dialog.peerId);
if(dialog.folder_id === 1) { if(dialog.folder_id === 1) {
@ -1372,10 +1425,12 @@ export class AppDialogsManager {
return; return;
} }
const isMuted = appNotificationsManager.isPeerLocalMuted(dialog.peerId, true); if(!isBatch) {
const wasMuted = dom.listEl.classList.contains('is-muted'); const isMuted = appNotificationsManager.isPeerLocalMuted(dialog.peerId, true);
if(isMuted !== wasMuted) { const wasMuted = dom.listEl.classList.contains('is-muted');
SetTransition(dom.listEl, 'is-muted', isMuted, 200); if(isMuted !== wasMuted) {
SetTransition(dom.listEl, 'is-muted', isMuted, 200);
}
} }
const lastMessage = dialog.draft?._ === 'draftMessage' ? const lastMessage = dialog.draft?._ === 'draftMessage' ?
@ -1422,12 +1477,14 @@ export class AppDialogsManager {
} }
} }
SetTransition(dom.unreadBadge, 'is-visible', hasUnreadBadge, 200, hasUnreadBadge ? undefined : () => { const transitionDuration = isBatch ? 0 : 200;
SetTransition(dom.unreadBadge, 'is-visible', hasUnreadBadge, transitionDuration, hasUnreadBadge ? undefined : () => {
dom.unreadBadge.remove(); dom.unreadBadge.remove();
}, !isUnreadBadgeMounted ? 2 : 0); }, !isUnreadBadgeMounted ? 2 : 0);
if(dom.mentionsBadge) { if(dom.mentionsBadge) {
SetTransition(dom.mentionsBadge, 'is-visible', hasMentionsBadge, 200, hasMentionsBadge ? undefined : () => { SetTransition(dom.mentionsBadge, 'is-visible', hasMentionsBadge, transitionDuration, hasMentionsBadge ? undefined : () => {
dom.mentionsBadge.remove(); dom.mentionsBadge.remove();
delete dom.mentionsBadge; delete dom.mentionsBadge;
}, !isMentionBadgeMounted ? 2 : 0); }, !isMentionBadgeMounted ? 2 : 0);
@ -1474,9 +1531,55 @@ export class AppDialogsManager {
return this.doms[peerId]; return this.doms[peerId];
} }
private getDialog(dialog: Dialog | number): Dialog {
if(typeof(dialog) === 'number') {
const originalDialog = appMessagesManager.getDialogOnly(dialog);
if(!originalDialog) {
return {
peerId: dialog,
peer: appPeersManager.getOutputPeer(dialog),
pFlags: {}
} as any;
}
return originalDialog;
}
return dialog;
}
private addListDialog(options: Parameters<AppDialogsManager['addDialogNew']>[0] & {isBatch?: boolean}) {
const dialog = this.getDialog(options.dialog);
const filter = appMessagesManager.filtersStorage.getFilter(this.filterId);
if((filter && !appMessagesManager.filtersStorage.testDialogForFilter(dialog, filter)) ||
(!filter && this.filterId !== dialog.folder_id)) {
return;
}
if(!options.container) {
options.container = this.scroll;
}
const ret = this.addDialogNew(options);
if(ret) {
this.doms[dialog.peerId] = ret.dom;
const isMuted = appNotificationsManager.isPeerLocalMuted(dialog.peerId, true);
if(isMuted) {
ret.dom.listEl.classList.add('is-muted');
}
this.setLastMessage(dialog, undefined, undefined, undefined, options.loadPromises, options.isBatch);
}
return ret;
}
public addDialogNew(options: { public addDialogNew(options: {
dialog: Dialog | number, dialog: Dialog | number,
container?: HTMLUListElement | Scrollable | false, container?: Parameters<AppDialogsManager['addDialog']>[1],
drawStatus?: boolean, drawStatus?: boolean,
rippleEnabled?: boolean, rippleEnabled?: boolean,
onlyFirstName?: boolean, onlyFirstName?: boolean,
@ -1485,60 +1588,40 @@ export class AppDialogsManager {
avatarSize?: number, avatarSize?: number,
autonomous?: boolean, autonomous?: boolean,
lazyLoadQueue?: LazyLoadQueueIntersector, lazyLoadQueue?: LazyLoadQueueIntersector,
loadPromises?: Promise<any>[]
}) { }) {
return this.addDialog(options.dialog, options.container, options.drawStatus, options.rippleEnabled, options.onlyFirstName, options.meAsSaved, options.append, options.avatarSize, options.autonomous, options.lazyLoadQueue); 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);
} }
public addDialog(_dialog: Dialog | number, container?: HTMLUListElement | Scrollable | false, drawStatus = true, rippleEnabled = true, onlyFirstName = false, meAsSaved = true, append = true, avatarSize = 54, autonomous = !!container, lazyLoadQueue?: LazyLoadQueueIntersector) { public addDialog(_dialog: Dialog | number,
let dialog: Dialog; container?: HTMLElement | Scrollable | DocumentFragment | false,
drawStatus = true,
if(typeof(_dialog) === 'number') { rippleEnabled = true,
let originalDialog = appMessagesManager.getDialogOnly(_dialog); onlyFirstName = false,
if(!originalDialog) { meAsSaved = true,
originalDialog = { append = true,
peerId: _dialog, avatarSize = 54,
peer: appPeersManager.getOutputPeer(_dialog), autonomous = !!container,
pFlags: {} lazyLoadQueue?: LazyLoadQueueIntersector,
} as any; loadPromises?: Promise<any>[]) {
} const dialog = this.getDialog(_dialog);
const peerId = dialog.peerId;
dialog = originalDialog;
} else {
dialog = _dialog;
}
const peerId: number = dialog.peerId;
if(container === undefined) {
if(this.doms[peerId] || dialog.migratedTo !== undefined) return;
const filter = appMessagesManager.filtersStorage.getFilter(this.filterId);
if((filter && !appMessagesManager.filtersStorage.testDialogForFilter(dialog, filter)) || (!filter && this.filterId !== dialog.folder_id)) {
return;
}
}
const avatarEl = new AvatarElement(); const avatarEl = new AvatarElement();
avatarEl.loadPromises = loadPromises;
avatarEl.lazyLoadQueue = lazyLoadQueue; avatarEl.lazyLoadQueue = lazyLoadQueue;
avatarEl.setAttribute('dialog', meAsSaved ? '1' : '0'); avatarEl.setAttribute('dialog', meAsSaved ? '1' : '0');
avatarEl.setAttribute('peer', '' + peerId); avatarEl.setAttribute('peer', '' + peerId);
avatarEl.classList.add('dialog-avatar', 'avatar-' + avatarSize); avatarEl.classList.add('dialog-avatar', 'avatar-' + avatarSize);
if(drawStatus && peerId !== rootScope.myId && dialog.peer) { if(drawStatus && peerId !== rootScope.myId) {
const peer = dialog.peer; if(peerId > 0) {
const user = appUsersManager.getUser(peerId);
//console.log('found user', user);
switch(peer._) { if(user.status && user.status._ === 'userStatusOnline') {
case 'peerUser': avatarEl.classList.add('is-online');
const user = appUsersManager.getUser(peerId); }
//console.log('found user', user);
if(user.status && user.status._ === 'userStatusOnline') {
avatarEl.classList.add('is-online');
}
break;
default:
break;
} }
} }
@ -1629,23 +1712,8 @@ export class AppDialogsManager {
good = true; good = true;
} }
} */ } */
const method: 'append' | 'prepend' = append ? 'append' : 'prepend'; if(container) {
if(container === undefined/* || good */) { const method = append ? 'append' : 'prepend';
this.scroll[method](li);
this.doms[dialog.peerId] = dom;
/* if(container) {
container.append(li);
} */
const isMuted = appNotificationsManager.isPeerLocalMuted(dialog.peerId, true);
if(isMuted) {
li.classList.add('is-muted');
}
this.setLastMessage(dialog);
} else if(container) {
container[method](li); container[method](li);
} }

View File

@ -1639,7 +1639,7 @@ export class AppMessagesManager {
for(; folderId < 2; ++folderId) { for(; folderId < 2; ++folderId) {
let offsetIndex = 0; let offsetIndex = 0;
for(;;) { for(;;) {
const {dialogs} = await appMessagesManager.getConversations(query, offsetIndex, limit, folderId); const {dialogs} = await appMessagesManager.getConversations(query, offsetIndex, limit, folderId).promise;
if(dialogs.length) { if(dialogs.length) {
outDialogs.push(...dialogs); outDialogs.push(...dialogs);
@ -2508,9 +2508,9 @@ export class AppMessagesManager {
message.totalEntities = RichTextProcessor.mergeEntities(apiEntities, myEntities); // ! only in this order, otherwise bold and emoji formatting won't work message.totalEntities = RichTextProcessor.mergeEntities(apiEntities, myEntities); // ! only in this order, otherwise bold and emoji formatting won't work
} }
public wrapMessageForReply(message: any, text: string, usingMids: number[], plain: true, highlightWord?: string): string; public wrapMessageForReply(message: any, text: string, usingMids: number[], plain: true, highlightWord?: string, withoutMediaType?: boolean): string;
public wrapMessageForReply(message: any, text?: string, usingMids?: number[], plain?: false, highlightWord?: string): DocumentFragment; public wrapMessageForReply(message: any, text?: string, usingMids?: number[], plain?: false, highlightWord?: string, withoutMediaType?: boolean): DocumentFragment;
public wrapMessageForReply(message: any, text: string = message.message, usingMids?: number[], plain?: boolean, highlightWord?: string): DocumentFragment | string { public wrapMessageForReply(message: any, text: string = message.message, usingMids?: number[], plain?: boolean, highlightWord?: string, withoutMediaType?: boolean): DocumentFragment | string {
const parts: (HTMLElement | string)[] = []; const parts: (HTMLElement | string)[] = [];
const addPart = (langKey: LangPackKey, part?: string | HTMLElement, text?: string) => { const addPart = (langKey: LangPackKey, part?: string | HTMLElement, text?: string) => {
@ -2557,7 +2557,7 @@ export class AppMessagesManager {
usingFullAlbum = false; usingFullAlbum = false;
} }
if(!usingFullAlbum) { if(!usingFullAlbum && !withoutMediaType) {
const media = message.media; const media = message.media;
switch(media._) { switch(media._) {
case 'messageMediaPhoto': case 'messageMediaPhoto':

View File

@ -11,6 +11,7 @@
import { MOUNT_CLASS_TO } from "../../config/debug"; import { MOUNT_CLASS_TO } from "../../config/debug";
import { filterUnique } from "../../helpers/array"; import { filterUnique } from "../../helpers/array";
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
import cleanSearchText from "../../helpers/cleanSearchText"; import cleanSearchText from "../../helpers/cleanSearchText";
import cleanUsername from "../../helpers/cleanUsername"; import cleanUsername from "../../helpers/cleanUsername";
import { tsNow } from "../../helpers/date"; import { tsNow } from "../../helpers/date";
@ -42,7 +43,7 @@ export class AppUsersManager {
private users: {[userId: number]: User}; private users: {[userId: number]: User};
private usernames: {[username: string]: number}; private usernames: {[username: string]: number};
private contactsIndex: SearchIndex<number>; private contactsIndex: SearchIndex<number>;
private contactsFillPromise: Promise<Set<number>>; private contactsFillPromise: CancellablePromise<Set<number>>;
private contactsList: Set<number>; private contactsList: Set<number>;
private updatedContactsList: boolean; private updatedContactsList: boolean;
@ -138,7 +139,8 @@ export class AppUsersManager {
}); });
if(contactsList.length) { if(contactsList.length) {
this.contactsFillPromise = Promise.resolve(this.contactsList); this.contactsFillPromise = deferredPromise();
this.contactsFillPromise.resolve(this.contactsList);
} }
} }
@ -198,13 +200,19 @@ export class AppUsersManager {
public fillContacts() { public fillContacts() {
if(this.contactsFillPromise && this.updatedContactsList) { if(this.contactsFillPromise && this.updatedContactsList) {
return this.contactsFillPromise; return {
cached: this.contactsFillPromise.isFulfilled,
promise: this.contactsFillPromise
};
} }
this.updatedContactsList = true; this.updatedContactsList = true;
const promise = apiManager.invokeApi('contacts.getContacts').then((result) => { const promise = deferredPromise<Set<number>>();
apiManager.invokeApi('contacts.getContacts').then((result) => {
if(result._ === 'contacts.contacts') { if(result._ === 'contacts.contacts') {
this.contactsList.clear();
this.saveApiUsers(result.users); this.saveApiUsers(result.users);
result.contacts.forEach((contact) => { result.contacts.forEach((contact) => {
@ -212,14 +220,19 @@ export class AppUsersManager {
}); });
this.onContactsModified(); this.onContactsModified();
this.contactsFillPromise = promise;
} }
this.contactsFillPromise = promise; promise.resolve(this.contactsList);
}, () => {
return this.contactsList; this.updatedContactsList = false;
}); });
return this.contactsFillPromise || (this.contactsFillPromise = promise); return {
cached: this.contactsFillPromise?.isFulfilled,
promise: this.contactsFillPromise || (this.contactsFillPromise = promise)
};
} }
public resolveUsername(username: string): Promise<Chat | User> { public resolveUsername(username: string): Promise<Chat | User> {
@ -265,7 +278,7 @@ export class AppUsersManager {
} }
public getContacts(query?: string, includeSaved = false, sortBy: 'name' | 'online' | 'none' = 'name') { public getContacts(query?: string, includeSaved = false, sortBy: 'name' | 'online' | 'none' = 'name') {
return this.fillContacts().then(_contactsList => { return this.fillContacts().promise.then(_contactsList => {
let contactsList = [..._contactsList]; let contactsList = [..._contactsList];
if(query) { if(query) {
const results = this.contactsIndex.search(query); const results = this.contactsIndex.search(query);

View File

@ -599,7 +599,26 @@ export default class DialogsStorage {
return indexStr; return indexStr;
} }
public getDialogs(query = '', offsetIndex?: number, limit = 20, folderId = 0, skipMigrated = false) { public getDialogs(query = '', offsetIndex?: number, limit = 20, folderId = 0, skipMigrated = false): {
cached: boolean;
promise: Promise<{
dialogs: Dialog[];
count: number;
isEnd: boolean;
}>;
} {
if(folderId > 1) {
const fillContactsResult = this.appUsersManager.fillContacts();
if(!fillContactsResult.cached) {
return {
cached: false,
promise: fillContactsResult.promise.then(() => {
return this.getDialogs(query, offsetIndex, limit, folderId, skipMigrated).promise;
})
};
}
}
const realFolderId = folderId > 1 ? 0 : folderId; const realFolderId = folderId > 1 ? 0 : folderId;
let curDialogStorage = this.getFolder(folderId, skipMigrated); let curDialogStorage = this.getFolder(folderId, skipMigrated);
@ -641,37 +660,43 @@ export default class DialogsStorage {
const loadedAll = this.isDialogsLoaded(realFolderId); const loadedAll = this.isDialogsLoaded(realFolderId);
if(query || loadedAll || curDialogStorage.length >= offset + limit) { if(query || loadedAll || curDialogStorage.length >= offset + limit) {
return Promise.resolve({ return {
dialogs: curDialogStorage.slice(offset, offset + limit), cached: true,
count: loadedAll ? curDialogStorage.length : null, promise: Promise.resolve({
isEnd: loadedAll && (offset + limit) >= curDialogStorage.length dialogs: curDialogStorage.slice(offset, offset + limit),
}); count: loadedAll ? curDialogStorage.length : null,
isEnd: loadedAll && (offset + limit) >= curDialogStorage.length
})
};
} }
return this.appMessagesManager.getTopMessages(limit, realFolderId).then(result => { return {
//const curDialogStorage = this[folderId]; cached: false,
if(skipMigrated) { promise: this.appMessagesManager.getTopMessages(limit, realFolderId).then(result => {
curDialogStorage = this.getFolder(folderId, skipMigrated); //const curDialogStorage = this[folderId];
} if(skipMigrated) {
curDialogStorage = this.getFolder(folderId, skipMigrated);
}
offset = 0; offset = 0;
if(offsetIndex > 0) { if(offsetIndex > 0) {
for(let length = curDialogStorage.length; offset < length; ++offset) { for(let length = curDialogStorage.length; offset < length; ++offset) {
if(offsetIndex > curDialogStorage[offset][indexStr]) { if(offsetIndex > curDialogStorage[offset][indexStr]) {
break; break;
}
} }
} }
}
//this.log.warn(offset, offset + limit, curDialogStorage.dialogs.length, this.dialogs.length); //this.log.warn(offset, offset + limit, curDialogStorage.dialogs.length, this.dialogs.length);
return { return {
dialogs: curDialogStorage.slice(offset, offset + limit), dialogs: curDialogStorage.slice(offset, offset + limit),
count: result.count === undefined ? curDialogStorage.length : result.count, count: result.count === undefined ? curDialogStorage.length : result.count,
// isEnd: this.isDialogsLoaded(realFolderId) && (offset + limit) >= curDialogStorage.length // isEnd: this.isDialogsLoaded(realFolderId) && (offset + limit) >= curDialogStorage.length
isEnd: result.isEnd isEnd: result.isEnd
}; };
}); })
};
} }
// only 0 and 1 folders // only 0 and 1 folders

View File

@ -2049,6 +2049,14 @@ $bubble-margin: .25rem;
} }
} }
&.sticker,
&.round,
&.emoji-big {
.message {
right: 0;
}
}
.reply-border, .reply-border,
.quote:before { .quote:before {
background-color: var(--message-out-primary-color); background-color: var(--message-out-primary-color);

View File

@ -307,6 +307,17 @@ ul.chatlist {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
.tgico-play {
position: absolute;
z-index: 1;
color: #fff;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
line-height: 1;
font-size: .625rem;
}
.media-photo { .media-photo {
width: inherit; width: inherit;
height: inherit; height: inherit;