fixes & fixes
This commit is contained in:
parent
633413ab97
commit
5f807a6552
@ -149,7 +149,7 @@ export default class ChatBubbles {
|
||||
private replyFollowHistory: number[] = [];
|
||||
|
||||
private isHeavyAnimationInProgress = false;
|
||||
private scrollingToNewBubble: HTMLElement;
|
||||
private scrollingToBubble: HTMLElement;
|
||||
|
||||
private isFirstLoad = true;
|
||||
private needReflowScroll: boolean;
|
||||
@ -225,7 +225,7 @@ export default class ChatBubbles {
|
||||
this.setBubblePosition(bubble, message, false);
|
||||
//this.log('history_update', this.bubbles[mid], mid, message);
|
||||
|
||||
if(this.scrollingToNewBubble) {
|
||||
if(this.scrollingToBubble) {
|
||||
this.scrollToBubbleEnd();
|
||||
}
|
||||
|
||||
@ -1420,6 +1420,12 @@ export default class ChatBubbles {
|
||||
return;
|
||||
} */
|
||||
|
||||
if(!scrolledDown) {
|
||||
if(this.scrollingToBubble && this.scrollingToBubble === this.getLastBubble()) {
|
||||
scrolledDown = true;
|
||||
}
|
||||
}
|
||||
|
||||
const promise = this.performHistoryResult(mids, false, true);
|
||||
if(scrolledDown) {
|
||||
promise.then(() => {
|
||||
@ -1437,6 +1443,13 @@ export default class ChatBubbles {
|
||||
}
|
||||
}
|
||||
|
||||
public getLastBubble() {
|
||||
const lastDateGroup = this.getLastDateGroup();
|
||||
if(lastDateGroup) {
|
||||
return lastDateGroup.lastElementChild as HTMLElement;
|
||||
}
|
||||
}
|
||||
|
||||
public scrollToBubble(
|
||||
element: HTMLElement,
|
||||
position: ScrollLogicalPosition,
|
||||
@ -1458,21 +1471,17 @@ export default class ChatBubbles {
|
||||
return this.scrollable.scrollIntoViewNew(element, position, 4, undefined, forceDirection, forceDuration);
|
||||
}
|
||||
|
||||
public scrollToBubbleEnd(bubble?: HTMLElement) {
|
||||
if(!bubble) {
|
||||
const lastDateGroup = this.getLastDateGroup();
|
||||
if(lastDateGroup) {
|
||||
bubble = lastDateGroup.lastElementChild as HTMLElement;
|
||||
}
|
||||
}
|
||||
public scrollToBubbleEnd(bubble = this.getLastBubble()) {
|
||||
/* if(DEBUG) {
|
||||
this.log('scrollToNewLastBubble: will scroll into view:', bubble);
|
||||
} */
|
||||
|
||||
if(bubble) {
|
||||
this.scrollingToNewBubble = bubble;
|
||||
this.scrollingToBubble = bubble;
|
||||
const middleware = this.getMiddleware();
|
||||
this.scrollToBubble(bubble, 'end').then(() => {
|
||||
this.scrollingToNewBubble = null;
|
||||
if(!middleware()) return;
|
||||
this.scrollingToBubble = undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1517,7 +1526,7 @@ export default class ChatBubbles {
|
||||
const date = new Date(message.date * 1000);
|
||||
date.setHours(0, 0, 0);
|
||||
const dateTimestamp = date.getTime();
|
||||
if(!(dateTimestamp in this.dateMessages)) {
|
||||
if(!this.dateMessages[dateTimestamp]) {
|
||||
let dateElement: HTMLElement;
|
||||
|
||||
const today = new Date();
|
||||
@ -1549,8 +1558,8 @@ export default class ChatBubbles {
|
||||
}
|
||||
}
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.className = 'bubble service is-date';
|
||||
const bubble = document.createElement('div');
|
||||
bubble.className = 'bubble service is-date';
|
||||
const bubbleContent = document.createElement('div');
|
||||
bubbleContent.classList.add('bubble-content');
|
||||
const serviceMsg = document.createElement('div');
|
||||
@ -1559,36 +1568,38 @@ export default class ChatBubbles {
|
||||
serviceMsg.append(dateElement);
|
||||
|
||||
bubbleContent.append(serviceMsg);
|
||||
div.append(bubbleContent);
|
||||
bubble.append(bubbleContent);
|
||||
////////this.log('need to render date message', dateTimestamp, str);
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.className = 'bubbles-date-group';
|
||||
container.append(bubble);
|
||||
|
||||
const haveTimestamps = getObjectKeysAndSort(this.dateMessages, 'asc');
|
||||
let i = 0;
|
||||
for(; i < haveTimestamps.length; ++i) {
|
||||
const t = haveTimestamps[i];
|
||||
if(dateTimestamp < t) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.dateMessages[dateTimestamp] = {
|
||||
div,
|
||||
div: bubble,
|
||||
container,
|
||||
firstTimestamp: date.getTime()
|
||||
};
|
||||
|
||||
container.append(div);
|
||||
const haveTimestamps = getObjectKeysAndSort(this.dateMessages, 'asc');
|
||||
let i = 0, length = haveTimestamps.length, insertBefore: HTMLElement; // there can be 'first bubble' (e.g. bot description) so can't insert by index
|
||||
for(; i < haveTimestamps.length; ++i) {
|
||||
const t = haveTimestamps[i];
|
||||
insertBefore = this.dateMessages[t].container;
|
||||
if(dateTimestamp < t) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
positionElementByIndex(container, this.chatInner, i);
|
||||
if(i === length && insertBefore) {
|
||||
insertBefore = insertBefore.nextElementSibling as HTMLElement;
|
||||
}
|
||||
|
||||
/* if(reverse) {
|
||||
this.chatInner.prepend(container);
|
||||
} else {
|
||||
if(!insertBefore) {
|
||||
this.chatInner.append(container);
|
||||
} */
|
||||
} else {
|
||||
this.chatInner.insertBefore(container, insertBefore);
|
||||
}
|
||||
|
||||
if(this.stickyIntersector) {
|
||||
this.stickyIntersector.observeStickyHeaderChanges(container);
|
||||
@ -1676,6 +1687,8 @@ export default class ChatBubbles {
|
||||
this.onAnimateLadder = undefined;
|
||||
this.resolveLadderAnimation = undefined;
|
||||
this.emptyPlaceholderMid = undefined;
|
||||
|
||||
this.scrollingToBubble = undefined;
|
||||
////console.timeEnd('appImManager cleanup');
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ export default class AppArchivedTab extends SliderSuperTab {
|
||||
this.container.id = 'chats-archived-container';
|
||||
this.setTitle('ArchivedChats');
|
||||
|
||||
if(!appDialogsManager.chatLists[AppArchivedTab.filterId]) {
|
||||
if(!appDialogsManager.sortedLists[AppArchivedTab.filterId]) {
|
||||
const chatList = appDialogsManager.createChatList();
|
||||
appDialogsManager.generateScrollable(chatList, AppArchivedTab.filterId).container.append(chatList);
|
||||
appDialogsManager.setListClickListener(chatList, null, true);
|
||||
@ -40,7 +40,7 @@ export default class AppArchivedTab extends SliderSuperTab {
|
||||
|
||||
// вообще, так делать нельзя, но нет времени чтобы переделать главный чатлист на слайд...
|
||||
onOpenAfterTimeout() {
|
||||
appDialogsManager.chatLists[this.wasFilterId].innerHTML = '';
|
||||
appDialogsManager.sortedLists[this.wasFilterId].clear();
|
||||
}
|
||||
|
||||
onClose() {
|
||||
@ -49,7 +49,7 @@ export default class AppArchivedTab extends SliderSuperTab {
|
||||
}
|
||||
|
||||
onCloseAfterTimeout() {
|
||||
appDialogsManager.chatLists[AppArchivedTab.filterId].innerHTML = '';
|
||||
appDialogsManager.sortedLists[AppArchivedTab.filterId].clear();
|
||||
return super.onCloseAfterTimeout();
|
||||
}
|
||||
}
|
||||
|
@ -8,21 +8,19 @@ import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
|
||||
import appDialogsManager, { DialogDom } from "../lib/appManagers/appDialogsManager";
|
||||
import { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck";
|
||||
import appUsersManager from "../lib/appManagers/appUsersManager";
|
||||
import { insertInDescendSortedArray } from "../helpers/array";
|
||||
import isInDOM from "../helpers/dom/isInDOM";
|
||||
import positionElementByIndex from "../helpers/dom/positionElementByIndex";
|
||||
import replaceContent from "../helpers/dom/replaceContent";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
import { fastRaf } from "../helpers/schedulers";
|
||||
import SortedList, { SortedElementBase } from "../helpers/sortedList";
|
||||
|
||||
type SortedUser = {
|
||||
peerId: number,
|
||||
status: number,
|
||||
interface SortedUser extends SortedElementBase {
|
||||
dom: DialogDom
|
||||
};
|
||||
export default class SortedUserList {
|
||||
}
|
||||
|
||||
export default class SortedUserList extends SortedList<SortedUser> {
|
||||
protected static SORT_INTERVAL = 30e3;
|
||||
protected users: Map<number, SortedUser>;
|
||||
protected sorted: Array<SortedUser>;
|
||||
public list: HTMLUListElement;
|
||||
|
||||
protected lazyLoadQueue: LazyLoadQueueIntersector;
|
||||
@ -35,17 +33,53 @@ export default class SortedUserList {
|
||||
rippleEnabled: SortedUserList['rippleEnabled'],
|
||||
new: boolean
|
||||
}> = {}) {
|
||||
super({
|
||||
getIndex: (id) => appUsersManager.getUserStatusForSort(id),
|
||||
onDelete: (element) => element.dom.listEl.remove(),
|
||||
onUpdate: (element) => {
|
||||
const status = appUsersManager.getUserStatusString(element.id);
|
||||
replaceContent(element.dom.lastMessageSpan, status);
|
||||
},
|
||||
onSort: (element, idx) => positionElementByIndex(element.dom.listEl, this.list, idx),
|
||||
onElementCreate: (base) => {
|
||||
const {dom} = appDialogsManager.addDialogNew({
|
||||
dialog: base.id,
|
||||
container: false,
|
||||
drawStatus: false,
|
||||
avatarSize: this.avatarSize,
|
||||
autonomous: true,
|
||||
meAsSaved: false,
|
||||
rippleEnabled: this.rippleEnabled,
|
||||
lazyLoadQueue: this.lazyLoadQueue
|
||||
});
|
||||
|
||||
(base as SortedUser).dom = dom;
|
||||
return base as SortedUser;
|
||||
},
|
||||
updateElementWith: fastRaf,
|
||||
updateListWith: async(callback) => {
|
||||
if(!isInDOM(this.list)) {
|
||||
return callback(false);
|
||||
}
|
||||
|
||||
await getHeavyAnimationPromise();
|
||||
|
||||
if(!isInDOM(this.list)) {
|
||||
return callback(false);
|
||||
}
|
||||
|
||||
callback(true);
|
||||
}
|
||||
});
|
||||
|
||||
safeAssign(this, options);
|
||||
|
||||
this.list = appDialogsManager.createChatList({new: options.new});
|
||||
|
||||
this.users = new Map();
|
||||
this.sorted = [];
|
||||
|
||||
let timeout: number;
|
||||
const doTimeout = () => {
|
||||
timeout = window.setTimeout(() => {
|
||||
this.updateList().then((good) => {
|
||||
this.updateList((good) => {
|
||||
if(good) {
|
||||
doTimeout();
|
||||
}
|
||||
@ -55,83 +89,4 @@ export default class SortedUserList {
|
||||
|
||||
doTimeout();
|
||||
}
|
||||
|
||||
public async updateList() {
|
||||
if(!isInDOM(this.list)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await getHeavyAnimationPromise();
|
||||
|
||||
if(!isInDOM(this.list)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.users.forEach(user => {
|
||||
this.update(user.peerId, true);
|
||||
});
|
||||
|
||||
this.sorted.forEach((sortedUser, idx) => {
|
||||
positionElementByIndex(sortedUser.dom.listEl, this.list, idx);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public has(peerId: number) {
|
||||
return this.users.has(peerId);
|
||||
}
|
||||
|
||||
public add(peerId: number) {
|
||||
if(this.has(peerId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {dom} = appDialogsManager.addDialogNew({
|
||||
dialog: peerId,
|
||||
container: false,
|
||||
drawStatus: false,
|
||||
avatarSize: this.avatarSize,
|
||||
autonomous: true,
|
||||
meAsSaved: false,
|
||||
rippleEnabled: this.rippleEnabled,
|
||||
lazyLoadQueue: this.lazyLoadQueue
|
||||
});
|
||||
|
||||
const sortedUser: SortedUser = {
|
||||
peerId,
|
||||
status: appUsersManager.getUserStatusForSort(peerId),
|
||||
dom
|
||||
};
|
||||
|
||||
this.users.set(peerId, sortedUser);
|
||||
this.update(peerId);
|
||||
}
|
||||
|
||||
public delete(peerId: number) {
|
||||
const user = this.users.get(peerId);
|
||||
if(!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
user.dom.listEl.remove();
|
||||
this.users.delete(peerId);
|
||||
|
||||
const idx = this.sorted.indexOf(user);
|
||||
if(idx !== -1) {
|
||||
this.sorted.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public update(peerId: number, batch = false) {
|
||||
const sortedUser = this.users.get(peerId);
|
||||
sortedUser.status = appUsersManager.getUserStatusForSort(peerId);
|
||||
const status = appUsersManager.getUserStatusString(peerId);
|
||||
replaceContent(sortedUser.dom.lastMessageSpan, status);
|
||||
|
||||
const idx = insertInDescendSortedArray(this.sorted, sortedUser, 'status');
|
||||
if(!batch) {
|
||||
positionElementByIndex(sortedUser.dom.listEl, this.list, idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,6 @@ import rootScope from "../lib/rootScope";
|
||||
import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise";
|
||||
import { dispatchHeavyAnimationEvent } from "../hooks/useHeavyAnimationCheck";
|
||||
import whichChild from "../helpers/dom/whichChild";
|
||||
import findUpClassName from "../helpers/dom/findUpClassName";
|
||||
import { isSafari } from "../helpers/userAgent";
|
||||
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
||||
|
||||
function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) {
|
||||
@ -33,13 +31,13 @@ function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, t
|
||||
|
||||
function slideTabs(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) {
|
||||
// Jolly Cobra's // Workaround for scrollable content flickering during animation.
|
||||
const scrollableContainer = findUpClassName(tabContent, 'scrollable-y');
|
||||
if(scrollableContainer && scrollableContainer.style.overflowY !== 'hidden') {
|
||||
// const scrollBarWidth = scrollableContainer.offsetWidth - scrollableContainer.clientWidth;
|
||||
scrollableContainer.style.overflowY = 'hidden';
|
||||
// scrollableContainer.style.paddingRight = `${scrollBarWidth}px`;
|
||||
// this.container.classList.add('sliding');
|
||||
}
|
||||
// const scrollableContainer = findUpClassName(tabContent, 'scrollable-y');
|
||||
// if(scrollableContainer && scrollableContainer.style.overflowY !== 'hidden') {
|
||||
// // const scrollBarWidth = scrollableContainer.offsetWidth - scrollableContainer.clientWidth;
|
||||
// scrollableContainer.style.overflowY = 'hidden';
|
||||
// // scrollableContainer.style.paddingRight = `${scrollBarWidth}px`;
|
||||
// // this.container.classList.add('sliding');
|
||||
// }
|
||||
|
||||
//window.requestAnimationFrame(() => {
|
||||
const width = prevTabContent.getBoundingClientRect().width;
|
||||
@ -62,22 +60,22 @@ function slideTabs(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight
|
||||
return () => {
|
||||
prevTabContent.style.transform = '';
|
||||
|
||||
if(scrollableContainer) {
|
||||
// Jolly Cobra's // Workaround for scrollable content flickering during animation.
|
||||
if(isSafari) { // ! safari doesn't respect sticky header, so it flicks when overflow is changing
|
||||
scrollableContainer.style.display = 'none';
|
||||
}
|
||||
// if(scrollableContainer) {
|
||||
// // Jolly Cobra's // Workaround for scrollable content flickering during animation.
|
||||
// if(isSafari) { // ! safari doesn't respect sticky header, so it flicks when overflow is changing
|
||||
// scrollableContainer.style.display = 'none';
|
||||
// }
|
||||
|
||||
scrollableContainer.style.overflowY = '';
|
||||
// scrollableContainer.style.overflowY = '';
|
||||
|
||||
if(isSafari) {
|
||||
void scrollableContainer.offsetLeft; // reflow
|
||||
scrollableContainer.style.display = '';
|
||||
}
|
||||
// if(isSafari) {
|
||||
// void scrollableContainer.offsetLeft; // reflow
|
||||
// scrollableContainer.style.display = '';
|
||||
// }
|
||||
|
||||
// scrollableContainer.style.paddingRight = '0';
|
||||
// this.container.classList.remove('sliding');
|
||||
}
|
||||
// // scrollableContainer.style.paddingRight = '0';
|
||||
// // this.container.classList.remove('sliding');
|
||||
// }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1005,6 +1005,59 @@ export function renderImageWithFadeIn(container: HTMLElement,
|
||||
});
|
||||
}
|
||||
|
||||
// export function renderImageWithFadeIn(container: HTMLElement,
|
||||
// image: HTMLImageElement,
|
||||
// url: string,
|
||||
// needFadeIn: boolean,
|
||||
// aspecter = container,
|
||||
// thumbImage?: HTMLImageElement
|
||||
// ) {
|
||||
// if(needFadeIn) {
|
||||
// // image.classList.add('fade-in-new', 'not-yet');
|
||||
// image.classList.add('fade-in');
|
||||
// }
|
||||
|
||||
// return new Promise<void>((resolve) => {
|
||||
// /* if(photo._ === 'document') {
|
||||
// console.error('wrapPhoto: will render document', photo, size, cacheContext);
|
||||
// return resolve();
|
||||
// } */
|
||||
|
||||
// renderImageFromUrl(image, url, () => {
|
||||
// sequentialDom.mutateElement(container, () => {
|
||||
// aspecter.append(image);
|
||||
// // (needFadeIn ? getHeavyAnimationPromise() : Promise.resolve()).then(() => {
|
||||
|
||||
// // fastRaf(() => {
|
||||
// resolve();
|
||||
// // });
|
||||
|
||||
// if(needFadeIn) {
|
||||
// fastRaf(() => {
|
||||
// /* if(!image.isConnected) {
|
||||
// alert('aaaa');
|
||||
// } */
|
||||
// // fastRaf(() => {
|
||||
// image.classList.remove('not-yet');
|
||||
// // });
|
||||
// });
|
||||
|
||||
// image.addEventListener('transitionend', () => {
|
||||
// sequentialDom.mutate(() => {
|
||||
// image.classList.remove('fade-in-new');
|
||||
|
||||
// if(thumbImage) {
|
||||
// thumbImage.remove();
|
||||
// }
|
||||
// });
|
||||
// }, {once: true});
|
||||
// }
|
||||
// // });
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop, loadPromises, needFadeIn}: {
|
||||
doc: MyDocument,
|
||||
div: HTMLElement,
|
||||
|
@ -38,14 +38,22 @@ export function forEachReverse<T>(array: Array<T>, callback: (value: T, index?:
|
||||
};
|
||||
|
||||
export function insertInDescendSortedArray<T extends {[smth in K]?: number}, K extends keyof T>(array: Array<T>, element: T, property: K, pos?: number) {
|
||||
const sortProperty: number = element[property];
|
||||
|
||||
if(pos === undefined) {
|
||||
pos = array.indexOf(element);
|
||||
if(pos !== -1) {
|
||||
const prev = array[pos - 1];
|
||||
const next = array[pos + 1];
|
||||
if((!prev || prev[property] >= sortProperty) && (!next || next[property] <= sortProperty)) {
|
||||
// console.warn('same pos', pos, sortProperty, prev, next);
|
||||
return pos;
|
||||
}
|
||||
|
||||
array.splice(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const sortProperty: number = element[property];
|
||||
const len = array.length;
|
||||
if(!len || sortProperty <= array[len - 1][property]) {
|
||||
return array.push(element) - 1;
|
||||
|
@ -17,7 +17,9 @@ export default function positionElementByIndex(element: HTMLElement, container:
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
if(container.childElementCount > pos) {
|
||||
if(!pos) {
|
||||
container.prepend(element);
|
||||
} else if(container.childElementCount > pos) {
|
||||
container.insertBefore(element, container.children[pos]);
|
||||
} else {
|
||||
container.append(element);
|
||||
|
@ -63,6 +63,27 @@ export function fastRaf(callback: NoneToVoidFunction) {
|
||||
}
|
||||
}
|
||||
|
||||
let fastRafConventionalCallbacks: NoneToVoidFunction[] | undefined, processing = false;
|
||||
export function fastRafConventional(callback: NoneToVoidFunction) {
|
||||
if(!fastRafConventionalCallbacks) {
|
||||
fastRafConventionalCallbacks = [callback];
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
processing = true;
|
||||
for(let i = 0; i < fastRafConventionalCallbacks.length; ++i) {
|
||||
fastRafConventionalCallbacks[i]();
|
||||
}
|
||||
|
||||
fastRafConventionalCallbacks = undefined;
|
||||
processing = false;
|
||||
});
|
||||
} else if(processing) {
|
||||
callback();
|
||||
} else {
|
||||
fastRafConventionalCallbacks.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
let rafPromise: Promise<DOMHighResTimeStamp>;
|
||||
export function fastRafPromise() {
|
||||
if(rafPromise) return rafPromise;
|
||||
|
163
src/helpers/sortedList.ts
Normal file
163
src/helpers/sortedList.ts
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import { insertInDescendSortedArray } from "./array";
|
||||
import { getMiddleware } from "./middleware";
|
||||
import { safeAssign } from "./object";
|
||||
|
||||
export type SortedElementBase = {
|
||||
id: number,
|
||||
index: number
|
||||
};
|
||||
|
||||
export default class SortedList<SortedElement extends SortedElementBase> {
|
||||
protected elements: Map<number, SortedElement>;
|
||||
protected sorted: Array<SortedElement>;
|
||||
|
||||
protected getIndex: (id: number) => number;
|
||||
protected onDelete: (element: SortedElement) => void;
|
||||
protected onUpdate: (element: SortedElement) => void;
|
||||
protected onSort: (element: SortedElement, idx: number) => void;
|
||||
protected onElementCreate: (base: SortedElementBase, batch: boolean) => SortedElement;
|
||||
|
||||
protected updateElementWith = (callback: () => void) => callback();
|
||||
protected updateListWith = (callback: (canUpdate: boolean | undefined) => void) => callback(true);
|
||||
|
||||
protected middleware = getMiddleware();
|
||||
|
||||
constructor(options: {
|
||||
getIndex: SortedList<SortedElement>['getIndex'],
|
||||
onDelete?: SortedList<SortedElement>['onDelete'],
|
||||
onUpdate?: SortedList<SortedElement>['onUpdate'],
|
||||
onSort?: SortedList<SortedElement>['onSort'],
|
||||
onElementCreate: SortedList<SortedElement>['onElementCreate'],
|
||||
|
||||
updateElementWith?: SortedList<SortedElement>['updateElementWith'],
|
||||
updateListWith?: SortedList<SortedElement>['updateListWith']
|
||||
}) {
|
||||
safeAssign(this, options);
|
||||
|
||||
this.elements = new Map();
|
||||
this.sorted = [];
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.middleware.clean();
|
||||
this.elements.clear();
|
||||
this.sorted.length = 0;
|
||||
}
|
||||
|
||||
protected _updateList() {
|
||||
this.elements.forEach(element => {
|
||||
this.update(element.id, true);
|
||||
});
|
||||
|
||||
if(this.onSort) {
|
||||
this.sorted.forEach((element, idx) => {
|
||||
this.onSort(element, idx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public updateList(callback: (updated: boolean) => void) {
|
||||
const middleware = this.middleware.get();
|
||||
this.updateListWith((canUpdate) => {
|
||||
if(!middleware() || (canUpdate !== undefined && !canUpdate)) {
|
||||
return callback(false);
|
||||
}
|
||||
|
||||
this._updateList();
|
||||
|
||||
callback(true);
|
||||
});
|
||||
}
|
||||
|
||||
public has(id: number) {
|
||||
return this.elements.has(id);
|
||||
}
|
||||
|
||||
public get(id: number) {
|
||||
return this.elements.get(id);
|
||||
}
|
||||
|
||||
public getAll() {
|
||||
return this.elements;
|
||||
}
|
||||
|
||||
public add(id: number, batch = false, updateElementWith?: SortedList<SortedElement>['updateElementWith'], updateBatch = batch) {
|
||||
let element = this.get(id);
|
||||
if(element) {
|
||||
return element;
|
||||
}
|
||||
|
||||
const base: SortedElementBase = {
|
||||
id,
|
||||
index: 0
|
||||
};
|
||||
|
||||
element = this.onElementCreate(base, batch);
|
||||
this.elements.set(id, element);
|
||||
this.update(id, updateBatch, element, updateElementWith);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
public delete(id: number, noScheduler?: boolean) {
|
||||
const element = this.elements.get(id);
|
||||
if(!element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.elements.delete(id);
|
||||
|
||||
const idx = this.sorted.indexOf(element);
|
||||
if(idx !== -1) {
|
||||
this.sorted.splice(idx, 1);
|
||||
}
|
||||
|
||||
if(this.onDelete) {
|
||||
if(noScheduler) {
|
||||
this.onDelete(element);
|
||||
} else {
|
||||
const middleware = this.middleware.get();
|
||||
this.updateElementWith(() => {
|
||||
if(!middleware()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onDelete(element);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public update(id: number, batch = false, element = this.get(id), updateElementWith?: SortedList<SortedElement>['updateElementWith']) {
|
||||
if(!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
element.index = this.getIndex(id);
|
||||
this.onUpdate && this.onUpdate(element);
|
||||
|
||||
const idx = insertInDescendSortedArray(this.sorted, element, 'index');
|
||||
if(!batch && this.onSort) {
|
||||
const middleware = this.middleware.get();
|
||||
(updateElementWith || this.updateElementWith)(() => {
|
||||
if(!middleware()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// * в случае пересортировки этого же элемента во время ожидания вызовется вторая такая же. нужно соблюдать последовательность событий
|
||||
this.onSort(element, idx);
|
||||
/* if(this.get(id) === element) {
|
||||
this.onSort(element, this.sorted.indexOf(element));
|
||||
} */
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -43,7 +43,7 @@ import replaceContent from "../../helpers/dom/replaceContent";
|
||||
import ConnectionStatusComponent from "../../components/connectionStatus";
|
||||
import appChatsManager from "./appChatsManager";
|
||||
import { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl";
|
||||
import { fastRafPromise } from "../../helpers/schedulers";
|
||||
import { fastRaf, fastRafConventional, fastRafPromise } from "../../helpers/schedulers";
|
||||
import SortedUserList from "../../components/sortedUserList";
|
||||
import { isTouchSupported } from "../../helpers/touchSupport";
|
||||
import handleTabSwipe from "../../helpers/dom/handleTabSwipe";
|
||||
@ -52,6 +52,8 @@ import isInDOM from "../../helpers/dom/isInDOM";
|
||||
import appPhotosManager, { MyPhoto } from "./appPhotosManager";
|
||||
import { MyDocument } from "./appDocsManager";
|
||||
import { setSendingStatus } from "../../components/sendingStatus";
|
||||
import SortedList, { SortedElementBase } from "../../helpers/sortedList";
|
||||
import debounce from "../../helpers/schedulers/debounce";
|
||||
|
||||
export type DialogDom = {
|
||||
avatarEl: AvatarElement,
|
||||
@ -68,14 +70,56 @@ export type DialogDom = {
|
||||
subtitleEl: HTMLElement
|
||||
};
|
||||
|
||||
interface SortedDialog extends SortedElementBase {
|
||||
dom: DialogDom,
|
||||
loadPromises?: Promise<any>[]
|
||||
}
|
||||
|
||||
class SortedDialogList extends SortedList<SortedDialog> {
|
||||
constructor(public list: HTMLUListElement, public indexKey: ReturnType<DialogsStorage['getDialogIndexKey']>) {
|
||||
super({
|
||||
getIndex: (id) => appMessagesManager.getDialogOnly(id)[this.indexKey],
|
||||
onDelete: (element) => {
|
||||
element.dom.listEl.remove();
|
||||
appDialogsManager.onListLengthChange();
|
||||
},
|
||||
onSort: (element, idx) => {
|
||||
const willChangeLength = element.dom.listEl.parentElement !== this.list;
|
||||
positionElementByIndex(element.dom.listEl, this.list, idx);
|
||||
|
||||
if(willChangeLength) {
|
||||
appDialogsManager.onListLengthChange();
|
||||
}
|
||||
},
|
||||
onElementCreate: (base, batch) => {
|
||||
const loadPromises: Promise<any>[] = batch ? [] : undefined;
|
||||
|
||||
const {dom} = appDialogsManager.addListDialog({dialog: base.id, loadPromises, isBatch: batch});
|
||||
(base as SortedDialog).dom = dom;
|
||||
|
||||
if(loadPromises?.length) {
|
||||
(base as SortedDialog).loadPromises = loadPromises;
|
||||
Promise.all(loadPromises).finally(() => {
|
||||
delete (base as SortedDialog).loadPromises;
|
||||
});
|
||||
}
|
||||
|
||||
return base as SortedDialog;
|
||||
},
|
||||
updateElementWith: fastRafConventional
|
||||
});
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.list.innerHTML = '';
|
||||
super.clear();
|
||||
}
|
||||
}
|
||||
|
||||
//const testScroll = false;
|
||||
//let testTopSlice = 1;
|
||||
|
||||
export class AppDialogsManager {
|
||||
private chatList: HTMLUListElement;
|
||||
|
||||
private doms: {[peerId: number]: DialogDom} = {};
|
||||
|
||||
private chatsContainer = document.getElementById('chatlist-container') as HTMLDivElement;
|
||||
private chatsPreloader: HTMLElement;
|
||||
|
||||
@ -87,7 +131,8 @@ export class AppDialogsManager {
|
||||
|
||||
private contextMenu = new DialogsContextMenu();
|
||||
|
||||
public chatLists: {[filterId: number]: HTMLUListElement} = {};
|
||||
public sortedList: SortedDialogList;
|
||||
public sortedLists: {[filterId: number]: SortedDialogList} = {};
|
||||
public scrollables: {[filterId: number]: Scrollable} = {};
|
||||
public filterId: number;
|
||||
private folders: {[k in 'menu' | 'container' | 'menuScrollContainer']: HTMLElement} = {
|
||||
@ -111,7 +156,6 @@ export class AppDialogsManager {
|
||||
//private topOffsetIndex = 0;
|
||||
|
||||
private sliceTimeout: number;
|
||||
private reorderDialogsTimeout: number;
|
||||
|
||||
private lastActiveElements: Set<HTMLElement> = new Set();
|
||||
|
||||
@ -122,6 +166,8 @@ export class AppDialogsManager {
|
||||
|
||||
private indexKey: ReturnType<DialogsStorage['getDialogIndexKey']>;
|
||||
|
||||
public onListLengthChange: () => Promise<void>;
|
||||
|
||||
constructor() {
|
||||
this.chatsPreloader = putPreloader(null, true);
|
||||
|
||||
@ -170,7 +216,7 @@ export class AppDialogsManager {
|
||||
orderIndex: 0
|
||||
});
|
||||
|
||||
this.chatList = this.chatLists[this.filterId];
|
||||
this.sortedList = this.sortedLists[this.filterId];
|
||||
this.scroll = this.scrollables[this.filterId];
|
||||
|
||||
/* if(testScroll) {
|
||||
@ -212,7 +258,7 @@ export class AppDialogsManager {
|
||||
rootScope.addEventListener('dialog_flush', ({peerId}) => {
|
||||
const dialog = appMessagesManager.getDialogOnly(peerId);
|
||||
if(dialog) {
|
||||
this.setLastMessage(dialog);
|
||||
this.setLastMessage(dialog, undefined, undefined, undefined, undefined, undefined, true);
|
||||
this.validateDialogForFilter(dialog);
|
||||
this.setFiltersUnreadCount();
|
||||
}
|
||||
@ -313,7 +359,7 @@ export class AppDialogsManager {
|
||||
elements.container.remove();
|
||||
elements.menu.remove();
|
||||
|
||||
delete this.chatLists[filter.id];
|
||||
delete this.sortedLists[filter.id];
|
||||
delete this.scrollables[filter.id];
|
||||
delete this.filtersRendered[filter.id];
|
||||
|
||||
@ -328,6 +374,9 @@ export class AppDialogsManager {
|
||||
const filter = appMessagesManager.filtersStorage.getFilter(filterId);
|
||||
const renderedFilter = this.filtersRendered[filterId];
|
||||
|
||||
const sortedList = this.sortedLists[filterId];
|
||||
sortedList.indexKey = appMessagesManager.dialogsStorage.getDialogIndexKey(filterId);
|
||||
|
||||
positionElementByIndex(renderedFilter.menu, containerToAppend, filter.orderIndex);
|
||||
positionElementByIndex(renderedFilter.container, this.folders.container, filter.orderIndex);
|
||||
});
|
||||
@ -393,13 +442,13 @@ export class AppDialogsManager {
|
||||
|
||||
if(this.filterId === id) return;
|
||||
|
||||
this.chatLists[id].innerHTML = '';
|
||||
this.sortedLists[id].clear();
|
||||
this.setFilterId(id);
|
||||
this.onTabChange();
|
||||
}, () => {
|
||||
for(const folderId in this.chatLists) {
|
||||
for(const folderId in this.sortedLists) {
|
||||
if(+folderId !== this.filterId) {
|
||||
this.chatLists[folderId].innerHTML = '';
|
||||
this.sortedLists[folderId].clear();
|
||||
}
|
||||
}
|
||||
}, undefined, foldersScrollable);
|
||||
@ -436,6 +485,12 @@ export class AppDialogsManager {
|
||||
setTimeout(() => {
|
||||
lottieLoader.loadLottieWorkers();
|
||||
}, 200);
|
||||
|
||||
this.onListLengthChange = debounce(this._onListLengthChange, 100, false, true);
|
||||
}
|
||||
|
||||
public get chatList() {
|
||||
return this.sortedList.list;
|
||||
}
|
||||
|
||||
public setFilterId(filterId: number) {
|
||||
@ -484,7 +539,7 @@ export class AppDialogsManager {
|
||||
}
|
||||
|
||||
private isDialogMustBeInViewport(dialog: Dialog) {
|
||||
if(dialog.migratedTo !== undefined) return false;
|
||||
if(dialog.migratedTo !== undefined || !this.testDialogForFilter(dialog)) return false;
|
||||
//return true;
|
||||
const topOffset = this.getOffsetIndex('top');
|
||||
const bottomOffset = this.getOffsetIndex('bottom');
|
||||
@ -498,69 +553,31 @@ export class AppDialogsManager {
|
||||
}
|
||||
|
||||
private deleteDialog(peerId: number) {
|
||||
const dom = this.getDialogDom(peerId);
|
||||
if(dom) {
|
||||
dom.listEl.remove();
|
||||
delete this.doms[peerId];
|
||||
|
||||
this.onListLengthChange();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
this.sortedList.delete(peerId);
|
||||
}
|
||||
|
||||
private updateDialog(dialog: Dialog) {
|
||||
if(!dialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.isDialogMustBeInViewport(dialog)) {
|
||||
if(!this.doms.hasOwnProperty(dialog.peerId)) {
|
||||
const ret = this.addListDialog({dialog});
|
||||
if(ret) {
|
||||
const idx = appMessagesManager.getDialogByPeerId(dialog.peerId)[1];
|
||||
positionElementByIndex(ret.dom.listEl, this.chatList, idx);
|
||||
this.onListLengthChange();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.sortedList.add(dialog.peerId);
|
||||
} else {
|
||||
this.deleteDialog(dialog.peerId);
|
||||
return;
|
||||
}
|
||||
|
||||
/* const topOffset = this.getOffset('top');
|
||||
if(topOffset.index && dialog.index > topOffset.index) {
|
||||
const dom = this.getDialogDom(dialog.peerId);
|
||||
if(dom) {
|
||||
dom.listEl.remove();
|
||||
delete this.doms[dialog.peerId];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.doms.hasOwnProperty(dialog.peerId)) {
|
||||
this.addDialogNew({dialog});
|
||||
} */
|
||||
|
||||
if(this.getDialogDom(dialog.peerId)) {
|
||||
this.setLastMessage(dialog);
|
||||
this.reorderDialogs();
|
||||
const dom = this.getDialogDom(dialog.peerId);
|
||||
if(dom) {
|
||||
this.setLastMessage(dialog, undefined, dom, undefined, undefined, undefined, true);
|
||||
this.sortedList.update(dialog.peerId);
|
||||
}
|
||||
}
|
||||
|
||||
public onTabChange = () => {
|
||||
this.doms = {};
|
||||
this.scroll = this.scrollables[this.filterId];
|
||||
this.scroll.loadedAll.top = true;
|
||||
this.scroll.loadedAll.bottom = false;
|
||||
this.offsets.top = this.offsets.bottom = 0;
|
||||
this.loadDialogsPromise = undefined;
|
||||
this.chatList = this.chatLists[this.filterId];
|
||||
this.sortedList = this.sortedLists[this.filterId];
|
||||
this.onChatsScroll();
|
||||
};
|
||||
|
||||
@ -603,37 +620,37 @@ export class AppDialogsManager {
|
||||
* Удалит неподходящие чаты из списка, но не добавит их(!)
|
||||
*/
|
||||
private validateListForFilter() {
|
||||
// !WARNING, возможно это было зачем-то, но комментарий исправил архивирование
|
||||
//if(this.filterId === 0) return;
|
||||
|
||||
const folder = appMessagesManager.dialogsStorage.getFolder(this.filterId);
|
||||
for(const _peerId in this.doms) {
|
||||
const peerId = +_peerId;
|
||||
|
||||
// если больше не подходит по фильтру, удаляем
|
||||
if(folder.findIndex((dialog) => dialog.peerId === peerId) === -1) {
|
||||
this.deleteDialog(peerId);
|
||||
const filter = appMessagesManager.filtersStorage.getFilter(this.filterId);
|
||||
this.sortedList.getAll().forEach((element) => {
|
||||
const dialog = appMessagesManager.getDialogOnly(element.id);
|
||||
if(!this.testDialogForFilter(dialog, filter || null)) {
|
||||
this.deleteDialog(element.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалит неподходящие чат из списка, но не добавит его(!)
|
||||
* Удалит неподходящий чат из списка, но не добавит его(!)
|
||||
*/
|
||||
private validateDialogForFilter(dialog: Dialog, filter?: MyDialogFilter) {
|
||||
if(this.filterId <= 1 || !this.doms[dialog.peerId]) {
|
||||
if(!this.getDialogDom(dialog.peerId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!filter) {
|
||||
filter = appMessagesManager.filtersStorage.getFilter(this.filterId);
|
||||
}
|
||||
|
||||
if(!appMessagesManager.filtersStorage.testDialogForFilter(dialog, filter)) {
|
||||
if(!this.testDialogForFilter(dialog, filter)) {
|
||||
this.deleteDialog(dialog.peerId);
|
||||
}
|
||||
}
|
||||
|
||||
public testDialogForFilter(dialog: Dialog, filter = appMessagesManager.filtersStorage.getFilter(this.filterId)) {
|
||||
if((filter && !appMessagesManager.filtersStorage.testDialogForFilter(dialog, filter)) ||
|
||||
(!filter && this.filterId !== dialog.folder_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public generateScrollable(list: HTMLUListElement, filterId: number) {
|
||||
const scrollable = new Scrollable(null, 'CL', 500);
|
||||
scrollable.container.addEventListener('scroll', this.onChatsRegularScroll);
|
||||
@ -642,8 +659,13 @@ export class AppDialogsManager {
|
||||
scrollable.onScrolledBottom = this.onChatsScroll;
|
||||
scrollable.setVirtualContainer(list);
|
||||
|
||||
this.chatLists[filterId] = list;
|
||||
const sortedDialogList = new SortedDialogList(list, appMessagesManager.dialogsStorage ? appMessagesManager.dialogsStorage.getDialogIndexKey(filterId) : 'index');
|
||||
|
||||
this.scrollables[filterId] = scrollable;
|
||||
this.sortedLists[filterId] = sortedDialogList;
|
||||
|
||||
// list.classList.add('hide');
|
||||
// scrollable.container.style.backgroundColor = '#' + (Math.random() * (16 ** 6 - 1) | 0).toString(16);
|
||||
|
||||
return scrollable;
|
||||
}
|
||||
@ -723,15 +745,12 @@ export class AppDialogsManager {
|
||||
if(this.loadDialogsPromise/* || 1 === 1 */) return this.loadDialogsPromise;
|
||||
|
||||
const promise = new Promise<void>(async(resolve) => {
|
||||
if(!this.chatList.childElementCount) {
|
||||
const container = this.chatList.parentElement;
|
||||
container.append(this.chatsPreloader);
|
||||
}
|
||||
|
||||
const {chatList, filterId} = this;
|
||||
|
||||
//return;
|
||||
|
||||
const filterId = this.filterId;
|
||||
let loadCount = 30/*this.chatsLoadCount */;
|
||||
// let loadCount = 30/*this.chatsLoadCount */;
|
||||
let loadCount = windowSize.windowH / 72 * 1.25 | 0;
|
||||
let offsetIndex = 0;
|
||||
|
||||
const {index: currentOffsetIndex} = this.getOffsetIndex(side);
|
||||
@ -753,8 +772,9 @@ export class AppDialogsManager {
|
||||
//console.time('getDialogs time');
|
||||
|
||||
const getConversationsResult = appMessagesManager.getConversations('', offsetIndex, loadCount, filterId, true);
|
||||
if(getConversationsResult.cached) {
|
||||
this.chatsPreloader.remove();
|
||||
if(!getConversationsResult.cached && !chatList.childElementCount) {
|
||||
const container = chatList.parentElement;
|
||||
container.append(this.chatsPreloader);
|
||||
}
|
||||
|
||||
const result = await getConversationsResult.promise;
|
||||
@ -766,43 +786,37 @@ export class AppDialogsManager {
|
||||
//console.timeEnd('getDialogs time');
|
||||
|
||||
// * loaded all
|
||||
//if(!result.dialogs.length || this.chatList.childElementCount === result.count) {
|
||||
//if(!result.dialogs.length || chatList.childElementCount === result.count) {
|
||||
// !result.dialogs.length не подходит, так как при супердревном диалоге getConversations его не выдаст.
|
||||
//if(this.chatList.childElementCount === result.count) {
|
||||
//if(chatList.childElementCount === result.count) {
|
||||
if(side === 'bottom') {
|
||||
if(result.isEnd) {
|
||||
this.scroll.loadedAll[side] = true;
|
||||
}
|
||||
} else {
|
||||
const storage = appMessagesManager.dialogsStorage.getFolder(filterId);
|
||||
if(!result.dialogs.length || (storage.length && storage[0][this.indexKey] < offsetIndex)) {
|
||||
this.scroll.loadedAll[side] = true;
|
||||
}
|
||||
} else if(result.isTopEnd) {
|
||||
this.scroll.loadedAll[side] = true;
|
||||
}
|
||||
|
||||
if(result.dialogs.length) {
|
||||
const dialogs = side === 'top' ? result.dialogs.slice().reverse() : result.dialogs;
|
||||
|
||||
const container = document.createDocumentFragment();
|
||||
const loadPromises: Promise<any>[] = [];
|
||||
const append = side === 'bottom';
|
||||
|
||||
const callbacks: (() => void)[] = [];
|
||||
const cccc = (callback: () => void) => {
|
||||
callbacks.push(callback);
|
||||
};
|
||||
|
||||
dialogs.forEach((dialog) => {
|
||||
this.addListDialog({
|
||||
dialog,
|
||||
container,
|
||||
append,
|
||||
loadPromises,
|
||||
isBatch: true
|
||||
});
|
||||
const element = this.sortedList.add(dialog.peerId, true, cccc, false);
|
||||
if(element.loadPromises) {
|
||||
loadPromises.push(...element.loadPromises);
|
||||
}
|
||||
});
|
||||
|
||||
if(container.childElementCount) {
|
||||
if(loadPromises.length) {
|
||||
await Promise.all(loadPromises).finally();
|
||||
}
|
||||
await Promise.all(loadPromises).finally();
|
||||
|
||||
this.scroll[append ? 'append' : 'prepend'](container);
|
||||
}
|
||||
callbacks.forEach(callback => callback());
|
||||
}
|
||||
|
||||
const offsetDialog = result.dialogs[side === 'top' ? 0 : result.dialogs.length - 1];
|
||||
@ -810,9 +824,7 @@ export class AppDialogsManager {
|
||||
this.offsets[side] = offsetDialog[this.indexKey];
|
||||
}
|
||||
|
||||
this.onListLengthChange();
|
||||
|
||||
this.log.debug('getDialogs ' + loadCount + ' dialogs by offset:', offsetIndex, result, this.chatList.childElementCount);
|
||||
this.log.debug('getDialogs ' + loadCount + ' dialogs by offset:', offsetIndex, result, chatList.childElementCount);
|
||||
|
||||
setTimeout(() => {
|
||||
this.scroll.onScroll();
|
||||
@ -821,7 +833,10 @@ export class AppDialogsManager {
|
||||
this.log.error(err);
|
||||
}
|
||||
|
||||
this.chatsPreloader.remove();
|
||||
if(this.chatsPreloader.parentElement) {
|
||||
this.chatsPreloader.remove();
|
||||
}
|
||||
|
||||
resolve();
|
||||
}).finally(() => {
|
||||
this.loadDialogsPromise = undefined;
|
||||
@ -860,10 +875,11 @@ export class AppDialogsManager {
|
||||
return;
|
||||
}
|
||||
|
||||
const part = this.chatList.parentElement as HTMLElement;
|
||||
const chatList = this.chatList;
|
||||
const part = chatList.parentElement as HTMLElement;
|
||||
let placeholderContainer = (Array.from(part.children) as HTMLElement[]).find(el => el.matches('.empty-placeholder'));
|
||||
const needPlaceholder = this.scroll.loadedAll.bottom && !this.chatList.childElementCount/* || true */;
|
||||
// this.chatList.style.display = 'none';
|
||||
const needPlaceholder = this.scroll.loadedAll.bottom && !chatList.childElementCount/* || true */;
|
||||
// chatList.style.display = 'none';
|
||||
|
||||
if(needPlaceholder && placeholderContainer) {
|
||||
return;
|
||||
@ -944,15 +960,16 @@ export class AppDialogsManager {
|
||||
part.classList.add('with-placeholder');
|
||||
}
|
||||
|
||||
private onListLengthChange = () => {
|
||||
public _onListLengthChange = () => {
|
||||
this.checkIfPlaceholderNeeded();
|
||||
|
||||
if(this.filterId > 0) return;
|
||||
|
||||
const count = this.chatList.childElementCount;
|
||||
const chatList = this.chatList;
|
||||
const count = chatList.childElementCount;
|
||||
|
||||
const parts = this.chatList.parentElement.parentElement;
|
||||
const bottom = this.chatList.parentElement.nextElementSibling as HTMLElement;
|
||||
const parts = chatList.parentElement.parentElement;
|
||||
const bottom = chatList.parentElement.nextElementSibling as HTMLElement;
|
||||
const hasContacts = !!bottom.childElementCount;
|
||||
if(count >= 10) {
|
||||
if(hasContacts) {
|
||||
@ -1019,14 +1036,11 @@ export class AppDialogsManager {
|
||||
};
|
||||
|
||||
public onChatsRegularScroll = () => {
|
||||
// return;
|
||||
|
||||
if(this.sliceTimeout) clearTimeout(this.sliceTimeout);
|
||||
this.sliceTimeout = window.setTimeout(() => {
|
||||
this.sliceTimeout = undefined;
|
||||
|
||||
if(this.reorderDialogsTimeout) {
|
||||
this.onChatsRegularScroll();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.chatList.childElementCount || this.processContact) {
|
||||
return;
|
||||
@ -1040,6 +1054,10 @@ export class AppDialogsManager {
|
||||
observer.observe(el);
|
||||
}); */
|
||||
|
||||
fastRafConventional(() => {
|
||||
|
||||
const perf = performance.now();
|
||||
|
||||
const scrollTopWas = this.scroll.scrollTop;
|
||||
|
||||
const firstElementChild = this.chatList.firstElementChild;
|
||||
@ -1115,16 +1133,20 @@ export class AppDialogsManager {
|
||||
//alert('left length:' + children.length);
|
||||
|
||||
this.scroll.scrollTop = firstElement.offsetTop - elementOverflow;
|
||||
|
||||
this.log('slice time', performance.now() - perf);
|
||||
/* const firstElementRect = firstElement.getBoundingClientRect();
|
||||
const scrollTop = */
|
||||
|
||||
//this.scroll.scrollIntoView(firstElement, false);
|
||||
});
|
||||
}, 200);
|
||||
};
|
||||
|
||||
private setOffsets() {
|
||||
const firstDialog = this.getDialogFromElement(this.chatList.firstElementChild as HTMLElement);
|
||||
const lastDialog = this.getDialogFromElement(this.chatList.lastElementChild as HTMLElement);
|
||||
const chatList = this.chatList;
|
||||
const firstDialog = this.getDialogFromElement(chatList.firstElementChild as HTMLElement);
|
||||
const lastDialog = this.getDialogFromElement(chatList.lastElementChild as HTMLElement);
|
||||
|
||||
this.offsets.top = firstDialog[this.indexKey];
|
||||
this.offsets.bottom = lastDialog[this.indexKey];
|
||||
@ -1230,71 +1252,14 @@ export class AppDialogsManager {
|
||||
return list;
|
||||
}
|
||||
|
||||
private reorderDialogs() {
|
||||
//const perf = performance.now();
|
||||
if(this.reorderDialogsTimeout) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.loadDialogsPromise) {
|
||||
this.loadDialogsPromise.then(() => {
|
||||
this.reorderDialogs();
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.reorderDialogsTimeout = window.requestAnimationFrame(() => {
|
||||
this.reorderDialogsTimeout = 0;
|
||||
|
||||
if(this.loadDialogsPromise) {
|
||||
this.loadDialogsPromise.then(() => {
|
||||
this.reorderDialogs();
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const dialogs = appMessagesManager.dialogsStorage.getFolder(this.filterId);
|
||||
|
||||
const currentOrder = (Array.from(this.chatList.children) as HTMLElement[]).map(el => +el.dataset.peerId);
|
||||
|
||||
const {index} = this.getOffsetIndex('top');
|
||||
const pos = dialogs.findIndex(dialog => dialog[this.indexKey] <= index);
|
||||
|
||||
const offset = Math.max(0, pos);
|
||||
dialogs.forEach((dialog, index) => {
|
||||
const dom = this.getDialogDom(dialog.peerId);
|
||||
if(!dom) {
|
||||
return;
|
||||
}
|
||||
|
||||
const needIndex = index - offset;
|
||||
if(needIndex > currentOrder.length) {
|
||||
this.deleteDialog(dialog.peerId);
|
||||
return;
|
||||
}
|
||||
|
||||
const peerIdByIndex = currentOrder[needIndex];
|
||||
|
||||
if(peerIdByIndex !== dialog.peerId) {
|
||||
if(positionElementByIndex(dom.listEl, this.chatList, needIndex)) {
|
||||
this.log.debug('setDialogPosition:', dialog, dom, peerIdByIndex, needIndex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//this.log('Reorder time:', performance.now() - perf);
|
||||
});
|
||||
}
|
||||
|
||||
public setLastMessage(
|
||||
dialog: Dialog,
|
||||
lastMessage?: any,
|
||||
dom?: DialogDom,
|
||||
highlightWord?: string,
|
||||
loadPromises?: Promise<any>[],
|
||||
isBatch = false
|
||||
isBatch = false,
|
||||
setUnread = false
|
||||
) {
|
||||
///////console.log('setlastMessage:', lastMessage);
|
||||
if(!dom) {
|
||||
@ -1408,16 +1373,16 @@ export class AppDialogsManager {
|
||||
replaceContent(dom.lastTimeSpan, formatDateAccordingToTodayNew(new Date(date * 1000)));
|
||||
} else dom.lastTimeSpan.textContent = '';
|
||||
|
||||
if(this.doms[peerId] === dom) {
|
||||
this.setUnreadMessages(dialog, isBatch);
|
||||
} else { // means search
|
||||
dom.listEl.dataset.mid = lastMessage.mid;
|
||||
if(setUnread !== null) {
|
||||
if(setUnread) {
|
||||
this.setUnreadMessages(dialog, dom, isBatch);
|
||||
} else { // means search
|
||||
dom.listEl.dataset.mid = lastMessage.mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setUnreadMessages(dialog: Dialog, isBatch = false) {
|
||||
const dom = this.getDialogDom(dialog.peerId);
|
||||
|
||||
private setUnreadMessages(dialog: Dialog, dom = this.getDialogDom(dialog.peerId), isBatch = false) {
|
||||
if(dialog.folder_id === 1) {
|
||||
this.accumulateArchivedUnread();
|
||||
}
|
||||
@ -1523,7 +1488,9 @@ export class AppDialogsManager {
|
||||
}
|
||||
|
||||
private getDialogDom(peerId: number) {
|
||||
return this.doms[peerId];
|
||||
// return this.doms[peerId];
|
||||
const element = this.sortedList.get(peerId);
|
||||
return element?.dom;
|
||||
}
|
||||
|
||||
private getDialog(dialog: Dialog | number): Dialog {
|
||||
@ -1543,32 +1510,20 @@ export class AppDialogsManager {
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private addListDialog(options: Parameters<AppDialogsManager['addDialogNew']>[0] & {isBatch?: boolean}) {
|
||||
public 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;
|
||||
}
|
||||
|
||||
options.autonomous = false;
|
||||
|
||||
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);
|
||||
this.setLastMessage(dialog, undefined, ret.dom, undefined, options.loadPromises, options.isBatch, true);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -1745,7 +1700,7 @@ export class AppDialogsManager {
|
||||
}
|
||||
|
||||
dom.lastMessageSpan.classList.remove('user-typing');
|
||||
this.setLastMessage(dialog, null, dom);
|
||||
this.setLastMessage(dialog, null, dom, undefined, undefined, undefined, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ import PeerTitle from "../../components/peerTitle";
|
||||
import { forEachReverse } from "../../helpers/array";
|
||||
import htmlToDocumentFragment from "../../helpers/dom/htmlToDocumentFragment";
|
||||
import htmlToSpan from "../../helpers/dom/htmlToSpan";
|
||||
import { REPLIES_PEER_ID, SERVICE_PEER_ID } from "../mtproto/mtproto_config";
|
||||
import { MUTE_UNTIL, REPLIES_PEER_ID, SERVICE_PEER_ID } from "../mtproto/mtproto_config";
|
||||
import formatCallDuration from "../../helpers/formatCallDuration";
|
||||
import appAvatarsManager from "./appAvatarsManager";
|
||||
import telegramMeWebManager from "../mtproto/telegramMeWebManager";
|
||||
@ -1379,8 +1379,8 @@ export class AppMessagesManager {
|
||||
|
||||
//if(!options.isGroupedItem) {
|
||||
this.saveMessages([message], {storage, isOutgoing: true});
|
||||
this.setDialogTopMessage(message);
|
||||
setTimeout(() => {
|
||||
this.setDialogTopMessage(message);
|
||||
rootScope.dispatchEvent('history_append', {storage, peerId, mid: messageId});
|
||||
}, 0);
|
||||
}
|
||||
@ -3096,7 +3096,7 @@ export class AppMessagesManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
public canDeleteMessage(message: any) {
|
||||
public canDeleteMessage(message: MyMessage) {
|
||||
return message && (
|
||||
message.peerId > 0
|
||||
|| message.fromId === rootScope.myId
|
||||
@ -3553,7 +3553,7 @@ export class AppMessagesManager {
|
||||
}
|
||||
}
|
||||
|
||||
handleNewMessages = () => {
|
||||
private handleNewMessages = () => {
|
||||
clearTimeout(this.newMessagesHandleTimeout);
|
||||
this.newMessagesHandleTimeout = 0;
|
||||
|
||||
@ -3561,7 +3561,7 @@ export class AppMessagesManager {
|
||||
this.newMessagesToHandle = {};
|
||||
};
|
||||
|
||||
handleNewDialogs = () => {
|
||||
private handleNewDialogs = () => {
|
||||
let newMaxSeenId = 0;
|
||||
const obj = this.newDialogsToHandle;
|
||||
for(const peerId in obj) {
|
||||
@ -3593,8 +3593,9 @@ export class AppMessagesManager {
|
||||
}
|
||||
|
||||
if(this.newDialogsHandlePromise) return this.newDialogsHandlePromise;
|
||||
return this.newDialogsHandlePromise = new Promise((resolve) => {
|
||||
return this.newDialogsHandlePromise = new Promise<void>((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
this.newDialogsHandlePromise = undefined;
|
||||
this.handleNewDialogs();
|
||||
}, 0);
|
||||
@ -4113,7 +4114,7 @@ export class AppMessagesManager {
|
||||
}
|
||||
|
||||
if((message as Message.message).fwd_from) {
|
||||
notifyPeerToHandle.fwdCount++;
|
||||
++notifyPeerToHandle.fwdCount;
|
||||
}
|
||||
|
||||
notifyPeerToHandle.topMessage = message;
|
||||
@ -4322,17 +4323,10 @@ export class AppMessagesManager {
|
||||
};
|
||||
|
||||
private onUpdateChannelAvailableMessages = (update: Update.updateChannelAvailableMessages) => {
|
||||
const channelId: number = update.channel_id;
|
||||
const messages: number[] = [];
|
||||
const peerId: number = -channelId;
|
||||
const peerId: number = -update.channel_id;
|
||||
const history = this.getHistoryStorage(peerId).history.slice;
|
||||
if(history.length) {
|
||||
history.forEach((msgId: number) => {
|
||||
if(!update.available_min_id || msgId <= update.available_min_id) {
|
||||
messages.push(msgId);
|
||||
}
|
||||
});
|
||||
}
|
||||
const availableMinId = appMessagesIdsManager.generateMessageId(update.available_min_id);
|
||||
const messages = history.filter(mid => mid <= availableMinId);
|
||||
|
||||
(update as any as Update.updateDeleteChannelMessages).messages = messages;
|
||||
this.onUpdateDeleteMessages(update as any as Update.updateDeleteChannelMessages);
|
||||
@ -4368,17 +4362,14 @@ export class AppMessagesManager {
|
||||
return this.getHistoryStorage(+splitted[0], +splitted[1]);
|
||||
});
|
||||
|
||||
[this.getHistoryStorage(peerId)].concat(threadsStorages).forEach(historyStorage => {
|
||||
for(const mid in historyUpdated.msgs) {
|
||||
historyStorage.history.delete(+mid);
|
||||
const historyStorage = this.getHistoryStorage(peerId);
|
||||
[historyStorage].concat(threadsStorages).forEach(historyStorage => {
|
||||
for(const mid of historyUpdated.msgs) {
|
||||
historyStorage.history.delete(mid);
|
||||
}
|
||||
if(historyUpdated.count &&
|
||||
historyStorage.count !== null &&
|
||||
historyStorage.count > 0) {
|
||||
historyStorage.count -= historyUpdated.count;
|
||||
if(historyStorage.count < 0) {
|
||||
historyStorage.count = 0;
|
||||
}
|
||||
|
||||
if(historyUpdated.count && historyStorage.count) {
|
||||
historyStorage.count = Math.max(0, historyStorage.count - historyUpdated.count);
|
||||
}
|
||||
});
|
||||
|
||||
@ -4392,12 +4383,21 @@ export class AppMessagesManager {
|
||||
|
||||
if(historyUpdated.unread) {
|
||||
foundDialog.unread_count -= historyUpdated.unread;
|
||||
}
|
||||
|
||||
if(historyUpdated.unreadMentions || historyUpdated.unread) {
|
||||
rootScope.dispatchEvent('dialog_unread', {peerId});
|
||||
}
|
||||
|
||||
if(historyUpdated.msgs.has(foundDialog.top_message)) {
|
||||
this.reloadConversation(peerId);
|
||||
const slice = historyStorage.history.first;
|
||||
if(slice.isEnd(SliceEnd.Bottom) && slice.length) {
|
||||
const mid = slice[0];
|
||||
const message = this.getMessageByPeer(peerId, mid);
|
||||
this.setDialogTopMessage(message, foundDialog);
|
||||
} else {
|
||||
this.reloadConversation(peerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -4420,7 +4420,7 @@ export class AppMessagesManager {
|
||||
const dialog = this.getDialogOnly(peerId);
|
||||
if(!!dialog !== needDialog) {
|
||||
if(needDialog) {
|
||||
this.reloadConversation(-channelId);
|
||||
this.reloadConversation(peerId);
|
||||
} else {
|
||||
if(dialog) {
|
||||
this.dialogsStorage.dropDialog(peerId);
|
||||
@ -4431,13 +4431,12 @@ export class AppMessagesManager {
|
||||
};
|
||||
|
||||
private onUpdateChannelReload = (update: Update.updateChannelReload) => {
|
||||
const channelId = update.channel_id;
|
||||
const peerId = -channelId;
|
||||
const peerId = update.channel_id;
|
||||
|
||||
this.dialogsStorage.dropDialog(peerId);
|
||||
|
||||
delete this.historiesStorage[peerId];
|
||||
this.reloadConversation(-channelId).then(() => {
|
||||
this.reloadConversation(peerId).then(() => {
|
||||
rootScope.dispatchEvent('history_reload', peerId);
|
||||
});
|
||||
};
|
||||
@ -4447,7 +4446,7 @@ export class AppMessagesManager {
|
||||
const peerId = -update.channel_id;
|
||||
const mid = appMessagesIdsManager.generateMessageId(update.id);
|
||||
const message: Message.message = this.getMessageByPeer(peerId, mid);
|
||||
if(!message.deleted && message.views && message.views < views) {
|
||||
if(!message.deleted && message.views !== undefined && message.views < views) {
|
||||
message.views = views;
|
||||
rootScope.dispatchEvent('message_views', {peerId, mid, views});
|
||||
}
|
||||
@ -4458,7 +4457,7 @@ export class AppMessagesManager {
|
||||
const fromId = SERVICE_PEER_ID;
|
||||
const peerId = fromId;
|
||||
const messageId = this.generateTempMessageId(peerId);
|
||||
const message: any = {
|
||||
const message: Message.message = {
|
||||
_: 'message',
|
||||
id: messageId,
|
||||
from_id: appPeersManager.getOutputPeer(fromId),
|
||||
@ -4474,7 +4473,7 @@ export class AppMessagesManager {
|
||||
_: 'user',
|
||||
id: fromId,
|
||||
pFlags: {verified: true},
|
||||
access_hash: 0,
|
||||
access_hash: '0',
|
||||
first_name: 'Telegram',
|
||||
phone: '42777'
|
||||
}]);
|
||||
@ -4595,7 +4594,7 @@ export class AppMessagesManager {
|
||||
}
|
||||
};
|
||||
|
||||
public setDialogToStateIfMessageIsTop(message: any) {
|
||||
public setDialogToStateIfMessageIsTop(message: MyMessage) {
|
||||
const dialog = this.getDialogOnly(message.peerId);
|
||||
if(dialog && dialog.top_message === message.mid) {
|
||||
this.dialogsStorage.setDialogToState(dialog);
|
||||
@ -4642,9 +4641,9 @@ export class AppMessagesManager {
|
||||
return promise;
|
||||
}
|
||||
|
||||
private checkPendingMessage(message: any) {
|
||||
private checkPendingMessage(message: MyMessage) {
|
||||
const randomId = this.pendingByMessageId[message.mid];
|
||||
let pendingMessage: any;
|
||||
let pendingMessage: ReturnType<AppMessagesManager['finalizePendingMessage']>;
|
||||
if(randomId) {
|
||||
const pendingData = this.pendingByRandomId[randomId];
|
||||
if(pendingMessage = this.finalizePendingMessage(randomId, message)) {
|
||||
@ -4666,7 +4665,7 @@ export class AppMessagesManager {
|
||||
mute = !appNotificationsManager.isPeerLocalMuted(peerId, false);
|
||||
}
|
||||
|
||||
settings.mute_until = mute ? 0x7FFFFFFF : 0;
|
||||
settings.mute_until = mute ? MUTE_UNTIL : 0;
|
||||
|
||||
return appNotificationsManager.updateNotifySettings({
|
||||
_: 'inputNotifyPeer',
|
||||
@ -4684,7 +4683,7 @@ export class AppMessagesManager {
|
||||
}
|
||||
}
|
||||
|
||||
public finalizePendingMessage(randomId: string, finalMessage: any) {
|
||||
public finalizePendingMessage(randomId: string, finalMessage: MyMessage) {
|
||||
const pendingData = this.pendingByRandomId[randomId];
|
||||
// this.log('pdata', randomID, pendingData)
|
||||
|
||||
@ -4699,7 +4698,7 @@ export class AppMessagesManager {
|
||||
|
||||
// this.log('pending', randomID, historyStorage.pending)
|
||||
|
||||
const message = this.getMessageFromStorage(storage, tempId);
|
||||
const message: Message.message = this.getMessageFromStorage(storage, tempId);
|
||||
if(!message.deleted) {
|
||||
delete message.pFlags.is_outgoing;
|
||||
delete message.pending;
|
||||
@ -5382,17 +5381,18 @@ export class AppMessagesManager {
|
||||
|
||||
message.deleted = true;
|
||||
|
||||
if(message._ !== 'messageService' && message.grouped_id) {
|
||||
const groupedStorage = this.groupedMessagesStorage[message.grouped_id];
|
||||
const groupedId = (message as Message.message).grouped_id;
|
||||
if(groupedId) {
|
||||
const groupedStorage = this.groupedMessagesStorage[groupedId];
|
||||
if(groupedStorage) {
|
||||
delete groupedStorage[mid];
|
||||
|
||||
if(!history.albums) history.albums = {};
|
||||
(history.albums[message.grouped_id] || (history.albums[message.grouped_id] = new Set())).add(mid);
|
||||
(history.albums[groupedId] || (history.albums[groupedId] = new Set())).add(mid);
|
||||
|
||||
if(!Object.keys(groupedStorage).length) {
|
||||
delete history.albums;
|
||||
delete this.groupedMessagesStorage[message.grouped_id];
|
||||
delete this.groupedMessagesStorage[groupedId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -344,7 +344,7 @@ export class AppUsersManager {
|
||||
});
|
||||
}
|
||||
|
||||
public saveApiUsers(apiUsers: any[], override?: boolean) {
|
||||
public saveApiUsers(apiUsers: MTUser[], override?: boolean) {
|
||||
apiUsers.forEach((user) => this.saveApiUser(user, override));
|
||||
}
|
||||
|
||||
|
@ -11,3 +11,4 @@ export type UserAuth = {dcID: number | string, date: number, id: number};
|
||||
|
||||
export const REPLIES_PEER_ID = 1271266957;
|
||||
export const SERVICE_PEER_ID = 777000;
|
||||
export const MUTE_UNTIL = 0x7FFFFFFF;
|
||||
|
@ -43,6 +43,7 @@ export type BroadcastEvents = {
|
||||
'dialog_migrate': {migrateFrom: number, migrateTo: number},
|
||||
//'dialog_top': Dialog,
|
||||
'dialog_notify_settings': Dialog,
|
||||
// 'dialog_order': {dialog: Dialog, pos: number},
|
||||
'dialogs_multiupdate': {[peerId: string]: Dialog},
|
||||
'dialogs_archived_unread': {count: number},
|
||||
|
||||
|
@ -27,6 +27,7 @@ import rootScope from "../rootScope";
|
||||
import { safeReplaceObject } from "../../helpers/object";
|
||||
import { AppStateManager } from "../appManagers/appStateManager";
|
||||
import { SliceEnd } from "../../helpers/slicedArray";
|
||||
import { MyDialogFilter } from "./filters";
|
||||
|
||||
export type FolderDialog = {
|
||||
dialog: Dialog,
|
||||
@ -84,6 +85,14 @@ export default class DialogsStorage {
|
||||
}
|
||||
});
|
||||
|
||||
// to set new indexes
|
||||
rootScope.addEventListener('filter_order', () => {
|
||||
// ! MUST BE REFACTORED !
|
||||
for(let id in this.appMessagesManager.filtersStorage.filters) {
|
||||
this.getFolder(+id, false);
|
||||
}
|
||||
});
|
||||
|
||||
rootScope.addMultipleEventsListeners({
|
||||
updateFolderPeers: this.onUpdateFolderPeers,
|
||||
|
||||
@ -186,19 +195,7 @@ export default class DialogsStorage {
|
||||
const indexStr = this.getDialogIndexKey(id);
|
||||
for(const peerId in this.dialogs) {
|
||||
const dialog = this.dialogs[peerId];
|
||||
if(this.appMessagesManager.filtersStorage.testDialogForFilter(dialog, filter) && (!skipMigrated || dialog.migratedTo === undefined)) {
|
||||
let index: number;
|
||||
|
||||
const pinnedIndex = filter.pinned_peers.indexOf(dialog.peerId);
|
||||
if(pinnedIndex !== -1) {
|
||||
index = this.generateDialogIndex(this.generateDialogPinnedDateByIndex(filter.pinned_peers.length - 1 - pinnedIndex), true);
|
||||
} else if(dialog.pFlags?.pinned) {
|
||||
index = this.generateIndexForDialog(dialog, true);
|
||||
} else {
|
||||
index = dialog.index;
|
||||
}
|
||||
|
||||
dialog[indexStr] = index;
|
||||
if(this.setDialogIndexInFilter(dialog, indexStr, filter) && (!skipMigrated || dialog.migratedTo === undefined)) {
|
||||
insertInDescendSortedArray(dialogs, dialog, indexStr, -1);
|
||||
}
|
||||
}
|
||||
@ -209,6 +206,23 @@ export default class DialogsStorage {
|
||||
// return dialogs.map(d => d.dialog);
|
||||
}
|
||||
|
||||
private setDialogIndexInFilter(dialog: Dialog, indexKey: ReturnType<DialogsStorage['getDialogIndexKey']>, filter: MyDialogFilter) {
|
||||
let index: number;
|
||||
|
||||
if(this.appMessagesManager.filtersStorage.testDialogForFilter(dialog, filter)) {
|
||||
const pinnedIndex = filter.pinned_peers.indexOf(dialog.peerId);
|
||||
if(pinnedIndex !== -1) {
|
||||
index = this.generateDialogIndex(this.generateDialogPinnedDateByIndex(filter.pinned_peers.length - 1 - pinnedIndex), true);
|
||||
} else if(dialog.pFlags?.pinned) {
|
||||
index = this.generateIndexForDialog(dialog, true);
|
||||
} else {
|
||||
index = dialog.index;
|
||||
}
|
||||
}
|
||||
|
||||
return dialog[indexKey] = index;
|
||||
}
|
||||
|
||||
public getDialog(peerId: number, folderId?: number, skipMigrated = true): [Dialog, number] | [] {
|
||||
const folders: Dialog[][] = [];
|
||||
|
||||
@ -286,8 +300,17 @@ export default class DialogsStorage {
|
||||
}
|
||||
|
||||
const index = this.generateDialogIndex(topDate, isPinned);
|
||||
if(justReturn) return index;
|
||||
if(justReturn) {
|
||||
return index;
|
||||
}
|
||||
|
||||
dialog.index = index;
|
||||
|
||||
// ! MUST BE REFACTORED !
|
||||
for(let id in this.appMessagesManager.filtersStorage.filters) {
|
||||
const filter = this.appMessagesManager.filtersStorage.filters[id];
|
||||
this.setDialogIndexInFilter(dialog, this.getDialogIndexKey(+id), filter);
|
||||
}
|
||||
}
|
||||
|
||||
public generateDialogPinnedDateByIndex(pinnedIndex: number) {
|
||||
@ -327,11 +350,12 @@ export default class DialogsStorage {
|
||||
|
||||
public setDialogToState(dialog: Dialog) {
|
||||
const historyStorage = this.appMessagesManager.getHistoryStorage(dialog.peerId);
|
||||
const history = [].concat(historyStorage.history.slice);
|
||||
const messagesStorage = this.appMessagesManager.getMessagesStorage(dialog.peerId);
|
||||
const history = historyStorage.history.slice;
|
||||
let incomingMessage: any;
|
||||
for(let i = 0, length = history.length; i < length; ++i) {
|
||||
const mid = history[i];
|
||||
const message = this.appMessagesManager.getMessageByPeer(dialog.peerId, mid);
|
||||
const message: MyMessage = this.appMessagesManager.getMessageFromStorage(messagesStorage, mid);
|
||||
if(!message.pFlags.is_outgoing) {
|
||||
incomingMessage = message;
|
||||
|
||||
@ -339,7 +363,7 @@ export default class DialogsStorage {
|
||||
if(fromId !== dialog.peerId) {
|
||||
this.appStateManager.requestPeer(fromId, 'topMessage_' + dialog.peerId, 1);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -372,24 +396,29 @@ export default class DialogsStorage {
|
||||
if(pos !== -1) {
|
||||
dialogs.splice(pos, 1);
|
||||
}
|
||||
|
||||
|
||||
//if(!this.dialogs[dialog.peerId]) {
|
||||
this.dialogs[dialog.peerId] = dialog;
|
||||
|
||||
|
||||
this.setDialogToState(dialog);
|
||||
//}
|
||||
|
||||
//}
|
||||
|
||||
// let pos: number;
|
||||
if(offsetDate &&
|
||||
!dialog.pFlags.pinned &&
|
||||
(!this.dialogsOffsetDate[dialog.folder_id] || offsetDate < this.dialogsOffsetDate[dialog.folder_id])) {
|
||||
if(pos !== -1) {
|
||||
// So the dialog jumped to the last position
|
||||
if(pos !== -1) { // So the dialog jumped to the last position
|
||||
// dialogs.splice(pos, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.dialogsOffsetDate[dialog.folder_id] = offsetDate;
|
||||
}
|
||||
|
||||
insertInDescendSortedArray(dialogs, dialog, 'index', pos);
|
||||
/* const newPos = */insertInDescendSortedArray(dialogs, dialog, 'index', pos);
|
||||
/* if(pos !== -1 && pos !== newPos) {
|
||||
rootScope.dispatchEvent('dialog_order', {dialog, pos: newPos});
|
||||
} */
|
||||
}
|
||||
|
||||
public dropDialog(peerId: number): [Dialog, number] | [] {
|
||||
@ -600,12 +629,13 @@ export default class DialogsStorage {
|
||||
}
|
||||
|
||||
public getDialogs(query = '', offsetIndex?: number, limit = 20, folderId = 0, skipMigrated = false): {
|
||||
cached: boolean;
|
||||
cached: boolean,
|
||||
promise: Promise<{
|
||||
dialogs: Dialog[];
|
||||
count: number;
|
||||
isEnd: boolean;
|
||||
}>;
|
||||
dialogs: Dialog[],
|
||||
count: number,
|
||||
isTopEnd: boolean,
|
||||
isEnd: boolean
|
||||
}>
|
||||
} {
|
||||
if(folderId > 1) {
|
||||
const fillContactsResult = this.appUsersManager.fillContacts();
|
||||
@ -659,12 +689,14 @@ export default class DialogsStorage {
|
||||
}
|
||||
|
||||
const loadedAll = this.isDialogsLoaded(realFolderId);
|
||||
if(query || loadedAll || curDialogStorage.length >= offset + limit) {
|
||||
if(query || loadedAll || curDialogStorage.length >= (offset + limit)) {
|
||||
const dialogs = curDialogStorage.slice(offset, offset + limit);
|
||||
return {
|
||||
cached: true,
|
||||
promise: Promise.resolve({
|
||||
dialogs: curDialogStorage.slice(offset, offset + limit),
|
||||
dialogs,
|
||||
count: loadedAll ? curDialogStorage.length : null,
|
||||
isTopEnd: curDialogStorage.length && ((dialogs[0] && dialogs[0] === curDialogStorage[0]) || curDialogStorage[0][indexStr] < offsetIndex),
|
||||
isEnd: loadedAll && (offset + limit) >= curDialogStorage.length
|
||||
})
|
||||
};
|
||||
@ -689,9 +721,11 @@ export default class DialogsStorage {
|
||||
|
||||
//this.log.warn(offset, offset + limit, curDialogStorage.dialogs.length, this.dialogs.length);
|
||||
|
||||
const dialogs = curDialogStorage.slice(offset, offset + limit);
|
||||
return {
|
||||
dialogs: curDialogStorage.slice(offset, offset + limit),
|
||||
dialogs,
|
||||
count: result.count === undefined ? curDialogStorage.length : result.count,
|
||||
isTopEnd: curDialogStorage.length && ((dialogs[0] && dialogs[0] === curDialogStorage[0]) || curDialogStorage[0][indexStr] < offsetIndex),
|
||||
// isEnd: this.isDialogsLoaded(realFolderId) && (offset + limit) >= curDialogStorage.length
|
||||
isEnd: result.isEnd
|
||||
};
|
||||
|
@ -632,6 +632,10 @@ $bubble-margin: .25rem;
|
||||
|
||||
> .thumbnail {
|
||||
opacity: .8;
|
||||
|
||||
&.fade-in {
|
||||
animation: thumbnail-fade-in-opacity .2s ease-in-out forwards;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,10 @@
|
||||
|
||||
.media-photo {
|
||||
border-radius: inherit;
|
||||
|
||||
&.thumbnail {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-ico {
|
||||
|
@ -931,6 +931,15 @@ img.emoji {
|
||||
}
|
||||
}
|
||||
|
||||
/* .fade-in-new {
|
||||
opacity: 1;
|
||||
transition: opacity .2s ease-in-out;
|
||||
|
||||
&.not-yet {
|
||||
opacity: 0;
|
||||
}
|
||||
} */
|
||||
|
||||
.show-more {
|
||||
padding-top: 13px;
|
||||
padding-bottom: 13px;
|
||||
@ -1169,12 +1178,6 @@ middle-ellipsis-element {
|
||||
.media-sticker,
|
||||
.media-round,
|
||||
.media-poster {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
@include animation-level(2) {
|
||||
&.fade-in {
|
||||
animation: fade-in-opacity .2s ease-in-out forwards;
|
||||
@ -1184,14 +1187,14 @@ middle-ellipsis-element {
|
||||
animation: fade-out-opacity .2s ease-in-out forwards;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.media-photo.thumbnail {
|
||||
@include animation-level(2) {
|
||||
&.fade-in {
|
||||
animation: thumbnail-fade-in-opacity .2s ease-in-out forwards;
|
||||
}
|
||||
}
|
||||
// & ~ & {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
// }
|
||||
}
|
||||
|
||||
.media-video {
|
||||
|
Loading…
x
Reference in New Issue
Block a user