Browse Source

Updating status

master
Eduard Kuzmenko 4 years ago
parent
commit
3c2cd5fa87
  1. 131
      src/lib/appManagers/apiUpdatesManager.ts
  2. 146
      src/lib/appManagers/appDialogsManager.ts
  3. 2
      src/lib/appManagers/appUsersManager.ts
  4. 4
      src/lib/rootScope.ts
  5. 4
      src/scss/partials/_leftSidebar.scss

131
src/lib/appManagers/apiUpdatesManager.ts

@ -9,30 +9,38 @@ import appPeersManager from "./appPeersManager";
import appStateManager from './appStateManager'; import appStateManager from './appStateManager';
import appUsersManager from "./appUsersManager"; import appUsersManager from "./appUsersManager";
export class ApiUpdatesManager { type UpdatesState = {
public updatesState: {
pendingPtsUpdates: any[], pendingPtsUpdates: any[],
pendingSeqUpdates: any, pendingSeqUpdates: any,
syncPending: any, syncPending: {
syncLoading: any, seqAwaiting?: number,
ptsAwaiting?: true,
timeout: number
},
syncLoading: boolean,
seq?: number, seq?: number,
pts?: number, pts?: number,
date?: number date?: number,
} = { lastPtsUpdateTime?: number
};
export class ApiUpdatesManager {
public updatesState: UpdatesState = {
pendingPtsUpdates: [], pendingPtsUpdates: [],
pendingSeqUpdates: {}, pendingSeqUpdates: {},
syncPending: false, syncPending: null,
syncLoading: true syncLoading: true
}; };
public channelStates: any = {}; public channelStates: {[channelID: number]: UpdatesState} = {};
private attached = false; private attached = false;
private log = logger('UPDATES', LogLevels.error); private log = logger('UPDATES', LogLevels.error);
constructor() { constructor() {
appStateManager.addListener('save', () => { // * false for test purposes
/* false && */appStateManager.addListener('save', () => {
const us = this.updatesState; const us = this.updatesState;
appStateManager.pushToState('updates', { appStateManager.pushToState('updates', {
seq: us.seq, seq: us.seq,
@ -43,15 +51,14 @@ export class ApiUpdatesManager {
} }
public popPendingSeqUpdate() { public popPendingSeqUpdate() {
var nextSeq = this.updatesState.seq + 1; const nextSeq = this.updatesState.seq + 1;
var pendingUpdatesData = this.updatesState.pendingSeqUpdates[nextSeq]; const pendingUpdatesData = this.updatesState.pendingSeqUpdates[nextSeq];
if(!pendingUpdatesData) { if(!pendingUpdatesData) {
return false; return false;
} }
var updates = pendingUpdatesData.updates; const updates = pendingUpdatesData.updates;
var length; for(let i = 0, length = updates.length; i < length; i++) {
for(var i = 0, length = updates.length; i < length; i++) {
this.saveUpdate(updates[i]); this.saveUpdate(updates[i]);
} }
this.updatesState.seq = pendingUpdatesData.seq; this.updatesState.seq = pendingUpdatesData.seq;
@ -65,8 +72,8 @@ export class ApiUpdatesManager {
this.updatesState.syncPending.seqAwaiting && this.updatesState.syncPending.seqAwaiting &&
this.updatesState.seq >= this.updatesState.syncPending.seqAwaiting) { this.updatesState.seq >= this.updatesState.syncPending.seqAwaiting) {
if(!this.updatesState.syncPending.ptsAwaiting) { if(!this.updatesState.syncPending.ptsAwaiting) {
clearTimeout(this.updatesState.syncPending.timeout) clearTimeout(this.updatesState.syncPending.timeout);
this.updatesState.syncPending = false this.updatesState.syncPending = null;
} else { } else {
delete this.updatesState.syncPending.seqAwaiting; delete this.updatesState.syncPending.seqAwaiting;
} }
@ -76,7 +83,7 @@ export class ApiUpdatesManager {
} }
public popPendingPtsUpdate(channelID: number) { public popPendingPtsUpdate(channelID: number) {
var curState = channelID ? this.getChannelState(channelID) : this.updatesState; const curState = channelID ? this.getChannelState(channelID) : this.updatesState;
if(!curState.pendingPtsUpdates.length) { if(!curState.pendingPtsUpdates.length) {
return false; return false;
} }
@ -85,12 +92,11 @@ export class ApiUpdatesManager {
}); });
// this.log('pop update', channelID, curState.pendingPtsUpdates) // this.log('pop update', channelID, curState.pendingPtsUpdates)
var curPts = curState.pts; let curPts = curState.pts;
var goodPts = false; let goodPts = 0;
var goodIndex = 0; let goodIndex = 0;
var update; for(let i = 0, length = curState.pendingPtsUpdates.length; i < length; i++) {
for(var i = 0, length = curState.pendingPtsUpdates.length; i < length; i++) { const update = curState.pendingPtsUpdates[i];
update = curState.pendingPtsUpdates[i];
curPts += update.pts_count; curPts += update.pts_count;
if(curPts >= update.pts) { if(curPts >= update.pts) {
goodPts = 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)); this.log('pop pending pts updates', goodPts, curState.pendingPtsUpdates.slice(0, goodIndex + 1));
curState.pts = goodPts; curState.pts = goodPts;
for(i = 0; i <= goodIndex; i++) { for(let i = 0; i <= goodIndex; i++) {
update = curState.pendingPtsUpdates[i]; const update = curState.pendingPtsUpdates[i];
this.saveUpdate(update); this.saveUpdate(update);
} }
curState.pendingPtsUpdates.splice(0, goodIndex + 1); curState.pendingPtsUpdates.splice(0, goodIndex + 1);
@ -114,7 +120,7 @@ export class ApiUpdatesManager {
if(!curState.pendingPtsUpdates.length && curState.syncPending) { if(!curState.pendingPtsUpdates.length && curState.syncPending) {
if(!curState.syncPending.seqAwaiting) { if(!curState.syncPending.seqAwaiting) {
clearTimeout(curState.syncPending.timeout); clearTimeout(curState.syncPending.timeout);
curState.syncPending = false; curState.syncPending = null;
} else { } else {
delete curState.syncPending.ptsAwaiting; delete curState.syncPending.ptsAwaiting;
} }
@ -191,18 +197,19 @@ export class ApiUpdatesManager {
} }
}; };
public getDifference() { public getDifference(first = false) {
// this.trace('Get full diff') // this.trace('Get full diff')
const updatesState = this.updatesState; const updatesState = this.updatesState;
if(!updatesState.syncLoading) { if(!updatesState.syncLoading) {
updatesState.syncLoading = true; updatesState.syncLoading = true;
updatesState.pendingSeqUpdates = {}; updatesState.pendingSeqUpdates = {};
updatesState.pendingPtsUpdates = []; updatesState.pendingPtsUpdates = [];
rootScope.broadcast('state_synchronizing');
} }
if(updatesState.syncPending) { if(updatesState.syncPending) {
clearTimeout(updatesState.syncPending.timeout); clearTimeout(updatesState.syncPending.timeout);
updatesState.syncPending = false; updatesState.syncPending = null;
} }
return apiManager.invokeApi('updates.getDifference', { return apiManager.invokeApi('updates.getDifference', {
@ -217,10 +224,15 @@ export class ApiUpdatesManager {
updatesState.date = differenceResult.date; updatesState.date = differenceResult.date;
updatesState.seq = differenceResult.seq; updatesState.seq = differenceResult.seq;
updatesState.syncLoading = false; updatesState.syncLoading = false;
rootScope.broadcast('stateSynchronized'); rootScope.broadcast('state_synchronized');
return false; return false;
} }
// ! SORRY I'M SORRY I'M SORRY
if(first) {
rootScope.broadcast('state_synchronizing');
}
if(differenceResult._ != 'updates.differenceTooLong') { if(differenceResult._ != 'updates.differenceTooLong') {
appUsersManager.saveApiUsers(differenceResult.users); appUsersManager.saveApiUsers(differenceResult.users);
appChatsManager.saveApiChats(differenceResult.chats); appChatsManager.saveApiChats(differenceResult.chats);
@ -266,7 +278,7 @@ export class ApiUpdatesManager {
this.getDifference(); this.getDifference();
} else { } else {
// this.log('finished get diff') // this.log('finished get diff')
rootScope.broadcast('stateSynchronized'); rootScope.broadcast('state_synchronized');
updatesState.syncLoading = false; updatesState.syncLoading = false;
} }
}, () => { }, () => {
@ -279,11 +291,12 @@ export class ApiUpdatesManager {
if(!channelState.syncLoading) { if(!channelState.syncLoading) {
channelState.syncLoading = true; channelState.syncLoading = true;
channelState.pendingPtsUpdates = []; channelState.pendingPtsUpdates = [];
rootScope.broadcast('state_synchronizing', channelID);
} }
if(channelState.syncPending) { if(channelState.syncPending) {
clearTimeout(channelState.syncPending.timeout); clearTimeout(channelState.syncPending.timeout);
channelState.syncPending = false; channelState.syncPending = null;
} }
// this.log('Get channel diff', appChatsManager.getChat(channelID), channelState.pts) // this.log('Get channel diff', appChatsManager.getChat(channelID), channelState.pts)
@ -299,7 +312,7 @@ export class ApiUpdatesManager {
if(differenceResult._ == 'updates.channelDifferenceEmpty') { if(differenceResult._ == 'updates.channelDifferenceEmpty') {
this.log('apply channel empty diff', differenceResult); this.log('apply channel empty diff', differenceResult);
channelState.syncLoading = false; channelState.syncLoading = false;
rootScope.broadcast('stateSynchronized'); rootScope.broadcast('state_synchronized', channelID);
return false; return false;
} }
@ -337,7 +350,7 @@ export class ApiUpdatesManager {
this.getChannelDifference(channelID); this.getChannelDifference(channelID);
} else { } else {
this.log('finished channel get diff'); this.log('finished channel get diff');
rootScope.broadcast('stateSynchronized'); rootScope.broadcast('state_synchronized', channelID);
channelState.syncLoading = false; channelState.syncLoading = false;
} }
}, () => { }, () => {
@ -352,9 +365,10 @@ export class ApiUpdatesManager {
if(!(channelID in this.channelStates)) { if(!(channelID in this.channelStates)) {
this.channelStates[channelID] = { this.channelStates[channelID] = {
pts: pts, pts,
pendingPtsUpdates: [], pendingPtsUpdates: [],
syncPending: false, pendingSeqUpdates: null,
syncPending: null,
syncLoading: false syncLoading: false
}; };
@ -364,7 +378,7 @@ export class ApiUpdatesManager {
return false; return false;
} }
public getChannelState(channelID: number, pts?: any) { public getChannelState(channelID: number, pts?: number) {
if(this.channelStates[channelID] === undefined) { if(this.channelStates[channelID] === undefined) {
this.addChannelState(channelID, pts); this.addChannelState(channelID, pts);
} }
@ -373,7 +387,7 @@ export class ApiUpdatesManager {
} }
public processUpdate(update: any, options: any = {}) { public processUpdate(update: any, options: any = {}) {
var channelID: any = false; let channelID = 0;
switch(update._) { switch(update._) {
case 'updateNewChannelMessage': case 'updateNewChannelMessage':
case 'updateEditChannelMessage': case 'updateEditChannelMessage':
@ -390,7 +404,7 @@ export class ApiUpdatesManager {
break; 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) // this.log.log('process', channelID, curState.pts, update)
@ -411,10 +425,10 @@ export class ApiUpdatesManager {
update._ == 'updateEditMessage' || update._ == 'updateEditMessage' ||
update._ == 'updateNewChannelMessage' || update._ == 'updateNewChannelMessage' ||
update._ == 'updateEditChannelMessage') { update._ == 'updateEditChannelMessage') {
var message = update.message; const message = update.message;
var toPeerID = appPeersManager.getPeerID(message.peer_id); const toPeerID = appPeersManager.getPeerID(message.peer_id);
var fwdHeader = message.fwd_from || {}; const fwdHeader = message.fwd_from || {};
var reason: any = false; let reason: any = false;
if(message.from_id && !appUsersManager.hasUser(appPeersManager.getPeerID(message.from_id), message.pFlags.post/* || channelID*/) && (reason = 'author') || 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.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') || fwdHeader.channel_id && !appChatsManager.hasChat(fwdHeader.channel_id, true) && (reason = 'fwdChannel') ||
@ -433,17 +447,17 @@ export class ApiUpdatesManager {
return false; return false;
} }
var popPts; let popPts: boolean;
var popSeq; let popSeq: boolean;
if(update.pts) { if(update.pts) {
var newPts = curState.pts + (update.pts_count || 0); const newPts = curState.pts + (update.pts_count || 0);
if(newPts < update.pts) { if(newPts < update.pts) {
this.log.warn('Pts hole', curState, update, channelID && appChatsManager.getChat(channelID)); this.log.warn('Pts hole', curState, update, channelID && appChatsManager.getChat(channelID));
curState.pendingPtsUpdates.push(update); curState.pendingPtsUpdates.push(update);
if(!curState.syncPending) { if(!curState.syncPending) {
curState.syncPending = { curState.syncPending = {
timeout: setTimeout(() => { timeout: window.setTimeout(() => {
if(channelID) { if(channelID) {
this.getChannelDifference(channelID); this.getChannelDifference(channelID);
} else { } else {
@ -470,22 +484,22 @@ export class ApiUpdatesManager {
if(channelID && options.date && this.updatesState.date < options.date) { if(channelID && options.date && this.updatesState.date < options.date) {
this.updatesState.date = options.date; this.updatesState.date = options.date;
} }
} else if (!channelID && options.seq > 0) { } else if(!channelID && options.seq > 0) {
var seq = options.seq; const seq = options.seq;
var seqStart = options.seqStart || seq; const seqStart = options.seqStart || seq;
if(seqStart != curState.seq + 1) { if(seqStart != curState.seq + 1) {
if(seqStart > curState.seq) { if(seqStart > curState.seq) {
this.log.warn('Seq hole', curState, curState.syncPending && curState.syncPending.seqAwaiting); this.log.warn('Seq hole', curState, curState.syncPending && curState.syncPending.seqAwaiting);
if(curState.pendingSeqUpdates[seqStart] === undefined) { 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); curState.pendingSeqUpdates[seqStart].updates.push(update);
if(!curState.syncPending) { if(!curState.syncPending) {
curState.syncPending = { curState.syncPending = {
timeout: setTimeout(() => { timeout: window.setTimeout(() => {
this.getDifference(); this.getDifference();
}, 5000) }, 5000)
} }
@ -534,6 +548,7 @@ export class ApiUpdatesManager {
apiManager.setUpdatesProcessor(this.processUpdateMessage); apiManager.setUpdatesProcessor(this.processUpdateMessage);
//rootScope.broadcast('state_synchronizing');
if(!state || !state.pts || !state.date || !state.seq) { if(!state || !state.pts || !state.date || !state.seq) {
apiManager.invokeApi('updates.getState', {}, {noErrorBox: true}).then((stateResult) => { apiManager.invokeApi('updates.getState', {}, {noErrorBox: true}).then((stateResult) => {
this.updatesState.seq = stateResult.seq; this.updatesState.seq = stateResult.seq;
@ -541,16 +556,28 @@ export class ApiUpdatesManager {
this.updatesState.date = stateResult.date; this.updatesState.date = stateResult.date;
setTimeout(() => { setTimeout(() => {
this.updatesState.syncLoading = false; this.updatesState.syncLoading = false;
//rootScope.broadcast('state_synchronized');
}, 1000); }, 1000);
// ! for testing
// updatesState.seq = 1 // updatesState.seq = 1
// updatesState.pts = stateResult.pts - 5000 // updatesState.pts = stateResult.pts - 5000
// updatesState.date = 1 // updatesState.date = 1
// getDifference() // getDifference()
}); });
} else { } else {
// ! for testing
/* state.seq = 1;
state.pts = state.pts - 100;
state.date = 1; */
Object.assign(this.updatesState, state); Object.assign(this.updatesState, state);
this.getDifference();
this.getDifference(true)/* .finally(() => {
if(this.updatesState.syncLoading) {
rootScope.broadcast('state_synchronizing');
}
}) */;
} }
}); });
} }

146
src/lib/appManagers/appDialogsManager.ts

@ -42,6 +42,108 @@ type DialogDom = {
const testScroll = false; const testScroll = false;
let testTopSlice = 1; 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<typeof logger>;
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<number>('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 { export class AppDialogsManager {
public _chatList = document.getElementById('dialogs') as HTMLUListElement; public _chatList = document.getElementById('dialogs') as HTMLUListElement;
public chatList = this._chatList; public chatList = this._chatList;
@ -353,48 +455,8 @@ export class AppDialogsManager {
}); });
}); });
const statusDiv = document.createElement('div'); new ConnectionStatusComponent(this.chatsContainer);
statusDiv.classList.add('connection-status'); this.chatsContainer.append(bottomPart);
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<number>('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"
});
}); */
/* const mutationObserver = new MutationObserver((mutationList, observer) => { /* const mutationObserver = new MutationObserver((mutationList, observer) => {

2
src/lib/appManagers/appUsersManager.ts

@ -32,7 +32,7 @@ export class AppUsersManager {
constructor() { constructor() {
setInterval(this.updateUsersStatuses, 60000); setInterval(this.updateUsersStatuses, 60000);
rootScope.on('stateSynchronized', this.updateUsersStatuses); rootScope.on('state_synchronized', this.updateUsersStatuses);
rootScope.on('apiUpdate', (e) => { rootScope.on('apiUpdate', (e) => {
const update = e.detail as Update; const update = e.detail as Update;

4
src/lib/rootScope.ts

@ -51,12 +51,14 @@ type BroadcastEvents = {
'audio_play': {doc: MyDocument, mid: number}, 'audio_play': {doc: MyDocument, mid: number},
'audio_pause': void, 'audio_pause': void,
'state_synchronized': number,
'state_synchronizing': number,
//'contacts_update': any, //'contacts_update': any,
'avatar_update': number, 'avatar_update': number,
'chat_full_update': number, 'chat_full_update': number,
'poll_update': {poll: Poll, results: PollResults}, 'poll_update': {poll: Poll, results: PollResults},
'chat_update': number, 'chat_update': number,
'stateSynchronized': void,
'channel_settings': {channelID: number}, 'channel_settings': {channelID: number},
'webpage_updated': {id: string, msgs: number[]}, 'webpage_updated': {id: string, msgs: number[]},

4
src/scss/partials/_leftSidebar.scss

@ -299,13 +299,13 @@
overflow: hidden; overflow: hidden;
flex: 0 0 auto; flex: 0 0 auto;
&:not(.is-not-connected) { &:not(.is-shown) {
.connection-status-button { .connection-status-button {
display: none; display: none;
} }
} }
&.is-not-connected { &.is-shown {
&.animating { &.animating {
.connection-status-button, & + .connection-status-bottom { .connection-status-button, & + .connection-status-bottom {
transition: transform var(--layer-transition); transition: transform var(--layer-transition);

Loading…
Cancel
Save