From 3c2cd5fa8796fe7ee495876912a10a496f5b2ab2 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Sun, 15 Nov 2020 23:14:05 +0200 Subject: [PATCH] Updating status --- src/lib/appManagers/apiUpdatesManager.ts | 139 ++++++++++++--------- src/lib/appManagers/appDialogsManager.ts | 146 ++++++++++++++++------- src/lib/appManagers/appUsersManager.ts | 2 +- src/lib/rootScope.ts | 6 +- src/scss/partials/_leftSidebar.scss | 4 +- 5 files changed, 194 insertions(+), 103 deletions(-) diff --git a/src/lib/appManagers/apiUpdatesManager.ts b/src/lib/appManagers/apiUpdatesManager.ts index 800e97fc..2310f725 100644 --- a/src/lib/appManagers/apiUpdatesManager.ts +++ b/src/lib/appManagers/apiUpdatesManager.ts @@ -9,30 +9,38 @@ import appPeersManager from "./appPeersManager"; import appStateManager from './appStateManager'; import appUsersManager from "./appUsersManager"; +type UpdatesState = { + pendingPtsUpdates: any[], + pendingSeqUpdates: any, + syncPending: { + seqAwaiting?: number, + ptsAwaiting?: true, + timeout: number + }, + syncLoading: boolean, + + seq?: number, + pts?: number, + date?: number, + lastPtsUpdateTime?: number +}; + export class ApiUpdatesManager { - public updatesState: { - pendingPtsUpdates: any[], - pendingSeqUpdates: any, - syncPending: any, - syncLoading: any, - - seq?: number, - pts?: number, - date?: number - } = { + public updatesState: UpdatesState = { pendingPtsUpdates: [], pendingSeqUpdates: {}, - syncPending: false, + syncPending: null, syncLoading: true }; - public channelStates: any = {}; + public channelStates: {[channelID: number]: UpdatesState} = {}; private attached = false; private log = logger('UPDATES', LogLevels.error); constructor() { - appStateManager.addListener('save', () => { + // * false for test purposes + /* false && */appStateManager.addListener('save', () => { const us = this.updatesState; appStateManager.pushToState('updates', { seq: us.seq, @@ -43,15 +51,14 @@ export class ApiUpdatesManager { } public popPendingSeqUpdate() { - var nextSeq = this.updatesState.seq + 1; - var pendingUpdatesData = this.updatesState.pendingSeqUpdates[nextSeq]; + const nextSeq = this.updatesState.seq + 1; + const pendingUpdatesData = this.updatesState.pendingSeqUpdates[nextSeq]; if(!pendingUpdatesData) { return false; } - var updates = pendingUpdatesData.updates; - var length; - for(var i = 0, length = updates.length; i < length; i++) { + const updates = pendingUpdatesData.updates; + for(let i = 0, length = updates.length; i < length; i++) { this.saveUpdate(updates[i]); } this.updatesState.seq = pendingUpdatesData.seq; @@ -65,8 +72,8 @@ export class ApiUpdatesManager { this.updatesState.syncPending.seqAwaiting && this.updatesState.seq >= this.updatesState.syncPending.seqAwaiting) { if(!this.updatesState.syncPending.ptsAwaiting) { - clearTimeout(this.updatesState.syncPending.timeout) - this.updatesState.syncPending = false + clearTimeout(this.updatesState.syncPending.timeout); + this.updatesState.syncPending = null; } else { delete this.updatesState.syncPending.seqAwaiting; } @@ -76,7 +83,7 @@ export class ApiUpdatesManager { } public popPendingPtsUpdate(channelID: number) { - var curState = channelID ? this.getChannelState(channelID) : this.updatesState; + const curState = channelID ? this.getChannelState(channelID) : this.updatesState; if(!curState.pendingPtsUpdates.length) { return false; } @@ -85,12 +92,11 @@ export class ApiUpdatesManager { }); // this.log('pop update', channelID, curState.pendingPtsUpdates) - var curPts = curState.pts; - var goodPts = false; - var goodIndex = 0; - var update; - for(var i = 0, length = curState.pendingPtsUpdates.length; i < length; i++) { - update = curState.pendingPtsUpdates[i]; + let curPts = curState.pts; + let goodPts = 0; + let goodIndex = 0; + for(let i = 0, length = curState.pendingPtsUpdates.length; i < length; i++) { + const update = curState.pendingPtsUpdates[i]; curPts += update.pts_count; if(curPts >= update.pts) { goodPts = update.pts; @@ -105,8 +111,8 @@ export class ApiUpdatesManager { this.log('pop pending pts updates', goodPts, curState.pendingPtsUpdates.slice(0, goodIndex + 1)); curState.pts = goodPts; - for(i = 0; i <= goodIndex; i++) { - update = curState.pendingPtsUpdates[i]; + for(let i = 0; i <= goodIndex; i++) { + const update = curState.pendingPtsUpdates[i]; this.saveUpdate(update); } curState.pendingPtsUpdates.splice(0, goodIndex + 1); @@ -114,7 +120,7 @@ export class ApiUpdatesManager { if(!curState.pendingPtsUpdates.length && curState.syncPending) { if(!curState.syncPending.seqAwaiting) { clearTimeout(curState.syncPending.timeout); - curState.syncPending = false; + curState.syncPending = null; } else { delete curState.syncPending.ptsAwaiting; } @@ -191,18 +197,19 @@ export class ApiUpdatesManager { } }; - public getDifference() { + public getDifference(first = false) { // this.trace('Get full diff') const updatesState = this.updatesState; if(!updatesState.syncLoading) { updatesState.syncLoading = true; updatesState.pendingSeqUpdates = {}; updatesState.pendingPtsUpdates = []; + rootScope.broadcast('state_synchronizing'); } if(updatesState.syncPending) { clearTimeout(updatesState.syncPending.timeout); - updatesState.syncPending = false; + updatesState.syncPending = null; } return apiManager.invokeApi('updates.getDifference', { @@ -217,10 +224,15 @@ export class ApiUpdatesManager { updatesState.date = differenceResult.date; updatesState.seq = differenceResult.seq; updatesState.syncLoading = false; - rootScope.broadcast('stateSynchronized'); + rootScope.broadcast('state_synchronized'); return false; } + // ! SORRY I'M SORRY I'M SORRY + if(first) { + rootScope.broadcast('state_synchronizing'); + } + if(differenceResult._ != 'updates.differenceTooLong') { appUsersManager.saveApiUsers(differenceResult.users); appChatsManager.saveApiChats(differenceResult.chats); @@ -266,7 +278,7 @@ export class ApiUpdatesManager { this.getDifference(); } else { // this.log('finished get diff') - rootScope.broadcast('stateSynchronized'); + rootScope.broadcast('state_synchronized'); updatesState.syncLoading = false; } }, () => { @@ -279,11 +291,12 @@ export class ApiUpdatesManager { if(!channelState.syncLoading) { channelState.syncLoading = true; channelState.pendingPtsUpdates = []; + rootScope.broadcast('state_synchronizing', channelID); } if(channelState.syncPending) { clearTimeout(channelState.syncPending.timeout); - channelState.syncPending = false; + channelState.syncPending = null; } // this.log('Get channel diff', appChatsManager.getChat(channelID), channelState.pts) @@ -299,7 +312,7 @@ export class ApiUpdatesManager { if(differenceResult._ == 'updates.channelDifferenceEmpty') { this.log('apply channel empty diff', differenceResult); channelState.syncLoading = false; - rootScope.broadcast('stateSynchronized'); + rootScope.broadcast('state_synchronized', channelID); return false; } @@ -337,7 +350,7 @@ export class ApiUpdatesManager { this.getChannelDifference(channelID); } else { this.log('finished channel get diff'); - rootScope.broadcast('stateSynchronized'); + rootScope.broadcast('state_synchronized', channelID); channelState.syncLoading = false; } }, () => { @@ -352,9 +365,10 @@ export class ApiUpdatesManager { if(!(channelID in this.channelStates)) { this.channelStates[channelID] = { - pts: pts, + pts, pendingPtsUpdates: [], - syncPending: false, + pendingSeqUpdates: null, + syncPending: null, syncLoading: false }; @@ -364,7 +378,7 @@ export class ApiUpdatesManager { return false; } - public getChannelState(channelID: number, pts?: any) { + public getChannelState(channelID: number, pts?: number) { if(this.channelStates[channelID] === undefined) { this.addChannelState(channelID, pts); } @@ -373,7 +387,7 @@ export class ApiUpdatesManager { } public processUpdate(update: any, options: any = {}) { - var channelID: any = false; + let channelID = 0; switch(update._) { case 'updateNewChannelMessage': case 'updateEditChannelMessage': @@ -390,7 +404,7 @@ export class ApiUpdatesManager { break; } - var curState = channelID ? this.getChannelState(channelID, update.pts) : this.updatesState; + const curState = channelID ? this.getChannelState(channelID, update.pts) : this.updatesState; // this.log.log('process', channelID, curState.pts, update) @@ -411,10 +425,10 @@ export class ApiUpdatesManager { update._ == 'updateEditMessage' || update._ == 'updateNewChannelMessage' || update._ == 'updateEditChannelMessage') { - var message = update.message; - var toPeerID = appPeersManager.getPeerID(message.peer_id); - var fwdHeader = message.fwd_from || {}; - var reason: any = false; + const message = update.message; + const toPeerID = appPeersManager.getPeerID(message.peer_id); + const fwdHeader = message.fwd_from || {}; + let reason: any = false; if(message.from_id && !appUsersManager.hasUser(appPeersManager.getPeerID(message.from_id), message.pFlags.post/* || channelID*/) && (reason = 'author') || fwdHeader.from_id && !appUsersManager.hasUser(appPeersManager.getPeerID(fwdHeader.from_id), !!fwdHeader.channel_id) && (reason = 'fwdAuthor') || fwdHeader.channel_id && !appChatsManager.hasChat(fwdHeader.channel_id, true) && (reason = 'fwdChannel') || @@ -433,17 +447,17 @@ export class ApiUpdatesManager { return false; } - var popPts; - var popSeq; + let popPts: boolean; + let popSeq: boolean; if(update.pts) { - var newPts = curState.pts + (update.pts_count || 0); + const newPts = curState.pts + (update.pts_count || 0); if(newPts < update.pts) { this.log.warn('Pts hole', curState, update, channelID && appChatsManager.getChat(channelID)); curState.pendingPtsUpdates.push(update); if(!curState.syncPending) { curState.syncPending = { - timeout: setTimeout(() => { + timeout: window.setTimeout(() => { if(channelID) { this.getChannelDifference(channelID); } else { @@ -470,22 +484,22 @@ export class ApiUpdatesManager { if(channelID && options.date && this.updatesState.date < options.date) { this.updatesState.date = options.date; } - } else if (!channelID && options.seq > 0) { - var seq = options.seq; - var seqStart = options.seqStart || seq; + } else if(!channelID && options.seq > 0) { + const seq = options.seq; + const seqStart = options.seqStart || seq; if(seqStart != curState.seq + 1) { if(seqStart > curState.seq) { this.log.warn('Seq hole', curState, curState.syncPending && curState.syncPending.seqAwaiting); if(curState.pendingSeqUpdates[seqStart] === undefined) { - curState.pendingSeqUpdates[seqStart] = {seq: seq, date: options.date, updates: []}; + curState.pendingSeqUpdates[seqStart] = {seq, date: options.date, updates: []}; } curState.pendingSeqUpdates[seqStart].updates.push(update); if(!curState.syncPending) { curState.syncPending = { - timeout: setTimeout(() => { + timeout: window.setTimeout(() => { this.getDifference(); }, 5000) } @@ -534,6 +548,7 @@ export class ApiUpdatesManager { apiManager.setUpdatesProcessor(this.processUpdateMessage); + //rootScope.broadcast('state_synchronizing'); if(!state || !state.pts || !state.date || !state.seq) { apiManager.invokeApi('updates.getState', {}, {noErrorBox: true}).then((stateResult) => { this.updatesState.seq = stateResult.seq; @@ -541,16 +556,28 @@ export class ApiUpdatesManager { this.updatesState.date = stateResult.date; setTimeout(() => { this.updatesState.syncLoading = false; + //rootScope.broadcast('state_synchronized'); }, 1000); + // ! for testing // updatesState.seq = 1 // updatesState.pts = stateResult.pts - 5000 // updatesState.date = 1 // getDifference() }); } else { + // ! for testing + /* state.seq = 1; + state.pts = state.pts - 100; + state.date = 1; */ + Object.assign(this.updatesState, state); - this.getDifference(); + + this.getDifference(true)/* .finally(() => { + if(this.updatesState.syncLoading) { + rootScope.broadcast('state_synchronizing'); + } + }) */; } }); } diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 19643dc8..bae0561d 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -42,6 +42,108 @@ type DialogDom = { const testScroll = false; let testTopSlice = 1; +class ConnectionStatusComponent { + private statusContainer: HTMLElement; + private statusEl: HTMLElement; + private statusPreloader: HTMLElement; + + private currentText = ''; + + private connecting = false; + private updating = false; + + private log: ReturnType; + + constructor(chatsContainer: HTMLElement) { + this.log = logger('CS'); + + this.statusContainer = document.createElement('div'); + this.statusContainer.classList.add('connection-status'); + + this.statusEl = Button('btn-primary bg-warning connection-status-button', {noRipple: true}); + this.statusPreloader = putPreloader(null, true).firstElementChild as HTMLElement; + this.statusContainer.append(this.statusEl); + + chatsContainer.prepend(this.statusContainer); + + rootScope.on('connection_status_change', (e) => { + const status = e.detail; + console.log(status); + + setConnectionStatus(); + }); + + rootScope.on('state_synchronizing', (e) => { + const channelID = e.detail; + if(!channelID) { + this.updating = true; + this.log('updating', this.updating); + this.setState(); + } + }); + + rootScope.on('state_synchronized', (e) => { + const channelID = e.detail; + this.log('state_synchronized', channelID); + if(!channelID) { + this.updating = false; + this.log('updating', this.updating); + this.setState(); + } + }); + + const setConnectionStatus = () => { + AppStorage.get('dc').then(baseDcID => { + if(setFirstConnectionTimeout) { + clearTimeout(setFirstConnectionTimeout); + setFirstConnectionTimeout = 0; + } + + const status = rootScope.connectionStatus['NET-' + baseDcID]; + const online = status && status.online; + + this.connecting = !online; + this.log('connecting', this.connecting); + this.setState(); + }); + }; + + let setFirstConnectionTimeout = window.setTimeout(setConnectionStatus, 2e3); + + let bool = true; + document.addEventListener('dblclick', () => { + rootScope.broadcast('connection_status_change', { + dcID: 2, + isFileDownload: false, + isFileNetworker: false, + isFileUpload: false, + name: "NET-2", + online: bool = !bool, + _: "networkerStatus" + }); + }); + } + + private setStatusText = (text: string) => { + if(this.currentText == text) return; + this.statusEl.innerText = this.currentText = text; + this.statusEl.appendChild(this.statusPreloader); + }; + + private setState = () => { + if(this.connecting) { + this.setStatusText('Waiting for network...'); + } else if(this.updating) { + this.setStatusText('Updating...'); + } + + this.log('setState', this.connecting || this.updating); + window.requestAnimationFrame(() => { + SetTransition(this.statusContainer, 'is-shown', this.connecting || this.updating, 200); + }); + }; +} + export class AppDialogsManager { public _chatList = document.getElementById('dialogs') as HTMLUListElement; public chatList = this._chatList; @@ -353,48 +455,8 @@ export class AppDialogsManager { }); }); - const statusDiv = document.createElement('div'); - statusDiv.classList.add('connection-status'); - - const networkButton = Button('btn-primary bg-warning connection-status-button'); - networkButton.innerText = 'Waiting for network...'; - putPreloader(networkButton); - statusDiv.append(networkButton); - this.chatsContainer.append(statusDiv, bottomPart); - - rootScope.on('connection_status_change', e => { - const status = e.detail; - console.log(status); - - setConnectionStatus(); - }); - - const setConnectionStatus = () => { - AppStorage.get('dc').then(baseDcID => { - if(setFirstConnectionTimeout) { - clearTimeout(setFirstConnectionTimeout); - setFirstConnectionTimeout = 0; - } - - const status = rootScope.connectionStatus['NET-' + baseDcID]; - SetTransition(statusDiv, 'is-not-connected', !status?.online, 200); - }); - }; - - let setFirstConnectionTimeout = window.setTimeout(setConnectionStatus, 2e3); - - /* let bool = true; - document.addEventListener('dblclick', () => { - rootScope.broadcast('connection_status_change', { - dcID: 5, - isFileDownload: false, - isFileNetworker: false, - isFileUpload: false, - name: "NET-5", - online: bool = !bool, - _: "networkerStatus" - }); - }); */ + new ConnectionStatusComponent(this.chatsContainer); + this.chatsContainer.append(bottomPart); /* const mutationObserver = new MutationObserver((mutationList, observer) => { diff --git a/src/lib/appManagers/appUsersManager.ts b/src/lib/appManagers/appUsersManager.ts index ace5acae..61c3e930 100644 --- a/src/lib/appManagers/appUsersManager.ts +++ b/src/lib/appManagers/appUsersManager.ts @@ -32,7 +32,7 @@ export class AppUsersManager { constructor() { setInterval(this.updateUsersStatuses, 60000); - rootScope.on('stateSynchronized', this.updateUsersStatuses); + rootScope.on('state_synchronized', this.updateUsersStatuses); rootScope.on('apiUpdate', (e) => { const update = e.detail as Update; diff --git a/src/lib/rootScope.ts b/src/lib/rootScope.ts index 13bacf16..ec208713 100644 --- a/src/lib/rootScope.ts +++ b/src/lib/rootScope.ts @@ -50,13 +50,15 @@ type BroadcastEvents = { 'audio_play': {doc: MyDocument, mid: number}, 'audio_pause': void, - + + 'state_synchronized': number, + 'state_synchronizing': number, + //'contacts_update': any, 'avatar_update': number, 'chat_full_update': number, 'poll_update': {poll: Poll, results: PollResults}, 'chat_update': number, - 'stateSynchronized': void, 'channel_settings': {channelID: number}, 'webpage_updated': {id: string, msgs: number[]}, diff --git a/src/scss/partials/_leftSidebar.scss b/src/scss/partials/_leftSidebar.scss index 55da7fd8..3802035a 100644 --- a/src/scss/partials/_leftSidebar.scss +++ b/src/scss/partials/_leftSidebar.scss @@ -299,13 +299,13 @@ overflow: hidden; flex: 0 0 auto; - &:not(.is-not-connected) { + &:not(.is-shown) { .connection-status-button { display: none; } } - &.is-not-connected { + &.is-shown { &.animating { .connection-status-button, & + .connection-status-bottom { transition: transform var(--layer-transition);