diff --git a/server.js b/server.js index f204a0da..7fa3ae1c 100644 --- a/server.js +++ b/server.js @@ -25,7 +25,7 @@ const server = useHttp ? http : https; let options = {}; if(!useHttp) { options.key = fs.readFileSync(__dirname + '/certs/server-key.pem'); - options.cert = s.readFileSync(__dirname + '/certs/server-cert.pem'); + options.cert = fs.readFileSync(__dirname + '/certs/server-cert.pem'); } server.createServer(options, app).listen(port, () => { diff --git a/src/components/popups/deleteDialog.ts b/src/components/popups/deleteDialog.ts index 905066ff..6a17c5f5 100644 --- a/src/components/popups/deleteDialog.ts +++ b/src/components/popups/deleteDialog.ts @@ -24,7 +24,10 @@ export default class PopupDeleteDialog { }; */ const callbackLeave = (checked: PopupPeerButtonCallbackCheckboxes) => { - const promise = appChatsManager.leave(-peerId); + const promise = appChatsManager.leave(-peerId).then(() => { + return appMessagesManager.flushHistory(-peerId); + }); + onSelect && onSelect(promise); }; @@ -37,7 +40,7 @@ export default class PopupDeleteDialog { if(checked[checkboxes[0].text]) { promise = appChatsManager.delete(-peerId); } else { - promise = appChatsManager.leave(-peerId); + return callbackLeave(checked); } } diff --git a/src/index.ts b/src/index.ts index 9b831459..c0d8d940 100644 --- a/src/index.ts +++ b/src/index.ts @@ -248,6 +248,11 @@ console.timeEnd('get storage1'); */ //import('./vendor/dateFormat'); const langPromise = I18n.default.getCacheLangPack(); + + function loadFonts(): Promise { + // @ts-ignore + return 'fonts' in document ? Promise.all(['400 1rem Roboto', '500 1rem Roboto'].map(font => document.fonts.load(font))) : Promise.resolve(); + } const [state, langPack] = await Promise.all([ appStateManager.default.getState(), @@ -263,6 +268,16 @@ console.timeEnd('get storage1'); */ I18n.default.getLangPack(langPack.lang_code); } + function fadeInWhenFontsReady(elem: HTMLElement, promise: Promise) { + elem.style.opacity = '0'; + + promise.then(() => { + window.requestAnimationFrame(() => { + elem.style.opacity = ''; + }); + }); + } + console.log('got state, time:', performance.now() - perf); const authState = state.authState; @@ -271,11 +286,14 @@ console.timeEnd('get storage1'); */ const el = document.getElementById('auth-pages'); if(el) { - const scrollable = el.querySelector('.scrollable'); + const scrollable = el.querySelector('.scrollable') as HTMLElement; if((!touchSupport.isTouchSupported || isMobileSafari)) { scrollable.classList.add('no-scrollbar'); } + // @ts-ignore + fadeInWhenFontsReady(scrollable, 'fonts' in document ? document.fonts.ready : Promise.resolve()); + const placeholder = document.createElement('div'); placeholder.classList.add('auth-placeholder'); @@ -348,6 +366,7 @@ console.timeEnd('get storage1'); */ }, 500); */ } else { console.log('Will mount IM page:', Date.now() / 1000); + fadeInWhenFontsReady(document.getElementById('main-columns'), loadFonts()); (await import('./pages/pageIm')).default.mount(); //getNearestDc(); } diff --git a/src/lib/appManagers/apiUpdatesManager.ts b/src/lib/appManagers/apiUpdatesManager.ts index 64194320..8592787a 100644 --- a/src/lib/appManagers/apiUpdatesManager.ts +++ b/src/lib/appManagers/apiUpdatesManager.ts @@ -11,16 +11,15 @@ //import apiManager from '../mtproto/apiManager'; import DEBUG, { MOUNT_CLASS_TO } from '../../config/debug'; -import { copy } from '../../helpers/object'; import { Update } from '../../layer'; import { logger, LogTypes } from '../logger'; import apiManager from '../mtproto/mtprotoworker'; import rootScope from '../rootScope'; //import networkerFactory from '../mtproto/networkerFactory'; +import appUsersManager from "./appUsersManager"; import appChatsManager from "./appChatsManager"; import appPeersManager from "./appPeersManager"; import appStateManager from './appStateManager'; -import appUsersManager from "./appUsersManager"; type UpdatesState = { pendingPtsUpdates: {pts: number, pts_count: number}[], @@ -51,7 +50,7 @@ export class ApiUpdatesManager { public channelStates: {[channelId: number]: UpdatesState} = {}; private attached = false; - private log = logger('UPDATES', LogTypes.Error | LogTypes.Warn/* | LogTypes.Log | LogTypes.Debug */); + private log = logger('UPDATES', LogTypes.Error | LogTypes.Warn | LogTypes.Log/* | LogTypes.Debug */); private debug = DEBUG; private setProxy() { @@ -60,17 +59,21 @@ export class ApiUpdatesManager { set: function(target: ApiUpdatesManager['updatesState'], key: keyof ApiUpdatesManager['updatesState'], value: ApiUpdatesManager['updatesState'][typeof key]) { // @ts-ignore target[key] = value; - const us = self.updatesState; - appStateManager.pushToState('updates', { - seq: us.seq, - pts: us.pts, - date: us.date - }); + self.saveUpdatesState(); return true; } }); } + public saveUpdatesState() { + const us = this.updatesState; + appStateManager.pushToState('updates', { + seq: us.seq, + pts: us.pts, + date: us.date + }); + } + private popPendingSeqUpdate() { const state = this.updatesState; const nextSeq = state.seq + 1; @@ -132,7 +135,7 @@ export class ApiUpdatesManager { return false; } - this.debug && this.log('pop pending pts updates', goodPts, curState.pendingPtsUpdates.slice(0, goodIndex + 1)); + this.debug && this.log.debug('pop pending pts updates', goodPts, curState.pendingPtsUpdates.slice(0, goodIndex + 1)); curState.pts = goodPts; for(let i = 0; i <= goodIndex; ++i) { @@ -172,7 +175,7 @@ export class ApiUpdatesManager { //ignoreSyncLoading: options.ignoreSyncLoading }; - this.debug && this.log('processUpdateMessage', updateMessage); + this.debug && this.log.debug('processUpdateMessage', updateMessage); switch(updateMessage._) { case 'updatesTooLong': @@ -186,7 +189,7 @@ export class ApiUpdatesManager { case 'updateShortMessage': case 'updateShortChatMessage': { - this.debug && this.log('updateShortMessage | updateShortChatMessage', {...updateMessage}); + this.debug && this.log.debug('updateShortMessage | updateShortChatMessage', {...updateMessage}); const isOut = updateMessage.pFlags.out; const fromId = updateMessage.from_id || (isOut ? rootScope.myId : updateMessage.user_id); const toId = updateMessage.chat_id @@ -249,10 +252,10 @@ export class ApiUpdatesManager { }, { timeout: 0x7fffffff }).then((differenceResult) => { - this.debug && this.log('Get diff result', differenceResult); + this.debug && this.log.debug('Get diff result', differenceResult); if(differenceResult._ === 'updates.differenceEmpty') { - this.debug && this.log('apply empty diff', differenceResult.seq); + this.debug && this.log.debug('apply empty diff', differenceResult.seq); updatesState.date = differenceResult.date; updatesState.seq = differenceResult.seq; return; @@ -307,7 +310,7 @@ export class ApiUpdatesManager { if(differenceResult._ === 'updates.differenceSlice') { return this.getDifference(); } else { - this.debug && this.log('finished get diff'); + this.debug && this.log.debug('finished get diff'); } }); @@ -337,16 +340,16 @@ export class ApiUpdatesManager { pts: channelState.pts, limit: 30 }, {timeout: 0x7fffffff}).then((differenceResult) => { - this.debug && this.log('Get channel diff result', differenceResult) + this.debug && this.log.debug('Get channel diff result', differenceResult) channelState.pts = 'pts' in differenceResult ? differenceResult.pts : undefined; if(differenceResult._ === 'updates.channelDifferenceEmpty') { - this.debug && this.log('apply channel empty diff', differenceResult); + this.debug && this.log.debug('apply channel empty diff', differenceResult); return; } if(differenceResult._ === 'updates.channelDifferenceTooLong') { - this.debug && this.log('channel diff too long', differenceResult); + this.debug && this.log.debug('channel diff too long', differenceResult); delete this.channelStates[channelId]; // @ts-ignore @@ -358,12 +361,12 @@ export class ApiUpdatesManager { appChatsManager.saveApiChats(differenceResult.chats); // Should be first because of updateMessageID - this.debug && this.log('applying', differenceResult.other_updates.length, 'channel other updates'); + this.debug && this.log.debug('applying', differenceResult.other_updates.length, 'channel other updates'); differenceResult.other_updates.forEach((update) => { this.saveUpdate(update); }); - this.debug && this.log('applying', differenceResult.new_messages.length, 'channel new messages'); + this.debug && this.log.debug('applying', differenceResult.new_messages.length, 'channel new messages'); differenceResult.new_messages.forEach((apiMessage) => { this.saveUpdate({ _: 'updateNewChannelMessage', @@ -373,13 +376,13 @@ export class ApiUpdatesManager { }); }); - this.debug && this.log('apply channel diff', channelState.pts); + this.debug && this.log.debug('apply channel diff', channelState.pts); if(differenceResult._ === 'updates.channelDifference' && !differenceResult.pFlags['final']) { return this.getChannelDifference(channelId); } else { - this.debug && this.log('finished channel get diff'); + this.debug && this.log.debug('finished channel get diff'); } }); @@ -593,7 +596,7 @@ export class ApiUpdatesManager { } public saveUpdate(update: Update) { - this.log('saveUpdate', update); + this.debug && this.log('saveUpdate', update); rootScope.dispatchEvent(update._, update as any); } @@ -618,6 +621,7 @@ export class ApiUpdatesManager { this.updatesState.seq = stateResult.seq; this.updatesState.pts = stateResult.pts; this.updatesState.date = stateResult.date; + this.saveUpdatesState(); //setTimeout(() => { this.updatesState.syncLoading = null; resolve(); @@ -639,7 +643,7 @@ export class ApiUpdatesManager { Object.assign(this.updatesState, state); - this.log('will get difference', copy(state)); + this.log('will get difference', Object.assign({}, state)); this.getDifference(true)/* .finally(() => { if(this.updatesState.syncLoading) { diff --git a/src/lib/appManagers/appChatsManager.ts b/src/lib/appManagers/appChatsManager.ts index 9af2a1f5..01e702da 100644 --- a/src/lib/appManagers/appChatsManager.ts +++ b/src/lib/appManagers/appChatsManager.ts @@ -19,7 +19,6 @@ import apiManager from '../mtproto/mtprotoworker'; import { RichTextProcessor } from "../richtextprocessor"; import rootScope from "../rootScope"; import apiUpdatesManager from "./apiUpdatesManager"; -import appMessagesManager from "./appMessagesManager"; import appPeersManager from "./appPeersManager"; import appProfileManager from "./appProfileManager"; import appStateManager from "./appStateManager"; @@ -637,12 +636,8 @@ export class AppChatsManager { }).then(this.onChatUpdated.bind(this, id)); } - public leaveChat(id: number, flushHistory = true) { - let promise: Promise = this.deleteChatUser(id, appUsersManager.getSelf().id) - if(flushHistory) promise = promise.then(() => { - return appMessagesManager.flushHistory(-id); - }); - return promise;; + public leaveChat(id: number) { + return this.deleteChatUser(id, appUsersManager.getSelf().id); } public leave(id: number) { diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index d01eb82b..b64964a4 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -213,7 +213,7 @@ export class AppDialogsManager { title: HTMLElement } } = {}; - private showFiltersTimeout: number; + private showFiltersPromise: Promise; private allUnreadCount: HTMLElement; private accumulateArchivedTimeout: number; @@ -491,16 +491,22 @@ export class AppDialogsManager { //selectTab(0); (this.folders.menu.firstElementChild as HTMLElement).click(); appMessagesManager.construct(); - appStateManager.getState().then((state) => { + appStateManager.getState().then(async(state) => { appNotificationsManager.getNotifyPeerTypeSettings(); - const getFiltersPromise = appMessagesManager.filtersStorage.getDialogFilters(); - getFiltersPromise.then((filters) => { + const renderFiltersPromise = appMessagesManager.filtersStorage.getDialogFilters().then((filters) => { for(const filter of filters) { this.addFilter(filter); } }); + if(state.filters && Object.keys(state.filters).length) { + await renderFiltersPromise; + if(this.showFiltersPromise) { + await this.showFiltersPromise; + } + } + if(appStateManager.storagesResults.dialogs.length) { appDraftsManager.getAllDrafts(); appDraftsManager.addMissedDialogs(); @@ -706,12 +712,15 @@ export class AppDialogsManager { title: titleSpan }; - if(!this.showFiltersTimeout && Object.keys(this.filtersRendered).length > 1) { - this.showFiltersTimeout = window.setTimeout(() => { - this.showFiltersTimeout = 0; - this.folders.menuScrollContainer.classList.remove('hide'); - this.setFiltersUnreadCount(); - }, 0); + if(!this.showFiltersPromise && Object.keys(this.filtersRendered).length > 1) { + this.showFiltersPromise = new Promise((resolve) => { + window.setTimeout(() => { + this.showFiltersPromise = undefined; + this.folders.menuScrollContainer.classList.remove('hide'); + this.setFiltersUnreadCount(); + resolve(); + }, 0); + }); } } diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 0818a16f..d2e76614 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -305,8 +305,8 @@ export class AppMessagesManager { } public construct() { - this.dialogsStorage = new DialogsStorage(this, appChatsManager, appPeersManager, appUsersManager, appDraftsManager, appNotificationsManager, appStateManager, apiUpdatesManager, serverTimeManager); this.filtersStorage = new FiltersStorage(this, appPeersManager, appUsersManager, appNotificationsManager, appStateManager, apiUpdatesManager, /* apiManager, */ rootScope); + this.dialogsStorage = new DialogsStorage(this, appChatsManager, appPeersManager, appUsersManager, appDraftsManager, appNotificationsManager, appStateManager, apiUpdatesManager, serverTimeManager); } public getInputEntities(entities: MessageEntity[]) { @@ -2362,7 +2362,6 @@ export class AppMessagesManager { break; case 'messageActionPhoneCall': - delete message.fromId; message.action.type = (message.pFlags.out ? 'out_' : 'in_') + ( diff --git a/src/lib/idb.ts b/src/lib/idb.ts index 05f197f2..8f92f96a 100644 --- a/src/lib/idb.ts +++ b/src/lib/idb.ts @@ -38,8 +38,9 @@ export type IDBOptions = { const DEBUG = false; export default class IDBStorage { - //private static STORAGES: IDBStorage[] = []; + private static STORAGES: IDBStorage[] = []; private openDbPromise: Promise; + private db: IDBDatabase; private storageIsAvailable = true; private log: ReturnType; @@ -57,7 +58,33 @@ export default class IDBStorage { this.openDatabase(true); - //IDBStorage.STORAGES.push(this); + IDBStorage.STORAGES.push(this); + } + + public static closeDatabases() { + this.STORAGES.forEach(storage => { + const db = storage.db; + if(db) { + db.onclose = () => {}; + db.close(); + } + }); + } + + public static deleteDatabase() { + this.closeDatabases(); + + return new Promise((resolve, reject) => { + const deleteRequest = indexedDB.deleteDatabase(Database.name); + + deleteRequest.onerror = () => { + reject(); + }; + + deleteRequest.onsuccess = () => { + resolve(); + }; + }); } public isAvailable() { @@ -134,7 +161,7 @@ export default class IDBStorage { this.log.error('onversionchange, lol?'); }; - resolve(db); + resolve(this.db = db); }; request.onerror = (event) => { diff --git a/src/lib/mtproto/apiManager.ts b/src/lib/mtproto/apiManager.ts index db617149..e75d305f 100644 --- a/src/lib/mtproto/apiManager.ts +++ b/src/lib/mtproto/apiManager.ts @@ -26,6 +26,7 @@ import { bytesFromHex, bytesToHex } from '../../helpers/bytes'; import { ctx, isSafari } from '../../helpers/userAgent'; import App from '../../config/app'; import { MOUNT_CLASS_TO } from '../../config/debug'; +import IDBStorage from '../idb'; /// #if !MTPROTO_WORKER import rootScope from '../rootScope'; @@ -149,10 +150,8 @@ export class ApiManager { this.baseDcId = 0; //this.telegramMeNotify(false); - const promise = sessionStorage.clear(); - promise.finally(() => { - self.postMessage({type: 'reload'}); - }); + IDBStorage.closeDatabases(); + self.postMessage({type: 'clear'}); }; setTimeout(clear, 1e3); diff --git a/src/lib/mtproto/mtprotoworker.ts b/src/lib/mtproto/mtprotoworker.ts index cc0e5c6f..a8b934ee 100644 --- a/src/lib/mtproto/mtprotoworker.ts +++ b/src/lib/mtproto/mtprotoworker.ts @@ -19,6 +19,7 @@ import { UserAuth } from './mtproto_config'; import type { MTMessage } from './networker'; import DEBUG, { MOUNT_CLASS_TO } from '../../config/debug'; import Socket from './transports/websocket'; +import IDBStorage from '../idb'; type Task = { taskId: number, @@ -87,8 +88,11 @@ export class ApiManagerProxy extends CryptoWorkerMethods { this.registerServiceWorker(); - this.addTaskListener('reload', () => { - location.reload(); + this.addTaskListener('clear', () => { + const promise = IDBStorage.deleteDatabase(); + promise.finally(() => { + location.reload(); + }); }); this.addTaskListener('connectionStatusChange', (task: any) => { diff --git a/src/lib/mtproto/networker.ts b/src/lib/mtproto/networker.ts index 579f85df..7cf49bf7 100644 --- a/src/lib/mtproto/networker.ts +++ b/src/lib/mtproto/networker.ts @@ -141,7 +141,7 @@ export default class MTPNetworker { const suffix = this.isFileUpload ? '-U' : this.isFileDownload ? '-D' : ''; this.name = 'NET-' + dcId + suffix; //this.log = logger(this.name, this.upload && this.dcId === 2 ? LogLevels.debug | LogLevels.warn | LogLevels.log | LogLevels.error : LogLevels.error); - this.log = logger(this.name, /* LogTypes.Log | LogTypes.Debug | */LogTypes.Error | LogTypes.Warn); + this.log = logger(this.name, LogTypes.Log | /* LogTypes.Debug | */LogTypes.Error | LogTypes.Warn); this.log('constructor'/* , this.authKey, this.authKeyID, this.serverSalt */); // Test resend after bad_server_salt @@ -596,7 +596,7 @@ export default class MTPNetworker { // this.log('parse for', message) this.parseResponse(result).then((response) => { if(Modes.debug) { - this.log('Server response', response); + this.log.debug('Server response', response); } this.processMessage(response.response, response.messageId, response.sessionId); @@ -735,7 +735,7 @@ export default class MTPNetworker { } if(this.debug) { - this.log('pushResend:', messageId, sentMessage, this.pendingMessages, delay); + this.log.debug('pushResend:', messageId, sentMessage, this.pendingMessages, delay); } this.scheduleRequest(delay); @@ -1089,7 +1089,7 @@ export default class MTPNetworker { public sendEncryptedRequest(message: MTMessage) { return this.getEncryptedOutput(message).then(requestData => { - this.debug && this.log('sendEncryptedRequest: launching message into space:', message, [message.msg_id].concat(message.inner || [])); + this.debug && this.log.debug('sendEncryptedRequest: launching message into space:', message, [message.msg_id].concat(message.inner || [])); const promise: Promise = this.transport.send(requestData) as any; /// #if !MTPROTO_HTTP && !MTPROTO_HTTP_UPLOAD @@ -1325,7 +1325,7 @@ export default class MTPNetworker { public reqResendMessage(msgId: string) { if(this.debug) { - this.log('Req resend', msgId); + this.log.debug('Req resend', msgId); } this.pendingResends.push(msgId); diff --git a/src/lib/storage.ts b/src/lib/storage.ts index 77c0a746..fe7e68ff 100644 --- a/src/lib/storage.ts +++ b/src/lib/storage.ts @@ -196,4 +196,8 @@ export default class AppStorage/* Storage ex } })); } + + public deleteDatabase() { + return IDBStorage.deleteDatabase(); + } } diff --git a/src/scss/partials/pages/_pages.scss b/src/scss/partials/pages/_pages.scss index 218ce104..a6648214 100644 --- a/src/scss/partials/pages/_pages.scss +++ b/src/scss/partials/pages/_pages.scss @@ -263,3 +263,8 @@ margin-top: 1.1875rem; // * verified with mockup } } + +#auth-pages > .scrollable, #main-columns { + opacity: 1; + transition: opacity var(--transition-standard-in); +}