diff --git a/example/web/tdweb/src/index.js b/example/web/tdweb/src/index.js index 92a6c4792..223c83bd0 100644 --- a/example/web/tdweb/src/index.js +++ b/example/web/tdweb/src/index.js @@ -17,10 +17,10 @@ const sleep = ms => new Promise(res => setTimeout(res, ms)); *
* Differences from TDLib API:
* 1. Added the update updateFatalError error:string = Update; which is sent whenever a TDLib fatal error is encountered.
- * 2. Added the field idb_key to file object, which contains IndexedDB key in which the file content is stored.
+ * 2. Added the field idb_key to file object, which contains IndexedDB key in which the file content is stored. IndexedDB table name is options.instanceName.
* This field is non-empty only for fully downloaded files. IndexedDB database name is chosen during TdClient creation.
* 3. Added the method setJsLogVerbosityLevel new_verbosity_level:string = Ok;, which allows to change the verbosity level of tdweb logging.
- * 4. Added the possibility to use blobs as input files via constructor inputFileBlob blob: = InputFile;.
+ * 4. Added the possibility to use blobs as input files via constructor inputFileBlob data: = InputFile;.
* 5. Added the method readFilePart path:string offset:int64 size:int64 = FilePart; and class filePart data: = FilePart;
* which can be used on a partially downloaded file to support media streaming.
* 6. Methods getStorageStatistics, getStorageStatisticsFast, optimizeStorage, addProxy are not supported.
@@ -36,58 +36,20 @@ class TdClient { * Create TdClient. * @param {Object} options - The options for TDLib instance creation. * @param {TdClient~updateCallback} options.onUpdate - The callback for all incoming updates. - * @param {string} [options.prefix=tdlib] - The name of the IndexedDB database which will be used for persistent data storage. Currently only one instance of TdClient per a database is allowed. All but one created instances will be automatically closed. Usually, the newest non-background instance is kept alive. + * @param {string} [options.instanceName=tdlib] - The name of the TDLib instance. Currently only one instance of TdClient with a given name is allowed. All but one created instances with a given name will be automatically closed. Usually, the newest non-background instance is kept alive. Files will be stored in IndexedDb table with the same name. * @param {boolean} [options.isBackground=false] - Pass true, if the instance is opened from the background. - * @param {string} [options.mode=wasm] - The type of the TDLib build to use. 'asmjs' for asm.js and 'wasm' for WebAssembly. * @param {string} [options.jsLogVerbosityLevel='info'] - The initial verbosity level of the JavaScript part of the code (one of 'error', 'warning', 'info', 'log', 'debug'). * @param {number} [options.logVerbosityLevel=2] - The initial verbosity level for TDLib internal logging (0-1023). - * @param {boolean} [options.noDb=false] - Pass true to use TDLib without database and secret chats. It will significantly improve load time, but some functionality will be unavailable. - * @param {boolean} [options.readOnly=false] - Pass true to open TDLib database in read-only mode. For debug only. + * @param {boolean} [options.useDatabase=true] - Pass false to use TDLib without database and secret chats. It will significantly improve load time, but some functionality will be unavailable. + * @param {string} [options.mode='auto'] - For debug only. The type of the TDLib build to use. 'asmjs' for asm.js and 'wasm' for WebAssembly. If mode == 'auto' WebAbassembly will be used if supported by browser, asm.js otherwise. + * @param {boolean} [options.readOnly=false] - For debug only. Pass true to open TDLib database in read-only mode */ constructor(options) { log.setVerbosity(options.jsLogVerbosityLevel); this.worker = new MyWorker(); var self = this; - this.worker.onmessage = function(e) { - let response = e.data; - log.debug( - '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.worker.onmessage = e => { + self.onResponse(e.data); }; this.query_id = 0; this.query_callbacks = new Map(); @@ -95,6 +57,7 @@ class TdClient { this.onUpdate = options.onUpdate; delete options.onUpdate; } + options.instanceName = options.instanceName || 'tdlib'; this.worker.postMessage({ '@type': 'init', options: options }); this.closeOtherClients(options); } @@ -111,11 +74,6 @@ class TdClient { * @returns {Promise} Promise object represents the result of the query. */ send(query) { - let unsupportedMethods = ['getStorageStatistics', 'getStorageStatisticsFast', 'optimizeStorage', 'addProxy', 'init', 'start']; - if (unsupportedMethods.includes(query['@type'])) { - return; // TODO what we need to return? - } - this.query_id++; if (query['@extra']) { query['@extra'] = { @@ -132,10 +90,75 @@ class TdClient { } log.debug('send to worker: ', query); - this.worker.postMessage(query); - return new Promise((resolve, reject) => { + let res = new Promise((resolve, reject) => { this.query_callbacks.set(this.query_id, [resolve, reject]); }); + this.externalPostMessage(query); + return res; + } + + /** @private */ + externalPostMessage(query) { + let unsupportedMethods = [ + 'getStorageStatistics', + 'getStorageStatisticsFast', + 'optimizeStorage', + 'addProxy', + 'init', + 'start' + ]; + if (unsupportedMethods.includes(query['@type'])) { + this.onResponse({ + '@type': 'error', + '@extra': query['@extra'], + code: 400, + message: "method '" + query['@type'] + "' is not supported" + }); + return; + } + this.worker.postMessage(query); + } + + /** @private */ + onResponse(response) { + log.debug( + '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] = this.query_callbacks.get(query_id); + this.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') { + this.onInited(); + return; + } + if ( + response['@type'] === 'updateAuthorizationState' && + response.authorization_state['@type'] === 'authorizationStateClosed' + ) { + this.onClosed(); + } + this.onUpdate(response); + } } /** @private */ @@ -253,8 +276,7 @@ class TdClient { this.waitSet = new Set(); log.info('close other clients'); - let prefix = options.prefix || 'tdlib'; - this.channel = new BroadcastChannel(prefix); + this.channel = new BroadcastChannel(options.instanceName); this.postState(); diff --git a/example/web/tdweb/src/worker.js b/example/web/tdweb/src/worker.js index 36f2c8f91..ba9e22e33 100644 --- a/example/web/tdweb/src/worker.js +++ b/example/web/tdweb/src/worker.js @@ -144,8 +144,12 @@ async function loadTdLib(mode, onFS) { return false; })(); if (!wasmSupported) { - log.warning('WebAssembly is not supported, trying to use asmjs'); - mode = 'asmjs'; + if (mode === 'wasm') { + log.error('WebAssembly is not supported, trying to use it anyway'); + } else { + log.warning('WebAssembly is not supported, trying to use asmjs'); + mode = 'asmjs'; + } } if (mode === 'asmjs') { @@ -367,15 +371,16 @@ class TdFileSystem { FS.mkdir(prefix); return FS; } - static async create(prefix, FS_promise, readOnly = false) { + static async create(instanceName, FS_promise, readOnly = false) { try { let tdfs = new TdFileSystem(); + let prefix = '/' + instanceName; tdfs.prefix = prefix; FS_promise = TdFileSystem.init_fs(prefix, FS_promise); //MEMFS. Store to IDB and delete files as soon as possible let inboundFileSystem = InboundFileSystem.create( - prefix, + instanceName, prefix + '/inboundfs', FS_promise ); @@ -460,14 +465,16 @@ class TdClient { self.onFS = resolve; }); - let prefix = options.prefix || 'tdlib'; let tdfs_promise = TdFileSystem.create( - '/' + prefix, + options.instanceName, FS_promise, options.readOnly ); - this.noDb = options.noDb || false; + this.useDatabase = true; + if ('useDatabase' in options) { + this.useDatabase = options.useDatabase; + } log.info('load TdModule'); this.TdModule = await loadTdLib(mode, self.onFS); @@ -549,7 +556,7 @@ class TdClient { name: 'ignore_background_updates', value: { '@type': 'optionValueBoolean', - value: this.noDb + value: !this.useDatabase } }); @@ -562,7 +569,7 @@ class TdClient { if (query['@type'] === 'inputFileBlob') { return { '@type': 'inputFileLocal', - path: this.tdfs.outboundFileSystem.blobToPath(query.blob, query.name) + path: this.tdfs.outboundFileSystem.blobToPath(query.data, query.name) }; } for (var key in query) { @@ -579,7 +586,7 @@ class TdClient { query.parameters.database_directory = this.tdfs.dbFileSystem.root; query.parameters.files_directory = this.tdfs.inboundFileSystem.root; - let useDb = !this.noDb; + let useDb = this.useDatabase; query.parameters.use_file_database = useDb; query.parameters.use_chat_info_database = useDb; query.parameters.use_message_database = useDb;