diff --git a/lib/encryption/utils/key_verification.dart b/lib/encryption/utils/key_verification.dart index 35cc121f..33963621 100644 --- a/lib/encryption/utils/key_verification.dart +++ b/lib/encryption/utils/key_verification.dart @@ -748,9 +748,10 @@ class KeyVerification { // no need to request cache, we already have it return; } - // ignore: unawaited_futures - encryption.ssss - .maybeRequestAll(_verifiedDevices.whereType().toList()); + unawaited( + encryption.ssss + .maybeRequestAll(_verifiedDevices.whereType().toList()), + ); if (requestInterval.length <= i) { return; } diff --git a/lib/matrix.dart b/lib/matrix.dart index da5aadeb..8ad93add 100644 --- a/lib/matrix.dart +++ b/lib/matrix.dart @@ -84,7 +84,7 @@ export 'msc_extensions/extension_timeline_export/timeline_export.dart'; export 'msc_extensions/msc_4140_delayed_events/api.dart'; export 'src/utils/web_worker/web_worker_stub.dart' - if (dart.library.html) 'src/utils/web_worker/web_worker.dart'; + if (dart.library.js_interop) 'src/utils/web_worker/web_worker.dart'; export 'src/utils/web_worker/native_implementations_web_worker_stub.dart' - if (dart.library.html) 'src/utils/web_worker/native_implementations_web_worker.dart'; + if (dart.library.js_interop) 'src/utils/web_worker/native_implementations_web_worker.dart'; diff --git a/lib/matrix_api_lite/utils/logs.dart b/lib/matrix_api_lite/utils/logs.dart index 495194cd..ff266686 100644 --- a/lib/matrix_api_lite/utils/logs.dart +++ b/lib/matrix_api_lite/utils/logs.dart @@ -22,7 +22,7 @@ */ import 'package:matrix/matrix_api_lite/utils/print_logs_native.dart' - if (dart.library.html) 'print_logs_web.dart'; + if (dart.library.js_interop) 'print_logs_web.dart'; enum Level { wtf, diff --git a/lib/matrix_api_lite/utils/print_logs_web.dart b/lib/matrix_api_lite/utils/print_logs_web.dart index b24ffd4b..c1e83379 100644 --- a/lib/matrix_api_lite/utils/print_logs_web.dart +++ b/lib/matrix_api_lite/utils/print_logs_web.dart @@ -1,4 +1,6 @@ -import 'dart:html'; +import 'dart:js_interop'; + +import 'package:web/web.dart'; import 'package:matrix/matrix_api_lite.dart'; @@ -13,22 +15,22 @@ extension PrintLogs on LogEvent { } switch (level) { case Level.wtf: - window.console.error('!!!CRITICAL!!! $logsStr'); + console.error('!!!CRITICAL!!! $logsStr'.toJS); break; case Level.error: - window.console.error(logsStr); + console.error(logsStr.toJS); break; case Level.warning: - window.console.warn(logsStr); + console.warn(logsStr.toJS); break; case Level.info: - window.console.info(logsStr); + console.info(logsStr.toJS); break; case Level.debug: - window.console.debug(logsStr); + console.debug(logsStr.toJS); break; case Level.verbose: - window.console.log(logsStr); + console.log(logsStr.toJS); break; } } diff --git a/lib/src/database/indexeddb_box.dart b/lib/src/database/indexeddb_box.dart index 080b1ad5..5726cb0e 100644 --- a/lib/src/database/indexeddb_box.dart +++ b/lib/src/database/indexeddb_box.dart @@ -1,13 +1,15 @@ import 'dart:async'; -import 'dart:html'; -import 'dart:indexed_db'; +import 'dart:js_interop'; +import 'package:web/web.dart'; + +import 'package:matrix/matrix_api_lite/utils/logs.dart'; import 'package:matrix/src/database/zone_transaction_mixin.dart'; /// Key-Value store abstraction over IndexedDB so that the sdk database can use /// a single interface for all platforms. API is inspired by Hive. class BoxCollection with ZoneTransactionMixin { - final Database _db; + final IDBDatabase _db; final Set boxNames; final String name; @@ -18,23 +20,45 @@ class BoxCollection with ZoneTransactionMixin { Set boxNames, { Object? sqfliteDatabase, Object? sqfliteFactory, - IdbFactory? idbFactory, + IDBFactory? idbFactory, int version = 1, }) async { - idbFactory ??= window.indexedDB!; - final db = await idbFactory.open( - name, - version: version, - onUpgradeNeeded: (VersionChangeEvent event) { - final db = event.target.result; - for (final name in boxNames) { - if (db.objectStoreNames.contains(name)) continue; + idbFactory ??= window.indexedDB; + final dbOpenCompleter = Completer(); + final request = idbFactory.open(name, version); - db.createObjectStore(name, autoIncrement: true); - } - }, - ); - return BoxCollection(db, boxNames, name); + request.onerror = (Event event) { + Logs().e( + '[IndexedDBBox] Error loading database - ${request.error?.toString()}', + ); + dbOpenCompleter.completeError( + 'Error loading database - ${request.error?.toString()}', + ); + }.toJS; + + request.onupgradeneeded = (IDBVersionChangeEvent event) { + final db = (event.target! as IDBOpenDBRequest).result as IDBDatabase; + + db.onerror = (Event event) { + Logs().e('[IndexedDBBox] [onupgradeneeded] Error loading database'); + dbOpenCompleter + .completeError('Error loading database onupgradeneeded.'); + }.toJS; + + for (final name in boxNames) { + if (db.objectStoreNames.contains(name)) continue; + db.createObjectStore( + name, + IDBObjectStoreParameters(autoIncrement: true), + ); + } + }.toJS; + + request.onsuccess = (Event event) { + final db = request.result as IDBDatabase; + dbOpenCompleter.complete(BoxCollection(db, boxNames, name)); + }.toJS; + return dbOpenCompleter.future; } Box openBox(String name) { @@ -44,7 +68,7 @@ class BoxCollection with ZoneTransactionMixin { return Box(name, this); } - List Function(Transaction txn)>? _txnCache; + List Function(IDBTransaction txn)>? _txnCache; Future transaction( Future Function() action, { @@ -52,15 +76,18 @@ class BoxCollection with ZoneTransactionMixin { bool readOnly = false, }) => zoneTransaction(() async { - boxNames ??= _db.objectStoreNames!.toList(); final txnCache = _txnCache = []; await action(); final cache = - List Function(Transaction txn)>.from(txnCache); + List Function(IDBTransaction txn)>.from(txnCache); _txnCache = null; if (cache.isEmpty) return; - final txn = - _db.transaction(boxNames, readOnly ? 'readonly' : 'readwrite'); + + final transactionCompleter = Completer(); + final txn = _db.transaction( + boxNames?.jsify() ?? _db.objectStoreNames, + readOnly ? 'readonly' : 'readwrite', + ); for (final fun in cache) { // The IDB methods return a Future in Dart but must not be awaited in // order to have an actual transaction. They must only be performed and @@ -69,16 +96,54 @@ class BoxCollection with ZoneTransactionMixin { // https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction unawaited(fun(txn)); } - await txn.completed; - return; + + txn.onerror = (Event event) { + Logs().e( + '[IndexedDBBox] [transaction] Error - ${txn.error?.toString()}', + ); + transactionCompleter.completeError( + 'Transaction not completed due to an error - ${txn.error?.toString()}' + .toJS, + ); + }.toJS; + + txn.oncomplete = (Event event) { + transactionCompleter.complete(); + }.toJS; + return transactionCompleter.future; }); Future clear() async { - final txn = _db.transaction(boxNames.toList(), 'readwrite'); + final transactionCompleter = Completer(); + final txn = _db.transaction(boxNames.toList().jsify()!, 'readwrite'); for (final name in boxNames) { - unawaited(txn.objectStore(name).clear()); + final objStoreClearCompleter = Completer(); + final request = txn.objectStore(name).clear(); + request.onerror = (Event event) { + Logs().e( + '[IndexedDBBox] [clear] Object store clear error - ${request.error?.toString()}', + ); + objStoreClearCompleter.completeError( + 'Object store clear not completed due to an error - ${request.error?.toString()}' + .toJS, + ); + }.toJS; + request.onsuccess = (Event event) { + objStoreClearCompleter.complete(); + }.toJS; + unawaited(objStoreClearCompleter.future); } - await txn.completed; + txn.onerror = (Event event) { + Logs().e('[IndexedDBBox] [clear] Error - ${txn.error?.toString()}'); + transactionCompleter.completeError( + 'DB clear transaction not completed due to an error - ${txn.error?.toString()}' + .toJS, + ); + }.toJS; + txn.oncomplete = (Event event) { + transactionCompleter.complete(); + }.toJS; + return transactionCompleter.future; } Future close() async { @@ -87,13 +152,24 @@ class BoxCollection with ZoneTransactionMixin { return zoneTransaction(() async => _db.close()); } - @Deprecated('use collection.deleteDatabase now') - static Future delete(String name, [dynamic factory]) => - (factory ?? window.indexedDB!).deleteDatabase(name); - Future deleteDatabase(String name, [dynamic factory]) async { await close(); - await (factory ?? window.indexedDB).deleteDatabase(name); + final deleteDatabaseCompleter = Completer(); + final request = + ((factory ?? window.indexedDB) as IDBFactory).deleteDatabase(name); + request.onerror = (Event event) { + Logs().e( + '[IndexedDBBox] [deleteDatabase] Error - ${request.error?.toString()}', + ); + deleteDatabaseCompleter.completeError( + 'Error deleting database - ${request.error?.toString()}'.toJS, + ); + }.toJS; + request.onsuccess = (Event event) { + Logs().i('[IndexedDBBox] [deleteDatabase] Database deleted.'); + deleteDatabaseCompleter.complete(); + }.toJS; + return deleteDatabaseCompleter.future; } } @@ -111,44 +187,109 @@ class Box { Box(this.name, this.boxCollection); - Future> getAllKeys([Transaction? txn]) async { + Future> getAllKeys([IDBTransaction? txn]) async { if (_quickAccessCachedKeys != null) return _quickAccessCachedKeys!.toList(); - txn ??= boxCollection._db.transaction(name, 'readonly'); + txn ??= boxCollection._db.transaction(name.toJS, 'readonly'); final store = txn.objectStore(name); - final request = store.getAllKeys(null); - await request.onSuccess.first; - final keys = request.result.cast(); + final getAllKeysCompleter = Completer(); + final request = store.getAllKeys(); + request.onerror = (Event event) { + Logs().e( + '[IndexedDBBox] [getAllKeys] Error - ${request.error?.toString()}', + ); + getAllKeysCompleter.completeError( + '[IndexedDBBox] [getAllKeys] Error - ${request.error?.toString()}'.toJS, + ); + }.toJS; + request.onsuccess = (Event event) { + getAllKeysCompleter.complete(); + }.toJS; + await getAllKeysCompleter.future; + final keys = (request.result?.dartify() as List?)?.cast() ?? []; _quickAccessCachedKeys = keys.toSet(); return keys; } - Future> getAllValues([Transaction? txn]) async { - txn ??= boxCollection._db.transaction(name, 'readonly'); + Future> getAllValues([IDBTransaction? txn]) async { + txn ??= boxCollection._db.transaction(name.toJS, 'readonly'); final store = txn.objectStore(name); final map = {}; - final cursorStream = store.openCursor(autoAdvance: true); - await for (final cursor in cursorStream) { - map[cursor.key as String] = _fromValue(cursor.value) as V; - } + + /// NOTE: This is a workaround to get the keys as [IDBObjectStore.getAll()] + /// only returns the values as a list. + /// And using the [IDBObjectStore.openCursor()] method is not working as expected. + final keys = await getAllKeys(txn); + + final getAllValuesCompleter = Completer(); + final getAllValuesRequest = store.getAll(); + getAllValuesRequest.onerror = (Event event) { + Logs().e( + '[IndexedDBBox] [getAllValues] Error - ${getAllValuesRequest.error?.toString()}', + ); + getAllValuesCompleter.completeError( + '[IndexedDBBox] [getAllValues] Error - ${getAllValuesRequest.error?.toString()}' + .toJS, + ); + }.toJS; + getAllValuesRequest.onsuccess = (Event event) { + final values = getAllValuesRequest.result.dartify() as List; + for (int i = 0; i < values.length; i++) { + map[keys[i]] = _fromValue(values[i]) as V; + } + getAllValuesCompleter.complete(); + }.toJS; + await getAllValuesCompleter.future; return map; } - Future get(String key, [Transaction? txn]) async { + Future get(String key, [IDBTransaction? txn]) async { if (_quickAccessCache.containsKey(key)) return _quickAccessCache[key]; - txn ??= boxCollection._db.transaction(name, 'readonly'); + txn ??= boxCollection._db.transaction(name.toJS, 'readonly'); final store = txn.objectStore(name); - _quickAccessCache[key] = await store.getObject(key).then(_fromValue); + final getObjectRequest = store.get(key.toJS); + final getObjectCompleter = Completer(); + getObjectRequest.onerror = (Event event) { + Logs().e( + '[IndexedDBBox] [get] Error - ${getObjectRequest.error?.toString()}', + ); + getObjectCompleter.completeError( + '[IndexedDBBox] [get] Error - ${getObjectRequest.error?.toString()}' + .toJS, + ); + }.toJS; + getObjectRequest.onsuccess = (Event event) { + getObjectCompleter.complete(); + }.toJS; + await getObjectCompleter.future; + _quickAccessCache[key] = _fromValue(getObjectRequest.result?.dartify()); return _quickAccessCache[key]; } - Future> getAll(List keys, [Transaction? txn]) async { + Future> getAll(List keys, [IDBTransaction? txn]) async { if (keys.every((key) => _quickAccessCache.containsKey(key))) { return keys.map((key) => _quickAccessCache[key]).toList(); } - txn ??= boxCollection._db.transaction(name, 'readonly'); + txn ??= boxCollection._db.transaction(name.toJS, 'readonly'); final store = txn.objectStore(name); final list = await Future.wait( - keys.map((key) => store.getObject(key).then(_fromValue)), + keys.map((key) async { + final getObjectRequest = store.get(key.toJS); + final getObjectCompleter = Completer(); + getObjectRequest.onerror = (Event event) { + Logs().e( + '[IndexedDBBox] [getAll] Error at key $key - ${getObjectRequest.error?.toString()}', + ); + getObjectCompleter.completeError( + '[IndexedDBBox] [getAll] Error at key $key - ${getObjectRequest.error?.toString()}' + .toJS, + ); + }.toJS; + getObjectRequest.onsuccess = (Event event) { + getObjectCompleter.complete(); + }.toJS; + await getObjectCompleter.future; + return _fromValue(getObjectRequest.result?.dartify()); + }), ); for (var i = 0; i < keys.length; i++) { _quickAccessCache[keys[i]] = list[i]; @@ -156,7 +297,7 @@ class Box { return list; } - Future put(String key, V val, [Transaction? txn]) async { + Future put(String key, V val, [IDBTransaction? txn]) async { if (boxCollection._txnCache != null) { boxCollection._txnCache!.add((txn) => put(key, val, txn)); _quickAccessCache[key] = val; @@ -164,15 +305,28 @@ class Box { return; } - txn ??= boxCollection._db.transaction(name, 'readwrite'); + txn ??= boxCollection._db.transaction(name.toJS, 'readwrite'); final store = txn.objectStore(name); - await store.put(val as Object, key); + final putRequest = store.put(val.jsify(), key.toJS); + final putCompleter = Completer(); + putRequest.onerror = (Event event) { + Logs().e( + '[IndexedDBBox] [put] Error - ${putRequest.error?.toString()}', + ); + putCompleter.completeError( + '[IndexedDBBox] [put] Error - ${putRequest.error?.toString()}'.toJS, + ); + }.toJS; + putRequest.onsuccess = (Event event) { + putCompleter.complete(); + }.toJS; + await putCompleter.future; _quickAccessCache[key] = val; _quickAccessCachedKeys?.add(key); return; } - Future delete(String key, [Transaction? txn]) async { + Future delete(String key, [IDBTransaction? txn]) async { if (boxCollection._txnCache != null) { boxCollection._txnCache!.add((txn) => delete(key, txn)); _quickAccessCache[key] = null; @@ -180,9 +334,23 @@ class Box { return; } - txn ??= boxCollection._db.transaction(name, 'readwrite'); + txn ??= boxCollection._db.transaction(name.toJS, 'readwrite'); final store = txn.objectStore(name); - await store.delete(key); + final deleteRequest = store.delete(key.toJS); + final deleteCompleter = Completer(); + deleteRequest.onerror = (Event event) { + Logs().e( + '[IndexedDBBox] [delete] Error - ${deleteRequest.error?.toString()}', + ); + deleteCompleter.completeError( + '[IndexedDBBox] [delete] Error - ${deleteRequest.error?.toString()}' + .toJS, + ); + }.toJS; + deleteRequest.onsuccess = (Event event) { + deleteCompleter.complete(); + }.toJS; + await deleteCompleter.future; // Set to null instead remove() so that inside of transactions null is // returned. @@ -191,7 +359,7 @@ class Box { return; } - Future deleteAll(List keys, [Transaction? txn]) async { + Future deleteAll(List keys, [IDBTransaction? txn]) async { if (boxCollection._txnCache != null) { boxCollection._txnCache!.add((txn) => deleteAll(keys, txn)); for (final key in keys) { @@ -201,10 +369,24 @@ class Box { return; } - txn ??= boxCollection._db.transaction(name, 'readwrite'); + txn ??= boxCollection._db.transaction(name.toJS, 'readwrite'); final store = txn.objectStore(name); for (final key in keys) { - await store.delete(key); + final deleteRequest = store.delete(key.toJS); + final deleteCompleter = Completer(); + deleteRequest.onerror = (Event event) { + Logs().e( + '[IndexedDBBox] [deleteAll] Error at key $key - ${deleteRequest.error?.toString()}', + ); + deleteCompleter.completeError( + '[IndexedDBBox] [deleteAll] Error at key $key - ${deleteRequest.error?.toString()}' + .toJS, + ); + }.toJS; + deleteRequest.onsuccess = (Event event) { + deleteCompleter.complete(); + }.toJS; + await deleteCompleter.future; _quickAccessCache[key] = null; _quickAccessCachedKeys?.remove(key); } @@ -216,15 +398,28 @@ class Box { _quickAccessCachedKeys = null; } - Future clear([Transaction? txn]) async { + Future clear([IDBTransaction? txn]) async { if (boxCollection._txnCache != null) { boxCollection._txnCache!.add((txn) => clear(txn)); } else { - txn ??= boxCollection._db.transaction(name, 'readwrite'); + txn ??= boxCollection._db.transaction(name.toJS, 'readwrite'); final store = txn.objectStore(name); - await store.clear(); + final clearRequest = store.clear(); + final clearCompleter = Completer(); + clearRequest.onerror = (Event event) { + Logs().e( + '[IndexedDBBox] [clear] Error - ${clearRequest.error?.toString()}', + ); + clearCompleter.completeError( + '[IndexedDBBox] [clear] Error - ${clearRequest.error?.toString()}' + .toJS, + ); + }.toJS; + clearRequest.onsuccess = (Event event) { + clearCompleter.complete(); + }.toJS; + await clearCompleter.future; } - clearQuickAccessCache(); } diff --git a/lib/src/database/matrix_sdk_database.dart b/lib/src/database/matrix_sdk_database.dart index 942418b8..fc2ddeaf 100644 --- a/lib/src/database/matrix_sdk_database.dart +++ b/lib/src/database/matrix_sdk_database.dart @@ -31,8 +31,8 @@ import 'package:matrix/src/utils/copy_map.dart'; import 'package:matrix/src/utils/queued_to_device_event.dart'; import 'package:matrix/src/utils/run_benchmarked.dart'; -import 'package:matrix/src/database/indexeddb_box.dart' - if (dart.library.io) 'package:matrix/src/database/sqflite_box.dart'; +import 'package:matrix/src/database/sqflite_box.dart' + if (dart.library.js_interop) 'package:matrix/src/database/indexeddb_box.dart'; import 'package:matrix/src/database/database_file_storage_stub.dart' if (dart.library.io) 'package:matrix/src/database/database_file_storage_io.dart'; @@ -167,8 +167,8 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { Database? database; - /// Custom IdbFactory used to create the indexedDB. On IO platforms it would - /// lead to an error to import "dart:indexed_db" so this is dynamically + /// Custom [IDBFactory] used to create the indexedDB. On IO platforms it would + /// lead to an error to import "package:web/web.dart" so this is dynamically /// typed. final dynamic idbFactory; diff --git a/lib/src/utils/crypto/crypto.dart b/lib/src/utils/crypto/crypto.dart index a97417ee..c8137934 100644 --- a/lib/src/utils/crypto/crypto.dart +++ b/lib/src/utils/crypto/crypto.dart @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -export 'native.dart' if (dart.library.js) 'js.dart'; +export 'native.dart' if (dart.library.js_interop) 'js.dart'; import 'dart:math'; import 'dart:typed_data'; diff --git a/lib/src/utils/web_worker/native_implementations_web_worker.dart b/lib/src/utils/web_worker/native_implementations_web_worker.dart index 6cd71a69..260eb45a 100644 --- a/lib/src/utils/web_worker/native_implementations_web_worker.dart +++ b/lib/src/utils/web_worker/native_implementations_web_worker.dart @@ -1,9 +1,11 @@ import 'dart:async'; import 'dart:collection'; -import 'dart:html'; +import 'dart:js_interop'; import 'dart:math'; import 'dart:typed_data'; +import 'package:web/web.dart'; + import 'package:matrix/matrix.dart'; class NativeImplementationsWebWorker extends NativeImplementations { @@ -23,8 +25,8 @@ class NativeImplementationsWebWorker extends NativeImplementations { Uri href, { this.timeout = const Duration(seconds: 30), this.onStackTrace = defaultStackTraceHandler, - }) : worker = Worker(href.toString()) { - worker.onMessage.listen(_handleIncomingMessage); + }) : worker = Worker(href.toString().toJS) { + worker.onmessage = _handleIncomingMessage.toJS; } Future operation(WebWorkerOperations name, U argument) async { @@ -32,27 +34,26 @@ class NativeImplementationsWebWorker extends NativeImplementations { final completer = Completer(); _completers[label] = completer; final message = WebWorkerData(label, name, argument); - worker.postMessage(message.toJson()); + worker.postMessage(message.toJson().jsify()); return completer.future.timeout(timeout); } - Future _handleIncomingMessage(MessageEvent event) async { - final data = event.data; + void _handleIncomingMessage(MessageEvent event) async { + final data = event.data.dartify() as LinkedHashMap; // don't forget handling errors of our second thread... if (data['label'] == 'stacktrace') { - final origin = event.data['origin']; + final origin = data['origin']; final completer = _completers[origin]; - final error = event.data['error']!; + final error = data['error']!; - final stackTrace = - await onStackTrace.call(event.data['stacktrace'] as String); + final stackTrace = await onStackTrace.call(data['stacktrace'] as String); completer?.completeError( WebWorkerError(error: error, stackTrace: stackTrace), ); } else { - final response = WebWorkerData.fromJson(event.data); + final response = WebWorkerData.fromJson(data); _completers[response.label]!.complete(response.data); } } diff --git a/lib/src/utils/web_worker/web_worker.dart b/lib/src/utils/web_worker/web_worker.dart index 89180856..64f7aca6 100644 --- a/lib/src/utils/web_worker/web_worker.dart +++ b/lib/src/utils/web_worker/web_worker.dart @@ -1,13 +1,11 @@ // ignore_for_file: avoid_print import 'dart:async'; -import 'dart:html'; -import 'dart:indexed_db'; -import 'dart:js'; +import 'dart:collection'; +import 'dart:js_interop'; import 'dart:typed_data'; -import 'package:js/js.dart'; -import 'package:js/js_util.dart'; +import 'package:web/web.dart'; import 'package:matrix/matrix.dart' hide Event; import 'package:matrix/src/utils/web_worker/native_implementations_web_worker.dart'; @@ -32,63 +30,73 @@ import 'package:matrix/src/utils/web_worker/native_implementations_web_worker.da /// the web worker in your CI pipeline. /// +DedicatedWorkerGlobalScope get _workerScope => + (globalContext as DedicatedWorkerGlobalScope).self + as DedicatedWorkerGlobalScope; + @pragma('dart2js:tryInline') Future startWebWorker() async { - print('[native implementations worker]: Starting...'); - setProperty( - context['self'] as Object, - 'onmessage', - allowInterop( - (MessageEvent event) async { - final data = event.data; - try { - final operation = WebWorkerData.fromJson(data); - switch (operation.name) { - case WebWorkerOperations.shrinkImage: - final result = MatrixImageFile.resizeImplementation( - MatrixImageFileResizeArguments.fromJson( - Map.from(operation.data as Map), - ), - ); - sendResponse(operation.label as double, result?.toJson()); - break; - case WebWorkerOperations.calcImageMetadata: - final result = MatrixImageFile.calcMetadataImplementation( - Uint8List.fromList( - (operation.data as JsArray).whereType().toList(), - ), - ); - sendResponse(operation.label as double, result?.toJson()); - break; - default: - throw TypeError(); - } - } on Event catch (e, s) { - allowInterop(_replyError) - .call((e.target as Request).error, s, data['label'] as double); - } catch (e, s) { - allowInterop(_replyError).call(e, s, data['label'] as double); - } - }, - ), - ); + Logs().i('[native implementations worker]: Starting...'); + _workerScope.onmessage = (MessageEvent event) { + final data = event.data.dartify() as LinkedHashMap; + try { + final operation = WebWorkerData.fromJson(data); + switch (operation.name) { + case WebWorkerOperations.shrinkImage: + final result = MatrixImageFile.resizeImplementation( + MatrixImageFileResizeArguments.fromJson( + Map.from(operation.data as Map), + ), + ); + _sendResponse( + operation.label as double, + result?.toJson(), + ); + break; + case WebWorkerOperations.calcImageMetadata: + final result = MatrixImageFile.calcMetadataImplementation( + Uint8List.fromList( + (operation.data as List).whereType().toList(), + ), + ); + _sendResponse( + operation.label as double, + result?.toJson(), + ); + break; + default: + throw TypeError(); + } + } catch (e, s) { + _replyError(e, s, data['label'] as double); + } + }.toJS; } -void sendResponse(double label, dynamic response) { +void _sendResponse( + double label, + dynamic response, +) { try { - self.postMessage({ - 'label': label, - 'data': response, - }); + _workerScope.postMessage( + { + 'label': label, + 'data': response, + }.jsify(), + ); } catch (e, s) { - print('[native implementations worker] Error responding: $e, $s'); + Logs().e('[native implementations worker] Error responding: $e, $s'); } } -void _replyError(Object? error, StackTrace stackTrace, double origin) { +void _replyError( + Object? error, + StackTrace stackTrace, + double origin, +) { if (error != null) { try { - final jsError = jsify(error); + final jsError = error.jsify(); if (jsError != null) { error = jsError; } @@ -97,24 +105,15 @@ void _replyError(Object? error, StackTrace stackTrace, double origin) { } } try { - self.postMessage({ - 'label': 'stacktrace', - 'origin': origin, - 'error': error, - 'stacktrace': stackTrace.toString(), - }); + _workerScope.postMessage( + { + 'label': 'stacktrace', + 'origin': origin, + 'error': error, + 'stacktrace': stackTrace.toString(), + }.jsify(), + ); } catch (e, s) { - print('[native implementations worker] Error responding: $e, $s'); - } -} - -/// represents the [WorkerGlobalScope] the worker currently runs in. -@JS('self') -external WorkerGlobalScope get self; - -/// adding all missing WebWorker-only properties to the [WorkerGlobalScope] -extension on WorkerGlobalScope { - void postMessage(Object data) { - callMethod(self, 'postMessage', [jsify(data)]); + Logs().e('[native implementations worker] Error responding: $e, $s'); } } diff --git a/lib/src/voip/utils/wrapped_media_stream.dart b/lib/src/voip/utils/wrapped_media_stream.dart index e8eb0f3a..836cb690 100644 --- a/lib/src/voip/utils/wrapped_media_stream.dart +++ b/lib/src/voip/utils/wrapped_media_stream.dart @@ -47,7 +47,7 @@ class WrappedMediaStream { Future dispose() async { // AOT it - const isWeb = bool.fromEnvironment('dart.library.js_util'); + const isWeb = bool.fromEnvironment('dart.library.js_interop'); // libwebrtc does not provide a way to clone MediaStreams. So stopping the // local stream here would break calls with all other participants if anyone diff --git a/pubspec.yaml b/pubspec.yaml index 58922dc9..af0b6f27 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: http: ">=0.13.0 <2.0.0" image: ^4.0.15 js: ^0.6.3 + js_interop: ^0.0.1 markdown: ^7.1.1 mime: ">=1.0.0 <3.0.0" path: ^1.9.1 @@ -32,6 +33,7 @@ dependencies: sqlite3: ^2.1.0 typed_data: ^1.3.2 vodozemac: ^0.2.0 + web: ^1.1.1 webrtc_interface: ^1.2.0 dev_dependencies: diff --git a/test/box_test.dart b/test/box_test.dart index 135b2526..e2e733a5 100644 --- a/test/box_test.dart +++ b/test/box_test.dart @@ -1,8 +1,8 @@ import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:test/test.dart'; -import 'package:matrix/src/database/indexeddb_box.dart' - if (dart.library.io) 'package:matrix/src/database/sqflite_box.dart'; +import 'package:matrix/src/database/sqflite_box.dart' + if (dart.library.js_interop) 'package:matrix/src/database/indexeddb_box.dart'; void main() { group('Box tests', () { @@ -11,7 +11,7 @@ void main() { const data = {'name': 'Fluffy', 'age': 2}; const data2 = {'name': 'Loki', 'age': 4}; Database? db; - const isWeb = bool.fromEnvironment('dart.library.js_util'); + const isWeb = bool.fromEnvironment('dart.library.js_interop'); setUp(() async { if (!isWeb) { db = await databaseFactoryFfi.openDatabase(':memory:');