Browse Source

New slice method for chatlist due to Safari bug

master
Eduard Kuzmenko 4 years ago
parent
commit
6a7fe6a8c4
  1. 21
      src/components/scrollable.ts
  2. 1
      src/components/sidebarLeft/tabs/archivedTab.ts
  3. 303
      src/lib/appManagers/appDialogsManager.ts
  4. 2
      src/lib/appManagers/appStateManager.ts

21
src/components/scrollable.ts

@ -129,7 +129,7 @@ export default class Scrollable extends ScrollableBase { @@ -129,7 +129,7 @@ export default class Scrollable extends ScrollableBase {
public onScrollMeasure: number = null;
public lastScrollTop: number = 0;
private lastScrollTop: number = 0;
public loadedAll: SliceSidesContainer = {top: true, bottom: false};
@ -155,6 +155,7 @@ export default class Scrollable extends ScrollableBase { @@ -155,6 +155,7 @@ export default class Scrollable extends ScrollableBase {
this.checkForTriggers();
this.onScrollMeasure = 0;
this.lastScrollTop = this.scrollTop;
});
};
@ -170,13 +171,16 @@ export default class Scrollable extends ScrollableBase { @@ -170,13 +171,16 @@ export default class Scrollable extends ScrollableBase {
const {clientHeight, scrollTop} = container;
const maxScrollTop = scrollHeight - clientHeight;
// 1 - bottom, -1 - top
const direction = this.lastScrollTop == scrollTop ? 0 : (this.lastScrollTop < scrollTop ? 1 : -1);
//this.log('checkForTriggers:', scrollTop, maxScrollTop);
if(this.onScrolledTop && scrollTop <= this.onScrollOffset) {
if(this.onScrolledTop && scrollTop <= this.onScrollOffset && direction <= 0/* && direction === -1 */) {
this.onScrolledTop();
}
if(this.onScrolledBottom && (maxScrollTop - scrollTop) <= this.onScrollOffset) {
if(this.onScrolledBottom && (maxScrollTop - scrollTop) <= this.onScrollOffset && direction >= 0/* && direction === 1 */) {
this.onScrolledBottom();
}
};
@ -215,7 +219,7 @@ export default class Scrollable extends ScrollableBase { @@ -215,7 +219,7 @@ export default class Scrollable extends ScrollableBase {
return this.scrollTop;
};
public slice(side: SliceSides, safeCount: number/* sliceLength: number */) {
/* public slice(side: SliceSides, safeCount: number) {
//const isOtherSideLoaded = this.loadedAll[side == 'top' ? 'bottom' : 'top'];
//const multiplier = 2 - +isOtherSideLoaded;
const multiplier = 2;
@ -239,8 +243,15 @@ export default class Scrollable extends ScrollableBase { @@ -239,8 +243,15 @@ export default class Scrollable extends ScrollableBase {
this.loadedAll[side] = false;
}
// * fix instant load of cutted side
if(side == 'top') {
this.lastScrollTop = 0;
} else {
this.lastScrollTop = this.scrollHeight + this.container.clientHeight;
}
return sliced;
}
} */
get isScrolledDown() {
return this.scrollHeight - Math.round(this.scrollTop + this.container.offsetHeight) <= 1;

1
src/components/sidebarLeft/tabs/archivedTab.ts

@ -12,6 +12,7 @@ export default class AppArchivedTab implements SliderTab { @@ -12,6 +12,7 @@ export default class AppArchivedTab implements SliderTab {
init() {
this.scroll = new Scrollable(this.container, 'CLA', 500);
this.scroll.container.addEventListener('scroll', appDialogsManager.onChatsRegularScroll);
this.scroll.setVirtualContainer(this.chatList);
this.scroll.onScrolledBottom = appDialogsManager.onChatsScroll;
///this.scroll.attachSentinels();

303
src/lib/appManagers/appDialogsManager.ts

@ -9,7 +9,7 @@ import appSidebarLeft from "../../components/sidebarLeft"; @@ -9,7 +9,7 @@ import appSidebarLeft from "../../components/sidebarLeft";
import { formatDateAccordingToToday } from "../../helpers/date";
import { escapeRegExp } from "../../helpers/string";
import { isTouchSupported } from "../../helpers/touchSupport";
import { isSafari } from "../../helpers/userAgent";
import { isApple, isSafari } from "../../helpers/userAgent";
import { logger, LogLevels } from "../logger";
import { RichTextProcessor } from "../richtextprocessor";
import rootScope from "../rootScope";
@ -192,6 +192,8 @@ export class AppDialogsManager { @@ -192,6 +192,8 @@ export class AppDialogsManager {
private topOffsetIndex = 0;
private sliceTimeout: number;
constructor() {
this.chatsPreloader = putPreloader(null, true);
@ -204,6 +206,7 @@ export class AppDialogsManager { @@ -204,6 +206,7 @@ export class AppDialogsManager {
bottomPart.append(this.folders.container);
this.scroll = this._scroll = new Scrollable(bottomPart, 'CL', 500);
this.scroll.container.addEventListener('scroll', this.onChatsRegularScroll);
this.scroll.onScrolledTop = this.onChatsScrollTop;
this.scroll.onScrolledBottom = this.onChatsScroll;
this.scroll.setVirtualContainer(this.chatList);
@ -589,132 +592,222 @@ export class AppDialogsManager { @@ -589,132 +592,222 @@ export class AppDialogsManager {
};
}
private async loadDialogs(side: SliceSides = 'bottom') {
private loadDialogs(side: SliceSides = 'bottom') {
if(testScroll) {
return;
}
if(this.loadDialogsPromise/* || 1 == 1 */) return this.loadDialogsPromise;
if(!this.chatList.childElementCount) {
const container = this.chatList.parentElement;
container.append(this.chatsPreloader);
}
//return;
const filterID = this.filterID;
let loadCount = 30/*this.chatsLoadCount */;
const storage = appMessagesManager.dialogsStorage.getFolder(filterID);
let offsetIndex = 0;
if(side == 'top') {
const element = this.chatList.firstElementChild;
if(element) {
const peerID = +element.getAttribute('data-peerID');
const index = storage.findIndex(dialog => dialog.peerID == peerID);
const needIndex = Math.max(0, index - loadCount);
loadCount = index - needIndex;
offsetIndex = storage[needIndex].index + 1;
}
} else {
const element = this.chatList.lastElementChild;
if(element) {
const peerID = +element.getAttribute('data-peerID');
const dialog = storage.find(dialog => dialog.peerID == peerID);
offsetIndex = dialog.index;
const promise = new Promise(async(resolve, reject) => {
if(!this.chatList.childElementCount) {
const container = this.chatList.parentElement;
container.append(this.chatsPreloader);
}
/* for(let i = storage.length - 1; i >= 0; --i) {
const dialog = storage[i];
if(this.getDialogDom(dialog.peerID)) {
//return;
const filterID = this.filterID;
let loadCount = 30/*this.chatsLoadCount */;
const storage = appMessagesManager.dialogsStorage.getFolder(filterID);
let offsetIndex = 0;
if(side == 'top') {
const element = this.chatList.firstElementChild;
if(element) {
const peerID = +element.getAttribute('data-peerID');
const index = storage.findIndex(dialog => dialog.peerID == peerID);
const needIndex = Math.max(0, index - loadCount);
loadCount = index - needIndex;
offsetIndex = storage[needIndex].index + 1;
}
} else {
const element = this.chatList.lastElementChild;
if(element) {
const peerID = +element.getAttribute('data-peerID');
const dialog = storage.find(dialog => dialog.peerID == peerID);
offsetIndex = dialog.index;
break;
}
} */
}
//let offset = storage[storage.length - 1]?.index || 0;
}
//let offset = storage[storage.length - 1]?.index || 0;
try {
//console.time('getDialogs time');
const getConversationPromise = (this.filterID > 1 ? appUsersManager.getContacts() as Promise<any> : Promise.resolve()).then(() => {
return appMessagesManager.getConversations('', offsetIndex, loadCount, filterID);
});
const result = await getConversationPromise;
if(this.filterID != filterID) {
return;
}
//console.timeEnd('getDialogs time');
// * loaded all
//if(!result.dialogs.length || this.chatList.childElementCount == result.count) {
// !result.dialogs.length не подходит, так как при супердревном диалоге getConversations его не выдаст.
//if(this.chatList.childElementCount == result.count) {
if(side == 'bottom') {
if(result.isEnd) {
this.scroll.loadedAll[side] = true;
}
} else {
const storage = appMessagesManager.dialogsStorage.getFolder(filterID);
if(!result.dialogs.length || (storage.length && storage[0].index < offsetIndex)) {
this.scroll.loadedAll[side] = true;
}
}
if(result.dialogs.length) {
const dialogs = side == 'top' ? result.dialogs.slice().reverse() : result.dialogs;
/* let previousScrollHeightMinusTop: number;
//if(isApple || true) {
if(isApple && side == 'top') {
const {scrollTop, scrollHeight} = this.scroll;
previousScrollHeightMinusTop = side == 'top' ? scrollHeight - scrollTop : scrollTop;
//this.scroll.scrollLocked = 1;
} */
dialogs.forEach((dialog) => {
this.addDialogNew({
dialog,
append: side == 'bottom'
});
});
/* if(previousScrollHeightMinusTop !== undefined) {
const newScrollTop = side == 'top' ? this.scroll.scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop;
// touchSupport for safari iOS
isTouchSupported && isApple && (this.scroll.container.style.overflow = 'hidden');
this.scroll.scrollTop = newScrollTop;
isTouchSupported && isApple && (this.scroll.container.style.overflow = '');
} */
//this.scroll.scrollLocked = 0;
//if(side == 'bottom' || true || (testTopSlice-- > 0 && side == 'top')) {
//setTimeout(() => {
/* const sliced = this.scroll.slice(side == 'bottom' ? 'top' : 'bottom', 30); // result.dialogs.length
sliced.forEach(el => {
const peerID = +el.getAttribute('data-peerID');
delete this.doms[peerID];
}); */
//}, 0);
//}
}
if(!this.scroll.loadedAll['top']) {
const element = this.chatList.firstElementChild;
if(element) {
const peerID = +element.getAttribute('data-peerID');
const dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
this.topOffsetIndex = dialog.index;
}
} else {
this.topOffsetIndex = 0;
}
this.log.debug('getDialogs ' + loadCount + ' dialogs by offset:', offsetIndex, result, this.chatList.childElementCount);
setTimeout(() => {
/* setTimeout(() => {
this.scroll.slice(true);
}, 100); */
this.scroll.onScroll();
}, 0);
} catch(err) {
this.log.error(err);
}
this.chatsPreloader.remove();
resolve();
});
try {
//console.time('getDialogs time');
return this.loadDialogsPromise = promise.finally(() => {
this.loadDialogsPromise = undefined;
});
}
const getConversationPromise = (this.filterID > 1 ? appUsersManager.getContacts() as Promise<any> : Promise.resolve()).then(() => {
return appMessagesManager.getConversations('', offsetIndex, loadCount, filterID);
public onChatsRegularScroll = () => {
if(this.sliceTimeout) clearTimeout(this.sliceTimeout);
this.sliceTimeout = window.setTimeout(() => {
/* const observer = new IntersectionObserver((entries) => {
const
});
this.loadDialogsPromise = getConversationPromise;
const result = await getConversationPromise;
Array.from(this.chatList.children).forEach(el => {
observer.observe(el);
}); */
if(this.filterID != filterID) {
return;
}
const scrollTopWas = this.scroll.scrollTop;
//console.timeEnd('getDialogs time');
const rect = this.scroll.container.getBoundingClientRect();
const children = Array.from(this.scroll.splitUp.children) as HTMLElement[];
const firstElement = document.elementFromPoint(rect.x, rect.y) as HTMLElement;
const lastElement = document.elementFromPoint(rect.x, rect.y + rect.height - 1) as HTMLElement;
// * loaded all
//if(!result.dialogs.length || this.chatList.childElementCount == result.count) {
// !result.dialogs.length не подходит, так как при супердревном диалоге getConversations его не выдаст.
//if(this.chatList.childElementCount == result.count) {
if(side == 'bottom') {
if(result.isEnd) {
this.scroll.loadedAll[side] = true;
}
} else {
const storage = appMessagesManager.dialogsStorage.getFolder(filterID);
if(!result.dialogs.length || (storage.length && storage[0].index < offsetIndex)) {
this.scroll.loadedAll[side] = true;
}
const firstElementRect = firstElement.getBoundingClientRect();
const elementOverflow = firstElementRect.y - rect.y;
const sliced: HTMLElement[] = [];
const firstIndex = children.indexOf(firstElement);
const lastIndex = children.indexOf(lastElement);
const saveLength = 10;
const sliceFromStart = isApple ? [] : children.slice(0, Math.max(0, firstIndex - saveLength));
const sliceFromEnd = children.slice(lastIndex + saveLength);
/* if(sliceFromStart.length != sliceFromEnd.length) {
console.log('not equal', sliceFromStart.length, sliceFromEnd.length);
}
if(result.dialogs.length) {
const dialogs = side == 'top' ? result.dialogs.slice().reverse() : result.dialogs;
dialogs.forEach((dialog) => {
this.addDialogNew({
dialog,
append: side == 'bottom'
});
});
//if(side == 'bottom' || true || (testTopSlice-- > 0 && side == 'top')) {
//setTimeout(() => {
const sliced = this.scroll.slice(side == 'bottom' ? 'top' : 'bottom', 30/* result.dialogs.length */);
sliced.forEach(el => {
const peerID = +el.getAttribute('data-peerID');
delete this.doms[peerID];
});
//}, 0);
//}
if(sliceFromStart.length > sliceFromEnd.length) {
const diff = sliceFromStart.length - sliceFromEnd.length;
sliceFromStart.splice(0, diff);
} else if(sliceFromEnd.length > sliceFromStart.length) {
const diff = sliceFromEnd.length - sliceFromStart.length;
sliceFromEnd.splice(sliceFromEnd.length - diff, diff);
} */
if(sliceFromStart.length) {
this.scroll.loadedAll['top'] = false;
}
if(!this.scroll.loadedAll['top']) {
const element = this.chatList.firstElementChild;
if(element) {
const peerID = +element.getAttribute('data-peerID');
const dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
this.topOffsetIndex = dialog.index;
}
} else {
this.topOffsetIndex = 0;
if(sliceFromEnd.length) {
this.scroll.loadedAll['bottom'] = false;
}
this.log.debug('getDialogs ' + loadCount + ' dialogs by offset:', offsetIndex, result, this.chatList.childElementCount);
sliced.push(...sliceFromStart);
sliced.push(...sliceFromEnd);
setTimeout(() => {
/* setTimeout(() => {
this.scroll.slice(true);
}, 100); */
this.scroll.onScroll();
}, 0);
} catch(err) {
this.log.error(err);
}
this.chatsPreloader.remove();
this.loadDialogsPromise = undefined;
}
sliced.forEach(el => {
el.remove();
const peerID = +el.getAttribute('data-peerID');
delete this.doms[peerID];
});
//this.log('[slicer] elements', firstElement, lastElement, rect, sliced, sliceFromStart.length, sliceFromEnd.length);
//this.log('[slicer] reset scrollTop', scrollTopWas, this.scroll.scrollTop, firstElement.offsetTop, firstElementRect.y, rect.y, elementOverflow);
this.scroll.scrollTop = firstElement.offsetTop - elementOverflow;
/* const firstElementRect = firstElement.getBoundingClientRect();
const scrollTop = */
//this.scroll.scrollIntoView(firstElement, false);
this.sliceTimeout = undefined;
}, 1e3);
};
public onChatsScrollTop = () => {
this.onChatsScroll('top');
@ -722,7 +815,7 @@ export class AppDialogsManager { @@ -722,7 +815,7 @@ export class AppDialogsManager {
public onChatsScroll = (side: SliceSides = 'bottom') => {
if(this.scroll.loadedAll[side] || this.loadDialogsPromise) return;
this.log.error('onChatsScroll', side);
this.log('onChatsScroll', side);
this.loadDialogs(side);
};

2
src/lib/appManagers/appStateManager.ts

@ -91,7 +91,7 @@ export class AppStateManager extends EventListenerBase<{ @@ -91,7 +91,7 @@ export class AppStateManager extends EventListenerBase<{
if(auth) {
// ! Warning ! DON'T delete this
this.state.authState = {_: 'authStateSignedIn'};
rootScope.broadcast('user_auth', auth);
rootScope.broadcast('user_auth', typeof(auth) !== 'number' ? (auth as any).id : auth); // * support old version
} else if(!this.state.authState) {
this.state.authState = {_: 'authStateSignIn'};
}

Loading…
Cancel
Save