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