Improve smoothness of opening chat on handhelds
Fix schedule send & delete Fix min schedule date
This commit is contained in:
parent
bb7c2bbe47
commit
cbd9c28d3e
@ -2408,7 +2408,8 @@ export default class ChatBubbles {
|
|||||||
//console.time('appImManager call getHistory');
|
//console.time('appImManager call getHistory');
|
||||||
const pageCount = this.appPhotosManager.windowH / 38/* * 1.25 */ | 0;
|
const pageCount = this.appPhotosManager.windowH / 38/* * 1.25 */ | 0;
|
||||||
//const loadCount = Object.keys(this.bubbles).length > 0 ? 50 : pageCount;
|
//const loadCount = Object.keys(this.bubbles).length > 0 ? 50 : pageCount;
|
||||||
const realLoadCount = Object.keys(this.bubbles).length > 0 || additionMsgId ? Math.max(40, pageCount) : pageCount;//const realLoadCount = 50;
|
//const realLoadCount = Object.keys(this.bubbles).length > 0 || additionMsgId ? Math.max(40, pageCount) : pageCount;//const realLoadCount = 50;
|
||||||
|
const realLoadCount = pageCount;//const realLoadCount = 50;
|
||||||
let loadCount = realLoadCount;
|
let loadCount = realLoadCount;
|
||||||
|
|
||||||
/* if(TEST_SCROLL) {
|
/* if(TEST_SCROLL) {
|
||||||
|
@ -191,7 +191,7 @@ export default class Chat extends EventListenerBase<{
|
|||||||
appSidebarRight.sharedMediaTab.loadSidebarMedia(false);
|
appSidebarRight.sharedMediaTab.loadSidebarMedia(false);
|
||||||
}); */
|
}); */
|
||||||
|
|
||||||
return this.setPeerPromise;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public finishPeerChange(isTarget: boolean, isJump: boolean, lastMsgId: number) {
|
public finishPeerChange(isTarget: boolean, isJump: boolean, lastMsgId: number) {
|
||||||
|
@ -113,7 +113,8 @@ export default class ChatContextMenu {
|
|||||||
const good = ['bubble', 'bubble__container', 'message', 'time', 'inner'].find(c => className.match(new RegExp(c + '($|\\s)')));
|
const good = ['bubble', 'bubble__container', 'message', 'time', 'inner'].find(c => className.match(new RegExp(c + '($|\\s)')));
|
||||||
if(good) {
|
if(good) {
|
||||||
cancelEvent(e);
|
cancelEvent(e);
|
||||||
onContextMenu((e as TouchEvent).changedTouches[0]);
|
//onContextMenu((e as TouchEvent).changedTouches[0]);
|
||||||
|
onContextMenu((e as TouchEvent).changedTouches ? (e as TouchEvent).changedTouches[0] : e as MouseEvent);
|
||||||
}
|
}
|
||||||
}, {listenerSetter: this.chat.bubbles.listenerSetter});
|
}, {listenerSetter: this.chat.bubbles.listenerSetter});
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ export default class ChatSearch {
|
|||||||
this.chat.bubbles.bubblesContainer.classList.remove('search-results-active');
|
this.chat.bubbles.bubblesContainer.classList.remove('search-results-active');
|
||||||
|
|
||||||
const res = this.chat.setPeer(peerId, lastMsgId);
|
const res = this.chat.setPeer(peerId, lastMsgId);
|
||||||
this.setPeerPromise = (res instanceof Promise ? res : Promise.resolve(res)).then(() => {
|
this.setPeerPromise = ((res instanceof Promise ? res : Promise.resolve(res)) as Promise<any>).then(() => {
|
||||||
this.selectedIndex = index;
|
this.selectedIndex = index;
|
||||||
this.foundCountEl.innerText = `${index + 1} of ${this.foundCount}`;
|
this.foundCountEl.innerText = `${index + 1} of ${this.foundCount}`;
|
||||||
|
|
||||||
|
@ -271,22 +271,24 @@ export default class ChatTopbar {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.chat.addListener('setPeer', (mid, isTopMessage) => {
|
if(this.pinnedMessage) {
|
||||||
const middleware = this.chat.bubbles.getMiddleware();
|
this.chat.addListener('setPeer', (mid, isTopMessage) => {
|
||||||
appStateManager.getState().then((state) => {
|
const middleware = this.chat.bubbles.getMiddleware();
|
||||||
if(!middleware()) return;
|
appStateManager.getState().then((state) => {
|
||||||
|
if(!middleware()) return;
|
||||||
this.pinnedMessage.hidden = !!state.hiddenPinnedMessages[this.chat.peerId];
|
|
||||||
|
this.pinnedMessage.hidden = !!state.hiddenPinnedMessages[this.chat.peerId];
|
||||||
if(isTopMessage) {
|
|
||||||
this.pinnedMessage.unsetScrollDownListener();
|
if(isTopMessage) {
|
||||||
this.pinnedMessage.testMid(mid, 0); // * because slider will not let get bubble by document.elementFromPoint
|
this.pinnedMessage.unsetScrollDownListener();
|
||||||
} else if(!this.pinnedMessage.locked) {
|
this.pinnedMessage.testMid(mid, 0); // * because slider will not let get bubble by document.elementFromPoint
|
||||||
this.pinnedMessage.handleFollowingPinnedMessage();
|
} else if(!this.pinnedMessage.locked) {
|
||||||
this.pinnedMessage.testMid(mid);
|
this.pinnedMessage.handleFollowingPinnedMessage();
|
||||||
}
|
this.pinnedMessage.testMid(mid);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
this.setPeerStatusInterval = window.setInterval(this.setPeerStatus, 60e3);
|
this.setPeerStatusInterval = window.setInterval(this.setPeerStatus, 60e3);
|
||||||
|
|
||||||
|
@ -2,9 +2,8 @@ import Countries, { Country, PhoneCodesMain } from "../countries";
|
|||||||
import { cancelEvent, CLICK_EVENT_NAME } from "../helpers/dom";
|
import { cancelEvent, CLICK_EVENT_NAME } from "../helpers/dom";
|
||||||
import ListenerSetter from "../helpers/listenerSetter";
|
import ListenerSetter from "../helpers/listenerSetter";
|
||||||
import mediaSizes from "../helpers/mediaSizes";
|
import mediaSizes from "../helpers/mediaSizes";
|
||||||
import { clamp } from "../helpers/number";
|
|
||||||
import { isTouchSupported } from "../helpers/touchSupport";
|
import { isTouchSupported } from "../helpers/touchSupport";
|
||||||
import { isApple, isSafari } from "../helpers/userAgent";
|
import { isApple } from "../helpers/userAgent";
|
||||||
|
|
||||||
export const loadedURLs: {[url: string]: boolean} = {};
|
export const loadedURLs: {[url: string]: boolean} = {};
|
||||||
const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => {
|
const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => {
|
||||||
|
@ -45,6 +45,10 @@ export default class PopupDatePicker extends PopupElement {
|
|||||||
|
|
||||||
this.minDate = options.minDate || new Date('2013-08-01T00:00:00');
|
this.minDate = options.minDate || new Date('2013-08-01T00:00:00');
|
||||||
|
|
||||||
|
if(initDate < this.minDate) {
|
||||||
|
initDate.setFullYear(this.minDate.getFullYear(), this.minDate.getMonth(), this.minDate.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
// Controls
|
// Controls
|
||||||
this.controlsDiv = document.createElement('div');
|
this.controlsDiv = document.createElement('div');
|
||||||
this.controlsDiv.classList.add('date-picker-controls');
|
this.controlsDiv.classList.add('date-picker-controls');
|
||||||
|
@ -2,8 +2,8 @@ import PopupDatePicker from "./datePicker";
|
|||||||
|
|
||||||
const getMinDate = () => {
|
const getMinDate = () => {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
date.setDate(date.getDate() - 1);
|
//date.setDate(date.getDate() - 1);
|
||||||
//date.setHours(0, 0, 0, 0);
|
date.setHours(0, 0, 0, 0);
|
||||||
return date;
|
return date;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -154,7 +154,9 @@ export default class Scrollable extends ScrollableBase {
|
|||||||
|
|
||||||
//return;
|
//return;
|
||||||
|
|
||||||
if(this.onScrollMeasure || ((this.scrollLocked || (!this.onScrolledTop && !this.onScrolledBottom)) && !this.splitUp && !this.onAdditionalScroll)) return;
|
//if(this.onScrollMeasure || ((this.scrollLocked || (!this.onScrolledTop && !this.onScrolledBottom)) && !this.splitUp && !this.onAdditionalScroll)) return;
|
||||||
|
if((this.scrollLocked || (!this.onScrolledTop && !this.onScrolledBottom)) && !this.splitUp && !this.onAdditionalScroll) return;
|
||||||
|
if(this.onScrollMeasure) window.cancelAnimationFrame(this.onScrollMeasure);
|
||||||
this.onScrollMeasure = window.requestAnimationFrame(() => {
|
this.onScrollMeasure = window.requestAnimationFrame(() => {
|
||||||
this.onScrollMeasure = 0;
|
this.onScrollMeasure = 0;
|
||||||
|
|
||||||
|
@ -559,7 +559,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
|||||||
if(message?.media?.preloader) { // means upload
|
if(message?.media?.preloader) { // means upload
|
||||||
message.media.preloader.attach(container);
|
message.media.preloader.attach(container);
|
||||||
} else if(!cacheContext.downloaded) {
|
} else if(!cacheContext.downloaded) {
|
||||||
preloader = new ProgressivePreloader(container, false, false, photo._ == 'document' ? 'prepend' : 'append');
|
preloader = new ProgressivePreloader(null, false, false, photo._ == 'document' ? 'prepend' : 'append');
|
||||||
}
|
}
|
||||||
|
|
||||||
const load = () => {
|
const load = () => {
|
||||||
|
@ -466,13 +466,14 @@ export function blurActiveElement() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CLICK_EVENT_NAME = isTouchSupported ? 'touchend' : 'click';
|
export const CLICK_EVENT_NAME: 'mousedown' | 'touchend' | 'click' = (isTouchSupported ? 'mousedown' : 'click') as any;
|
||||||
export type AttachClickOptions = AddEventListenerOptions & Partial<{listenerSetter: ListenerSetter, touchMouseDown: true}>;
|
export type AttachClickOptions = AddEventListenerOptions & Partial<{listenerSetter: ListenerSetter, touchMouseDown: true}>;
|
||||||
export const attachClickEvent = (elem: HTMLElement, callback: (e: TouchEvent | MouseEvent) => void, options: AttachClickOptions = {}) => {
|
export const attachClickEvent = (elem: HTMLElement, callback: (e: TouchEvent | MouseEvent) => void, options: AttachClickOptions = {}) => {
|
||||||
const add = options.listenerSetter ? options.listenerSetter.add.bind(options.listenerSetter, elem) : elem.addEventListener.bind(elem);
|
const add = options.listenerSetter ? options.listenerSetter.add.bind(options.listenerSetter, elem) : elem.addEventListener.bind(elem);
|
||||||
const remove = options.listenerSetter ? options.listenerSetter.removeManual.bind(options.listenerSetter, elem) : elem.removeEventListener.bind(elem);
|
const remove = options.listenerSetter ? options.listenerSetter.removeManual.bind(options.listenerSetter, elem) : elem.removeEventListener.bind(elem);
|
||||||
|
|
||||||
if(options.touchMouseDown && CLICK_EVENT_NAME === 'touchend') {
|
options.touchMouseDown = true;
|
||||||
|
/* if(options.touchMouseDown && CLICK_EVENT_NAME === 'touchend') {
|
||||||
add('mousedown', callback, options);
|
add('mousedown', callback, options);
|
||||||
} else if(CLICK_EVENT_NAME === 'touchend') {
|
} else if(CLICK_EVENT_NAME === 'touchend') {
|
||||||
const o = {...options, once: true};
|
const o = {...options, once: true};
|
||||||
@ -498,7 +499,8 @@ export const attachClickEvent = (elem: HTMLElement, callback: (e: TouchEvent | M
|
|||||||
add('touchstart', onTouchStart);
|
add('touchstart', onTouchStart);
|
||||||
} else {
|
} else {
|
||||||
add(CLICK_EVENT_NAME, callback, options);
|
add(CLICK_EVENT_NAME, callback, options);
|
||||||
}
|
} */
|
||||||
|
add(CLICK_EVENT_NAME, callback, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const detachClickEvent = (elem: HTMLElement, callback: (e: TouchEvent | MouseEvent) => void, options?: AddEventListenerOptions) => {
|
export const detachClickEvent = (elem: HTMLElement, callback: (e: TouchEvent | MouseEvent) => void, options?: AddEventListenerOptions) => {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { ArgumentTypes } from "../types";
|
import type { ArgumentTypes, SuperReturnType } from "../types";
|
||||||
|
|
||||||
export default class EventListenerBase<Listeners extends {[name: string]: Function}> {
|
export default class EventListenerBase<Listeners extends {[name: string]: Function}> {
|
||||||
protected listeners: Partial<{
|
protected listeners: Partial<{
|
||||||
@ -36,14 +36,17 @@ export default class EventListenerBase<Listeners extends {[name: string]: Functi
|
|||||||
this.listenerResults[name] = args;
|
this.listenerResults[name] = args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const arr: Array<SuperReturnType<Listeners[typeof name]>> = [];
|
||||||
if(this.listeners[name]) {
|
if(this.listeners[name]) {
|
||||||
this.listeners[name].forEach(listener => {
|
this.listeners[name].forEach(listener => {
|
||||||
listener.callback(...args);
|
arr.push(listener.callback(...args));
|
||||||
|
|
||||||
if(listener.once) {
|
if(listener.once) {
|
||||||
this.removeListener(name, listener.callback);
|
this.removeListener(name, listener.callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -40,7 +40,7 @@ export class ApiUpdatesManager {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// * false for test purposes
|
// * false for test purposes
|
||||||
/* false && */appStateManager.addListener('save', () => {
|
/* false && */appStateManager.addListener('save', async() => {
|
||||||
const us = this.updatesState;
|
const us = this.updatesState;
|
||||||
appStateManager.pushToState('updates', {
|
appStateManager.pushToState('updates', {
|
||||||
seq: us.seq,
|
seq: us.seq,
|
||||||
|
@ -51,8 +51,8 @@ export class AppImManager {
|
|||||||
|
|
||||||
public setPeerPromise: Promise<void> = null;
|
public setPeerPromise: Promise<void> = null;
|
||||||
|
|
||||||
private mainColumns: HTMLElement;
|
//private mainColumns: HTMLElement;
|
||||||
public _selectTab: ReturnType<typeof horizontalMenu>;
|
//public _selectTab: ReturnType<typeof horizontalMenu>;
|
||||||
public tabId = -1;
|
public tabId = -1;
|
||||||
//private closeBtn: HTMLButtonElement;// = this.topbar.querySelector('.sidebar-close-button') as HTMLButtonElement;
|
//private closeBtn: HTMLButtonElement;// = this.topbar.querySelector('.sidebar-close-button') as HTMLButtonElement;
|
||||||
public hideRightSidebar = false;
|
public hideRightSidebar = false;
|
||||||
@ -74,8 +74,8 @@ export class AppImManager {
|
|||||||
|
|
||||||
this.log = logger('IM', LogLevels.log | LogLevels.warn | LogLevels.debug | LogLevels.error);
|
this.log = logger('IM', LogLevels.log | LogLevels.warn | LogLevels.debug | LogLevels.error);
|
||||||
|
|
||||||
this.mainColumns = this.columnEl.parentElement;
|
//this.mainColumns = this.columnEl.parentElement;
|
||||||
this._selectTab = horizontalMenu(null, this.mainColumns);
|
//this._selectTab = horizontalMenu(null, this.mainColumns);
|
||||||
this.selectTab(0);
|
this.selectTab(0);
|
||||||
|
|
||||||
window.addEventListener('blur', () => {
|
window.addEventListener('blur', () => {
|
||||||
@ -420,7 +420,7 @@ export class AppImManager {
|
|||||||
document.body.classList.remove(RIGHT_COLUMN_ACTIVE_CLASSNAME);
|
document.body.classList.remove(RIGHT_COLUMN_ACTIVE_CLASSNAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._selectTab(id, mediaSizes.isMobile);
|
//this._selectTab(id, mediaSizes.isMobile);
|
||||||
//document.body.classList.toggle(RIGHT_COLUMN_ACTIVE_CLASSNAME, id == 2);
|
//document.body.classList.toggle(RIGHT_COLUMN_ACTIVE_CLASSNAME, id == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,24 +498,29 @@ export class AppImManager {
|
|||||||
return this.setPeer(peerId, lastMsgId);
|
return this.setPeer(peerId, lastMsgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(peerId || mediaSizes.activeScreen != ScreenSize.mobile) {
|
if(peerId || mediaSizes.activeScreen !== ScreenSize.mobile) {
|
||||||
chat.setPeer(peerId, lastMsgId);
|
const result = chat.setPeer(peerId, lastMsgId);
|
||||||
|
|
||||||
|
const promise = result?.cached ? result.promise : Promise.resolve();
|
||||||
|
if(peerId) {
|
||||||
|
promise.then(() => {
|
||||||
|
//window.requestAnimationFrame(() => {
|
||||||
|
setTimeout(() => { // * setTimeout is better here
|
||||||
|
if(this.hideRightSidebar) {
|
||||||
|
appSidebarRight.toggleSidebar(true);
|
||||||
|
this.hideRightSidebar = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectTab(1);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(peerId == 0) {
|
if(!peerId) {
|
||||||
this.selectTab(0);
|
this.selectTab(0);
|
||||||
|
|
||||||
document.body.classList.add(LEFT_COLUMN_ACTIVE_CLASSNAME);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.classList.remove(LEFT_COLUMN_ACTIVE_CLASSNAME);
|
|
||||||
if(this.hideRightSidebar) {
|
|
||||||
appSidebarRight.toggleSidebar(true);
|
|
||||||
this.hideRightSidebar = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectTab(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setInnerPeer(peerId: number, lastMsgId?: number, type: ChatType = 'chat') {
|
public setInnerPeer(peerId: number, lastMsgId?: number, type: ChatType = 'chat') {
|
||||||
|
@ -227,51 +227,77 @@ export class AppMessagesManager {
|
|||||||
}
|
}
|
||||||
}); */
|
}); */
|
||||||
|
|
||||||
|
function timedChunk(items: any[], process: (...args: any[]) => any, context: any, callback: (...args: any[]) => void) {
|
||||||
|
const todo = items.slice();
|
||||||
|
|
||||||
|
const f = () => {
|
||||||
|
const start = +new Date();
|
||||||
|
|
||||||
|
do {
|
||||||
|
process.call(context, todo.shift());
|
||||||
|
} while(todo.length > 0 && (+new Date() - start < 50));
|
||||||
|
|
||||||
|
if(todo.length > 0) {
|
||||||
|
setTimeout(f, 25);
|
||||||
|
} else {
|
||||||
|
callback(items);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setTimeout(f, 25);
|
||||||
|
}
|
||||||
|
|
||||||
appStateManager.addListener('save', () => {
|
appStateManager.addListener('save', () => {
|
||||||
const messages: any[] = [];
|
const messages: any[] = [];
|
||||||
const dialogs: Dialog[] = [];
|
const dialogs: Dialog[] = [];
|
||||||
|
const items: any[] = [];
|
||||||
|
|
||||||
|
const processDialog = (dialog: MTDialog.dialog) => {
|
||||||
|
const historyStorage = this.getHistoryStorage(dialog.peerId);
|
||||||
|
const history = [].concat(historyStorage.pending, historyStorage.history);
|
||||||
|
dialog = copy(dialog);
|
||||||
|
let removeUnread = 0;
|
||||||
|
for(const mid of history) {
|
||||||
|
const message = this.getMessageByPeer(dialog.peerId, mid);
|
||||||
|
if(/* message._ != 'messageEmpty' && */!message.pFlags.is_outgoing) {
|
||||||
|
messages.push(message);
|
||||||
|
|
||||||
|
if(message.fromId != dialog.peerId) {
|
||||||
|
appStateManager.setPeer(message.fromId, appPeersManager.getPeer(message.fromId));
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.top_message = message.mid;
|
||||||
|
this.setDialogIndexByMessage(dialog, message);
|
||||||
|
|
||||||
|
break;
|
||||||
|
} else if(message.pFlags && message.pFlags.unread) {
|
||||||
|
++removeUnread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(removeUnread && dialog.unread_count) dialog.unread_count -= removeUnread;
|
||||||
|
|
||||||
|
dialog.unread_count = Math.max(0, dialog.unread_count);
|
||||||
|
dialogs.push(dialog);
|
||||||
|
|
||||||
|
appStateManager.setPeer(dialog.peerId, appPeersManager.getPeer(dialog.peerId));
|
||||||
|
};
|
||||||
|
|
||||||
for(const folderId in this.dialogsStorage.byFolders) {
|
for(const folderId in this.dialogsStorage.byFolders) {
|
||||||
const folder = this.dialogsStorage.getFolder(+folderId);
|
const folder = this.dialogsStorage.getFolder(+folderId);
|
||||||
|
|
||||||
for(let dialog of folder) {
|
for(let dialog of folder) {
|
||||||
const historyStorage = this.getHistoryStorage(dialog.peerId);
|
items.push(dialog);
|
||||||
const history = [].concat(historyStorage.pending, historyStorage.history);
|
|
||||||
|
|
||||||
dialog = copy(dialog);
|
|
||||||
let removeUnread = 0;
|
|
||||||
for(const mid of history) {
|
|
||||||
const message = this.getMessageByPeer(dialog.peerId, mid);
|
|
||||||
if(/* message._ != 'messageEmpty' && */!message.pFlags.is_outgoing) {
|
|
||||||
messages.push(message);
|
|
||||||
|
|
||||||
if(message.fromId != dialog.peerId) {
|
|
||||||
appStateManager.setPeer(message.fromId, appPeersManager.getPeer(message.fromId));
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.top_message = message.mid;
|
|
||||||
this.setDialogIndexByMessage(dialog, message);
|
|
||||||
|
|
||||||
break;
|
|
||||||
} else if(message.pFlags && message.pFlags.unread) {
|
|
||||||
++removeUnread;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(removeUnread && dialog.unread_count) dialog.unread_count -= removeUnread;
|
|
||||||
|
|
||||||
dialog.unread_count = Math.max(0, dialog.unread_count);
|
|
||||||
dialogs.push(dialog);
|
|
||||||
|
|
||||||
appStateManager.setPeer(dialog.peerId, appPeersManager.getPeer(dialog.peerId));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appStateManager.pushToState('dialogs', dialogs);
|
return new Promise((resolve => timedChunk(items, processDialog, this, resolve))).then(() => {
|
||||||
appStateManager.pushToState('messages', messages);
|
appStateManager.pushToState('dialogs', dialogs);
|
||||||
appStateManager.pushToState('filters', this.filtersStorage.filters);
|
appStateManager.pushToState('messages', messages);
|
||||||
appStateManager.pushToState('allDialogsLoaded', this.dialogsStorage.allDialogsLoaded);
|
appStateManager.pushToState('filters', this.filtersStorage.filters);
|
||||||
appStateManager.pushToState('maxSeenMsgId', this.maxSeenId);
|
appStateManager.pushToState('allDialogsLoaded', this.dialogsStorage.allDialogsLoaded);
|
||||||
|
appStateManager.pushToState('maxSeenMsgId', this.maxSeenId);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
appStateManager.getState().then(state => {
|
appStateManager.getState().then(state => {
|
||||||
@ -3187,6 +3213,7 @@ export class AppMessagesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public readMessages(peerId: number, msgIds: number[]) {
|
public readMessages(peerId: number, msgIds: number[]) {
|
||||||
|
msgIds = msgIds.map(mid => this.getLocalMessageId(mid));
|
||||||
if(peerId < 0 && appPeersManager.isChannel(peerId)) {
|
if(peerId < 0 && appPeersManager.isChannel(peerId)) {
|
||||||
const channelId = -peerId;
|
const channelId = -peerId;
|
||||||
apiManager.invokeApi('channels.readMessageContents', {
|
apiManager.invokeApi('channels.readMessageContents', {
|
||||||
@ -4113,7 +4140,7 @@ export class AppMessagesManager {
|
|||||||
public sendScheduledMessages(peerId: number, mids: number[]) {
|
public sendScheduledMessages(peerId: number, mids: number[]) {
|
||||||
return apiManager.invokeApi('messages.sendScheduledMessages', {
|
return apiManager.invokeApi('messages.sendScheduledMessages', {
|
||||||
peer: appPeersManager.getInputPeerById(peerId),
|
peer: appPeersManager.getInputPeerById(peerId),
|
||||||
id: mids
|
id: mids.map(mid => this.getLocalMessageId(mid))
|
||||||
}).then(updates => {
|
}).then(updates => {
|
||||||
apiUpdatesManager.processUpdateMessage(updates);
|
apiUpdatesManager.processUpdateMessage(updates);
|
||||||
});
|
});
|
||||||
@ -4122,7 +4149,7 @@ export class AppMessagesManager {
|
|||||||
public deleteScheduledMessages(peerId: number, mids: number[]) {
|
public deleteScheduledMessages(peerId: number, mids: number[]) {
|
||||||
return apiManager.invokeApi('messages.deleteScheduledMessages', {
|
return apiManager.invokeApi('messages.deleteScheduledMessages', {
|
||||||
peer: appPeersManager.getInputPeerById(peerId),
|
peer: appPeersManager.getInputPeerById(peerId),
|
||||||
id: mids
|
id: mids.map(mid => this.getLocalMessageId(mid))
|
||||||
}).then(updates => {
|
}).then(updates => {
|
||||||
apiUpdatesManager.processUpdateMessage(updates);
|
apiUpdatesManager.processUpdateMessage(updates);
|
||||||
});
|
});
|
||||||
|
@ -16,13 +16,16 @@ const STATE_VERSION = App.version;
|
|||||||
|
|
||||||
type State = Partial<{
|
type State = Partial<{
|
||||||
dialogs: Dialog[],
|
dialogs: Dialog[],
|
||||||
allDialogsLoaded: DialogsStorage['allDialogsLoaded'],
|
allDialogsLoaded: DialogsStorage['allDialogsLoaded'],
|
||||||
//peers: {[peerId: string]: ReturnType<AppPeersManager['getPeer']>},
|
|
||||||
chats: {[peerId: string]: ReturnType<AppChatsManager['getChat']>},
|
chats: {[peerId: string]: ReturnType<AppChatsManager['getChat']>},
|
||||||
users: {[peerId: string]: ReturnType<AppUsersManager['getUser']>},
|
users: {[peerId: string]: ReturnType<AppUsersManager['getUser']>},
|
||||||
messages: any[],
|
messages: any[],
|
||||||
contactsList: number[],
|
contactsList: number[],
|
||||||
updates: any,
|
updates: Partial<{
|
||||||
|
seq: number,
|
||||||
|
pts: number,
|
||||||
|
date: number
|
||||||
|
}>,
|
||||||
filters: FiltersStorage['filters'],
|
filters: FiltersStorage['filters'],
|
||||||
maxSeenMsgId: number,
|
maxSeenMsgId: number,
|
||||||
stateCreatedTime: number,
|
stateCreatedTime: number,
|
||||||
@ -35,16 +38,45 @@ type State = Partial<{
|
|||||||
hiddenPinnedMessages: {[peerId: string]: number}
|
hiddenPinnedMessages: {[peerId: string]: number}
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
/* const STATE_INIT: State = {
|
||||||
|
dialogs: [],
|
||||||
|
allDialogsLoaded: {},
|
||||||
|
chats: {},
|
||||||
|
users: {},
|
||||||
|
messages: [],
|
||||||
|
contactsList: [],
|
||||||
|
updates: {},
|
||||||
|
filters: {},
|
||||||
|
maxSeenMsgId: 0,
|
||||||
|
stateCreatedTime: 0,
|
||||||
|
recentEmoji: [],
|
||||||
|
topPeers: [],
|
||||||
|
recentSearch: [],
|
||||||
|
stickerSets: {},
|
||||||
|
version: '',
|
||||||
|
authState:
|
||||||
|
}; */
|
||||||
|
|
||||||
|
const ALL_KEYS = ['dialogs', 'allDialogsLoaded', 'chats',
|
||||||
|
'users', 'messages', 'contactsList',
|
||||||
|
'updates', 'filters', 'maxSeenMsgId',
|
||||||
|
'stateCreatedTime', 'recentEmoji', 'topPeers',
|
||||||
|
'recentSearch', 'stickerSets', 'version',
|
||||||
|
'authState', 'hiddenPinnedMessages'
|
||||||
|
] as any as Array<keyof State>;
|
||||||
|
|
||||||
const REFRESH_KEYS = ['dialogs', 'allDialogsLoaded', 'messages', 'contactsList', 'stateCreatedTime',
|
const REFRESH_KEYS = ['dialogs', 'allDialogsLoaded', 'messages', 'contactsList', 'stateCreatedTime',
|
||||||
'updates', 'maxSeenMsgId', 'filters', 'topPeers'] as any as Array<keyof State>;
|
'updates', 'maxSeenMsgId', 'filters', 'topPeers'] as any as Array<keyof State>;
|
||||||
|
|
||||||
export class AppStateManager extends EventListenerBase<{
|
export class AppStateManager extends EventListenerBase<{
|
||||||
save: (state: State) => void
|
save: (state: State) => Promise<void>
|
||||||
}> {
|
}> {
|
||||||
public loaded: Promise<State>;
|
public loaded: Promise<State>;
|
||||||
private log = logger('STATE'/* , LogLevels.error */);
|
private log = logger('STATE'/* , LogLevels.error */);
|
||||||
|
|
||||||
private state: State;
|
private state: State;
|
||||||
|
private savePromise: Promise<void>;
|
||||||
|
private tempId = 0;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@ -55,7 +87,18 @@ export class AppStateManager extends EventListenerBase<{
|
|||||||
if(this.loaded) return this.loaded;
|
if(this.loaded) return this.loaded;
|
||||||
//console.time('load state');
|
//console.time('load state');
|
||||||
return this.loaded = new Promise((resolve) => {
|
return this.loaded = new Promise((resolve) => {
|
||||||
AppStorage.get<[State, UserAuth]>('state', 'user_auth').then(([state, auth]) => {
|
AppStorage.get<any>(...ALL_KEYS, 'user_auth').then((arr) => {
|
||||||
|
let state: State = {};
|
||||||
|
|
||||||
|
// ! then can't store false values
|
||||||
|
ALL_KEYS.forEach((key, idx) => {
|
||||||
|
const value = arr[idx];
|
||||||
|
if(value !== false) {
|
||||||
|
// @ts-ignore
|
||||||
|
state[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const time = Date.now();
|
const time = Date.now();
|
||||||
if(state) {
|
if(state) {
|
||||||
if(state.version != STATE_VERSION) {
|
if(state.version != STATE_VERSION) {
|
||||||
@ -84,6 +127,7 @@ export class AppStateManager extends EventListenerBase<{
|
|||||||
|
|
||||||
//return resolve();
|
//return resolve();
|
||||||
|
|
||||||
|
const auth: UserAuth = arr[arr.length - 1];
|
||||||
if(auth) {
|
if(auth) {
|
||||||
// ! Warning ! DON'T delete this
|
// ! Warning ! DON'T delete this
|
||||||
this.state.authState = {_: 'authStateSignedIn'};
|
this.state.authState = {_: 'authStateSignedIn'};
|
||||||
@ -95,7 +139,10 @@ export class AppStateManager extends EventListenerBase<{
|
|||||||
//console.timeEnd('load state');
|
//console.timeEnd('load state');
|
||||||
resolve(this.state);
|
resolve(this.state);
|
||||||
}).catch(resolve).finally(() => {
|
}).catch(resolve).finally(() => {
|
||||||
setInterval(() => this.saveState(), 10000);
|
setInterval(() => {
|
||||||
|
this.tempId++;
|
||||||
|
this.saveState();
|
||||||
|
}, 10000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -105,18 +152,26 @@ export class AppStateManager extends EventListenerBase<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public saveState() {
|
public saveState() {
|
||||||
if(this.state === undefined) return;
|
if(this.state === undefined || this.savePromise) return;
|
||||||
|
|
||||||
|
const tempId = this.tempId;
|
||||||
|
this.savePromise = Promise.all(this.setListenerResult('save', this.state)).then(() => {
|
||||||
|
return AppStorage.set(this.state);
|
||||||
|
}).then(() => {
|
||||||
|
this.savePromise = null;
|
||||||
|
|
||||||
|
if(this.tempId !== tempId) {
|
||||||
|
this.saveState();
|
||||||
|
}
|
||||||
|
});
|
||||||
//let perf = performance.now();
|
//let perf = performance.now();
|
||||||
this.setListenerResult('save', this.state);
|
|
||||||
//this.log('saveState: event time:', performance.now() - perf);
|
//this.log('saveState: event time:', performance.now() - perf);
|
||||||
|
|
||||||
//const pinnedOrders = appMessagesManager.dialogsStorage.pinnedOrders;
|
//const pinnedOrders = appMessagesManager.dialogsStorage.pinnedOrders;
|
||||||
|
|
||||||
//perf = performance.now();
|
//perf = performance.now();
|
||||||
AppStorage.set({
|
|
||||||
state: this.state
|
|
||||||
});
|
|
||||||
//this.log('saveState: storage set time:', performance.now() - perf);
|
//this.log('saveState: storage set time:', performance.now() - perf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +184,16 @@ export class AppStateManager extends EventListenerBase<{
|
|||||||
if(container.hasOwnProperty(peerId)) return;
|
if(container.hasOwnProperty(peerId)) return;
|
||||||
container[peerId] = peer;
|
container[peerId] = peer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public resetState() {
|
||||||
|
for(let i in this.state) {
|
||||||
|
// @ts-ignore
|
||||||
|
this.state[i] = false;
|
||||||
|
}
|
||||||
|
AppStorage.set(this.state).then(() => {
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.trace('appStateManager include');
|
//console.trace('appStateManager include');
|
||||||
|
@ -100,7 +100,7 @@ export class AppUsersManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
appStateManager.addListener('save', () => {
|
appStateManager.addListener('save', async() => {
|
||||||
const contactsList = [...this.contactsList];
|
const contactsList = [...this.contactsList];
|
||||||
for(const userId of contactsList) {
|
for(const userId of contactsList) {
|
||||||
appStateManager.setPeer(userId, this.getUser(userId));
|
appStateManager.setPeer(userId, this.getUser(userId));
|
||||||
|
@ -13,7 +13,7 @@ function dT() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function logger(prefix: string, level = LogLevels.log | LogLevels.warn | LogLevels.error) {
|
export function logger(prefix: string, level = LogLevels.log | LogLevels.warn | LogLevels.error) {
|
||||||
if(process.env.NODE_ENV != 'development'/* || true */) {
|
if(process.env.NODE_ENV != 'development' || true) {
|
||||||
level = LogLevels.error;
|
level = LogLevels.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,9 +70,9 @@ export class ProgressLine {
|
|||||||
this.container.addEventListener('mouseup', this.onMouseUp);
|
this.container.addEventListener('mouseup', this.onMouseUp);
|
||||||
|
|
||||||
if(isTouchSupported) {
|
if(isTouchSupported) {
|
||||||
this.container.addEventListener('touchmove', this.onMouseMove);
|
this.container.addEventListener('touchmove', this.onMouseMove, {passive: true});
|
||||||
this.container.addEventListener('touchstart', this.onMouseDown);
|
this.container.addEventListener('touchstart', this.onMouseDown, {passive: true});
|
||||||
this.container.addEventListener('touchend', this.onMouseUp);
|
this.container.addEventListener('touchend', this.onMouseUp, {passive: true});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,6 @@ import { MOUNT_CLASS_TO } from './mtproto/mtproto_config';
|
|||||||
//import { stringify } from '../helpers/json';
|
//import { stringify } from '../helpers/json';
|
||||||
|
|
||||||
class AppStorage {
|
class AppStorage {
|
||||||
//private log = (...args: any[]) => console.log('[SW LS]', ...args);
|
|
||||||
private log = (...args: any[]) => {};
|
|
||||||
|
|
||||||
private cacheStorage = new CacheStorageController('session');
|
private cacheStorage = new CacheStorageController('session');
|
||||||
|
|
||||||
//public noPrefix = false;
|
//public noPrefix = false;
|
||||||
@ -66,24 +63,23 @@ class AppStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async set(obj: any) {
|
public async set(obj: any) {
|
||||||
let keyValues: any = {};
|
const prefix = this.storageGetPrefix();
|
||||||
let prefix = this.storageGetPrefix(),
|
|
||||||
key, value;
|
|
||||||
|
|
||||||
//console.log('storageSetValue', obj, callback, arguments);
|
//console.log('storageSetValue', obj, callback, arguments);
|
||||||
|
|
||||||
for(key in obj) {
|
for(let key in obj) {
|
||||||
if(obj.hasOwnProperty(key)) {
|
if(obj.hasOwnProperty(key)) {
|
||||||
value = obj[key];
|
let value = obj[key];
|
||||||
key = prefix + key;
|
key = prefix + key;
|
||||||
this.cache[key] = value;
|
this.cache[key] = value;
|
||||||
|
|
||||||
|
let perf = performance.now();
|
||||||
value = JSON.stringify(value);
|
value = JSON.stringify(value);
|
||||||
/* let perf = performance.now();
|
|
||||||
let value2 = JSON.stringify(value);
|
|
||||||
console.log('LocalStorage set: stringify time by JSON.stringify:', performance.now() - perf, value2);
|
|
||||||
|
|
||||||
perf = performance.now();
|
let elapsedTime = performance.now() - perf;
|
||||||
|
if(elapsedTime > 10) {
|
||||||
|
console.warn('LocalStorage set: stringify time by JSON.stringify:', elapsedTime, key);
|
||||||
|
}
|
||||||
|
/* perf = performance.now();
|
||||||
value = stringify(value);
|
value = stringify(value);
|
||||||
console.log('LocalStorage set: stringify time by own stringify:', performance.now() - perf); */
|
console.log('LocalStorage set: stringify time by own stringify:', performance.now() - perf); */
|
||||||
|
|
||||||
@ -97,8 +93,6 @@ class AppStorage {
|
|||||||
//this.useCS = false;
|
//this.useCS = false;
|
||||||
console.error('[AS]: set error:', e, key/* , value */);
|
console.error('[AS]: set error:', e, key/* , value */);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
keyValues[key] = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,11 +103,9 @@ class AppStorage {
|
|||||||
keys = Array.prototype.slice.call(arguments);
|
keys = Array.prototype.slice.call(arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
let prefix = this.storageGetPrefix(),
|
const prefix = this.storageGetPrefix();
|
||||||
i, key;
|
for(let i = 0; i < keys.length; i++) {
|
||||||
|
const key = keys[i] = prefix + keys[i];
|
||||||
for(i = 0; i < keys.length; i++) {
|
|
||||||
key = keys[i] = prefix + keys[i];
|
|
||||||
delete this.cache[key];
|
delete this.cache[key];
|
||||||
if(this.useCS) {
|
if(this.useCS) {
|
||||||
try {
|
try {
|
||||||
|
@ -22,6 +22,8 @@ $chat-helper-size: 39px;
|
|||||||
transition: transform var(--layer-transition);
|
transition: transform var(--layer-transition);
|
||||||
transform: translateY(var(--translateY));
|
transform: translateY(var(--translateY));
|
||||||
|
|
||||||
|
//display: none !important;
|
||||||
|
|
||||||
/* // * for no ESG top
|
/* // * for no ESG top
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
height: calc(100% - 56px); */
|
height: calc(100% - 56px); */
|
||||||
@ -407,6 +409,17 @@ $chat-helper-size: 39px;
|
|||||||
position: relative;
|
position: relative;
|
||||||
flex: 3;
|
flex: 3;
|
||||||
|
|
||||||
|
@include respond-to(handhelds) {
|
||||||
|
body.is-left-column-shown & {
|
||||||
|
transform: translate3d(100vw, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.is-right-column-shown & {
|
||||||
|
transform: translate3d(-25vw, 0, 0);
|
||||||
|
filter: brightness(80%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include respond-to(floating-left-sidebar) {
|
@include respond-to(floating-left-sidebar) {
|
||||||
position: fixed !important;
|
position: fixed !important;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -9,6 +9,11 @@
|
|||||||
@include respond-to(handhelds) {
|
@include respond-to(handhelds) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
||||||
|
body:not(.is-left-column-shown) & {
|
||||||
|
transform: translate3d(-25vw, 0, 0);
|
||||||
|
filter: brightness(80%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include respond-to(floating-left-sidebar) {
|
@include respond-to(floating-left-sidebar) {
|
||||||
|
@ -5,6 +5,12 @@
|
|||||||
box-shadow: 0 0.25rem 0.5rem 0.1rem hsla(0, 0%, 44.7%, .25);
|
box-shadow: 0 0.25rem 0.5rem 0.1rem hsla(0, 0%, 44.7%, .25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include respond-to(handhelds) {
|
||||||
|
body:not(.is-right-column-shown) & {
|
||||||
|
transform: translate3d(100vw, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include respond-to(not-handhelds) {
|
@include respond-to(not-handhelds) {
|
||||||
width: var(--right-column-width);
|
width: var(--right-column-width);
|
||||||
transition: transform var(--layer-transition);
|
transition: transform var(--layer-transition);
|
||||||
|
@ -24,10 +24,12 @@
|
|||||||
@include respond-to(handhelds) {
|
@include respond-to(handhelds) {
|
||||||
.main-column {
|
.main-column {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
display: flex !important;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
&:not(.active) {
|
/* &:not(.active) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #column-left {
|
/* #column-left {
|
||||||
|
@ -177,7 +177,7 @@
|
|||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
//opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
src/types.d.ts
vendored
1
src/types.d.ts
vendored
@ -34,6 +34,7 @@ export type Modify<T, R> = Omit<T, keyof R> & R;
|
|||||||
//export type Parameters<T> = T extends (... args: infer T) => any ? T : never;
|
//export type Parameters<T> = T extends (... args: infer T) => any ? T : never;
|
||||||
|
|
||||||
export type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any ? A : never;
|
export type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any ? A : never;
|
||||||
|
export type SuperReturnType<F extends Function> = F extends (...args: any) => any ? ReturnType<F> : never;
|
||||||
|
|
||||||
export type AnyLiteral = Record<string, any>;
|
export type AnyLiteral = Record<string, any>;
|
||||||
export type AnyClass = new (...args: any[]) => any;
|
export type AnyClass = new (...args: any[]) => any;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user