Merge pull request #2153 from famedly/karthi/web-js-interop-pkgs

refactor: migrate to web and js_interop pkgs
This commit is contained in:
Krille-chan 2025-09-26 15:51:55 +02:00 committed by GitHub
commit cde303c939
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 364 additions and 164 deletions

View File

@ -748,9 +748,10 @@ class KeyVerification {
// no need to request cache, we already have it
return;
}
// ignore: unawaited_futures
encryption.ssss
.maybeRequestAll(_verifiedDevices.whereType<DeviceKeys>().toList());
unawaited(
encryption.ssss
.maybeRequestAll(_verifiedDevices.whereType<DeviceKeys>().toList()),
);
if (requestInterval.length <= i) {
return;
}

View File

@ -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';

View File

@ -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,

View File

@ -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;
}
}

View File

@ -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<String> boxNames;
final String name;
@ -18,23 +20,45 @@ class BoxCollection with ZoneTransactionMixin {
Set<String> 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<BoxCollection>();
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<V> openBox<V>(String name) {
@ -44,7 +68,7 @@ class BoxCollection with ZoneTransactionMixin {
return Box<V>(name, this);
}
List<Future<void> Function(Transaction txn)>? _txnCache;
List<Future<void> Function(IDBTransaction txn)>? _txnCache;
Future<void> transaction(
Future<void> 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<Future<void> Function(Transaction txn)>.from(txnCache);
List<Future<void> Function(IDBTransaction txn)>.from(txnCache);
_txnCache = null;
if (cache.isEmpty) return;
final txn =
_db.transaction(boxNames, readOnly ? 'readonly' : 'readwrite');
final transactionCompleter = Completer<void>();
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<void> 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<void> close() async {
@ -87,13 +152,24 @@ class BoxCollection with ZoneTransactionMixin {
return zoneTransaction(() async => _db.close());
}
@Deprecated('use collection.deleteDatabase now')
static Future<void> delete(String name, [dynamic factory]) =>
(factory ?? window.indexedDB!).deleteDatabase(name);
Future<void> 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<V> {
Box(this.name, this.boxCollection);
Future<List<String>> getAllKeys([Transaction? txn]) async {
Future<List<String>> 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<String>();
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<String>() ?? [];
_quickAccessCachedKeys = keys.toSet();
return keys;
}
Future<Map<String, V>> getAllValues([Transaction? txn]) async {
txn ??= boxCollection._db.transaction(name, 'readonly');
Future<Map<String, V>> getAllValues([IDBTransaction? txn]) async {
txn ??= boxCollection._db.transaction(name.toJS, 'readonly');
final store = txn.objectStore(name);
final map = <String, V>{};
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<V?> get(String key, [Transaction? txn]) async {
Future<V?> 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<List<V?>> getAll(List<String> keys, [Transaction? txn]) async {
Future<List<V?>> getAll(List<String> 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<V> {
return list;
}
Future<void> put(String key, V val, [Transaction? txn]) async {
Future<void> 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<V> {
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<void> delete(String key, [Transaction? txn]) async {
Future<void> 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<V> {
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<V> {
return;
}
Future<void> deleteAll(List<String> keys, [Transaction? txn]) async {
Future<void> deleteAll(List<String> 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<V> {
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<V> {
_quickAccessCachedKeys = null;
}
Future<void> clear([Transaction? txn]) async {
Future<void> 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();
}

View File

@ -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;

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
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';

View File

@ -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<T> operation<T, U>(WebWorkerOperations name, U argument) async {
@ -32,27 +34,26 @@ class NativeImplementationsWebWorker extends NativeImplementations {
final completer = Completer<T>();
_completers[label] = completer;
final message = WebWorkerData(label, name, argument);
worker.postMessage(message.toJson());
worker.postMessage(message.toJson().jsify());
return completer.future.timeout(timeout);
}
Future<void> _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);
}
}

View File

@ -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<void> 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<int>().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<int>().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');
}
}

View File

@ -47,7 +47,7 @@ class WrappedMediaStream {
Future<void> 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

View File

@ -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:

View File

@ -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:');