refactor: migrate to web and js_interop pkgs
This commit is contained in:
parent
a782e196d5
commit
6b73fc6354
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:');
|
||||
|
|
|
|||
Loading…
Reference in New Issue