tdlight/example/web/tdweb/src/index.js

213 lines
5.0 KiB
JavaScript
Raw Normal View History

import MyWorker from './worker.js';
import './third_party/broadcastchannel.js';
import uuid4 from 'uuid/v4';
import log from './logger.js';
const sleep = ms => new Promise(res => setTimeout(res, ms));
class TdClient {
constructor(options) {
log.setVerbosity(options.jsVerbosity);
this.worker = new MyWorker();
var self = this;
this.worker.onmessage = function(e) {
let response = e.data;
log.info(
'receive from worker: ',
JSON.parse(
JSON.stringify(response, (key, value) => {
if (key === 'arr') {
return undefined;
}
return value;
})
)
);
if ('@extra' in response) {
var query_id = response['@extra'].query_id;
var [resolve, reject] = self.query_callbacks.get(query_id);
self.query_callbacks.delete(query_id);
if ('@old_extra' in response['@extra']) {
response['@extra'] = response['@extra']['@old_extra'];
}
if (resolve) {
if (response['@type'] === 'error') {
reject(response);
} else {
resolve(response);
}
}
} else {
if (response['@type'] === 'inited') {
self.onInited();
return;
}
if (
response['@type'] === 'updateAuthorizationState' &&
response.authorization_state['@type'] === 'authorizationStateClosed'
) {
self.onClosed();
}
self.onUpdate(response);
}
};
this.query_id = 0;
this.query_callbacks = new Map();
this.worker.postMessage({ '@type': 'init', options: options });
this.closeOtherClients(options);
}
onBroadcastMessage(e) {
var message = e.data;
log.info('got broadcast message: ', message);
if (message.isBackground && !this.isBackground) {
// continue
} else if (
(!message.isBackground && this.isBackground) ||
message.timestamp > this.timestamp
) {
this.close();
return;
}
if (message.state === 'closed') {
this.waitSet.delete(message.uid);
if (this.waitSet.size === 0) {
log.info('onWaitSetEmpty');
this.onWaitSetEmpty();
this.onWaitSetEmpty = () => {};
}
} else {
this.waitSet.add(message.uid);
if (message.state !== 'closing') {
this.postState();
}
}
}
postState() {
let state = {
id: this.uid,
state: this.state,
timestamp: this.timestamp,
isBackground: this.isBackground
};
log.info('Post state: ', state);
this.channel.postMessage(state);
}
onWaitSetEmpty() {
// nop
}
onInited() {
this.isInited = true;
this.doSendStart();
}
sendStart() {
this.wantSendStart = true;
this.doSendStart();
}
doSendStart() {
if (!this.isInited || !this.wantSendStart || this.state !== 'start') {
return;
}
this.wantSendStart = false;
this.state = 'active';
let query = { '@type': 'start' };
log.info('send to worker: ', query);
this.worker.postMessage(query);
}
onClosed() {
this.isClosing = true;
this.worker.terminate();
log.info('worker is terminated');
this.state = 'closed';
this.postState();
}
close() {
if (this.isClosing) {
return;
}
this.isClosing = true;
log.info('close state: ', this.state);
if (this.state === 'start') {
this.onClosed();
this.onUpdate({
'@type': 'updateAuthorizationState',
authorization_state: {
'@type': 'authorizationStateClosed'
}
});
return;
}
let query = { '@type': 'close' };
log.info('send to worker: ', query);
this.worker.postMessage(query);
this.state = 'closing';
this.postState();
}
async closeOtherClients(options) {
this.uid = uuid4();
this.state = 'start';
this.isBackground = !!options.isBackground;
this.timestamp = Date.now();
this.waitSet = new Set();
log.info('close other clients');
let prefix = options.prefix || 'tdlib';
this.channel = new BroadcastChannel(prefix);
this.postState();
var self = this;
this.channel.onmessage = message => {
self.onBroadcastMessage(message);
};
await sleep(300);
if (this.waitSet.size !== 0) {
await new Promise(resolve => {
self.onWaitSetEmpty = resolve;
});
}
this.sendStart();
}
onUpdate(response) {
log.info('ignore onUpdate');
//nop
}
send(query) {
this.query_id++;
if (query['@extra']) {
query['@extra'] = {
'@old_extra': JSON.parse(JSON.stringify(query.extra)),
query_id: this.query_id
};
} else {
query['@extra'] = {
query_id: this.query_id
};
}
if (query['@type'] === 'setJsVerbosity') {
log.setVerbosity(query.verbosity);
}
log.info('send to worker: ', query);
this.worker.postMessage(query);
return new Promise((resolve, reject) => {
this.query_callbacks.set(this.query_id, [resolve, reject]);
});
}
}
export default TdClient;