diff --git a/.github/workflows/app.yml b/.github/workflows/app.yml index ceba9223..6640508e 100644 --- a/.github/workflows/app.yml +++ b/.github/workflows/app.yml @@ -47,6 +47,11 @@ jobs: rm -r example ./scripts/prepare.sh ./scripts/test.sh + - name: Ensure SDK compiles on web + run: | + pushd web_test + dart pub get + dart run webdev build coverage_without_olm: runs-on: ubuntu-latest diff --git a/lib/src/database/database_file_storage_io.dart b/lib/src/database/database_file_storage_io.dart new file mode 100644 index 00000000..6a80efd7 --- /dev/null +++ b/lib/src/database/database_file_storage_io.dart @@ -0,0 +1,55 @@ +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:matrix/matrix.dart'; + +mixin DatabaseFileStorage { + bool get supportsFileStoring => fileStorageLocation != null; + + late final Uri? fileStorageLocation; + late final Duration? deleteFilesAfterDuration; + + Future storeFile(Uri mxcUri, Uint8List bytes, int time) async { + final fileStorageLocation = this.fileStorageLocation; + if (!supportsFileStoring || fileStorageLocation == null) return; + + final dir = Directory.fromUri(fileStorageLocation); + + final file = File('${dir.path}/${mxcUri.toString().split('/').last}'); + + if (await file.exists()) return; + await file.writeAsBytes(bytes); + } + + Future getFile(Uri mxcUri) async { + final fileStorageLocation = this.fileStorageLocation; + if (!supportsFileStoring || fileStorageLocation == null) return null; + + final dir = Directory.fromUri(fileStorageLocation); + + final file = File('${dir.path}/${mxcUri.toString().split('/').last}'); + + if (await file.exists()) return await file.readAsBytes(); + return null; + } + + Future deleteOldFiles(int savedAt) async { + final dirUri = fileStorageLocation; + final deleteFilesAfterDuration = this.deleteFilesAfterDuration; + if (!supportsFileStoring || + dirUri == null || + deleteFilesAfterDuration == null) { + return; + } + final dir = Directory.fromUri(dirUri); + final entities = await dir.list().toList(); + for (final file in entities) { + if (file is! File) continue; + final stat = await file.stat(); + if (DateTime.now().difference(stat.modified) > deleteFilesAfterDuration) { + Logs().v('Delete old file', file.path); + await file.delete(); + } + } + } +} diff --git a/lib/src/database/database_file_storage_stub.dart b/lib/src/database/database_file_storage_stub.dart new file mode 100644 index 00000000..7addfe1a --- /dev/null +++ b/lib/src/database/database_file_storage_stub.dart @@ -0,0 +1,20 @@ +import 'dart:typed_data'; + +mixin DatabaseFileStorage { + bool get supportsFileStoring => false; + + late final Uri? fileStorageLocation; + late final Duration? deleteFilesAfterDuration; + + Future storeFile(Uri mxcUri, Uint8List bytes, int time) async { + return; + } + + Future getFile(Uri mxcUri) async { + return null; + } + + Future deleteOldFiles(int savedAt) async { + return; + } +} diff --git a/lib/src/database/matrix_sdk_database.dart b/lib/src/database/matrix_sdk_database.dart index 4a230769..96b6e28e 100644 --- a/lib/src/database/matrix_sdk_database.dart +++ b/lib/src/database/matrix_sdk_database.dart @@ -18,9 +18,7 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:io'; import 'dart:math'; -import 'dart:typed_data'; import 'package:sqflite_common/sqflite.dart'; @@ -36,6 +34,9 @@ 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/database_file_storage_stub.dart' + if (dart.library.io) 'package:matrix/src/database/database_file_storage_io.dart'; + /// Database based on SQlite3 on native and IndexedDB on web. For native you /// have to pass a `Database` object, which can be created with the sqflite /// package like this: @@ -50,7 +51,7 @@ import 'package:matrix/src/database/indexeddb_box.dart' /// [sqflite_common_ffi](https://pub.dev/packages/sqflite_common_ffi). /// Learn more at: /// https://github.com/famedly/matrix-dart-sdk/issues/1642#issuecomment-1865827227 -class MatrixSdkDatabase extends DatabaseApi { +class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { static const int version = 7; final String name; late BoxCollection _collection; @@ -100,12 +101,15 @@ class MatrixSdkDatabase extends DatabaseApi { late Box _seenDeviceIdsBox; late Box _seenDeviceKeysBox; - @override - bool get supportsFileStoring => fileStoragePath != null; + @override final int maxFileSize; - final Directory? fileStoragePath; - final Duration? deleteFilesAfterDuration; + + // there was a field of type `dart:io:Directory` here. This one broke the + // dart js standalone compiler. Migration via URI as file system identifier. + @Deprecated( + 'Breaks support for web standalone. Use [fileStorageLocation] instead.') + Object? get fileStoragePath => fileStorageLocation?.toFilePath(); static const String _clientBoxName = 'box_client'; @@ -168,9 +172,18 @@ class MatrixSdkDatabase extends DatabaseApi { this.idbFactory, this.sqfliteFactory, this.maxFileSize = 0, - this.fileStoragePath, - this.deleteFilesAfterDuration, - }); + // TODO : remove deprecated member migration on next major release + @Deprecated( + 'Breaks support for web standalone. Use [fileStorageLocation] instead.') + dynamic fileStoragePath, + Uri? fileStorageLocation, + Duration? deleteFilesAfterDuration, + }) { + final legacyPath = fileStoragePath?.path; + this.fileStorageLocation = fileStorageLocation ?? + (legacyPath is String ? Uri.tryParse(legacyPath) : null); + this.deleteFilesAfterDuration = deleteFilesAfterDuration; + } Future open() async { _collection = await BoxCollection.open( @@ -309,26 +322,6 @@ class MatrixSdkDatabase extends DatabaseApi { return; } - @override - Future deleteOldFiles(int savedAt) async { - final dir = fileStoragePath; - final deleteFilesAfterDuration = this.deleteFilesAfterDuration; - if (!supportsFileStoring || - dir == null || - deleteFilesAfterDuration == null) { - return; - } - final entities = await dir.list().toList(); - for (final file in entities) { - if (file is! File) continue; - final stat = await file.stat(); - if (DateTime.now().difference(stat.modified) > deleteFilesAfterDuration) { - Logs().v('Delete old file', file.path); - await file.delete(); - } - } - } - @override Future forgetRoom(String roomId) async { await _timelineFragmentsBox.delete(TupleKey(roomId, '').toString()); @@ -451,18 +444,6 @@ class MatrixSdkDatabase extends DatabaseApi { return await _getEventsByIds(eventIds.cast(), room); }); - @override - Future getFile(Uri mxcUri) async { - final fileStoragePath = this.fileStoragePath; - if (!supportsFileStoring || fileStoragePath == null) return null; - - final file = - File('${fileStoragePath.path}/${mxcUri.toString().split('/').last}'); - - if (await file.exists()) return await file.readAsBytes(); - return null; - } - @override Future getInboundGroupSession( String roomId, @@ -1136,18 +1117,6 @@ class MatrixSdkDatabase extends DatabaseApi { } } - @override - Future storeFile(Uri mxcUri, Uint8List bytes, int time) async { - final fileStoragePath = this.fileStoragePath; - if (!supportsFileStoring || fileStoragePath == null) return; - - final file = - File('${fileStoragePath.path}/${mxcUri.toString().split('/').last}'); - - if (await file.exists()) return; - await file.writeAsBytes(bytes); - } - @override Future storeInboundGroupSession( String roomId, diff --git a/web_test/.gitignore b/web_test/.gitignore new file mode 100644 index 00000000..3a857904 --- /dev/null +++ b/web_test/.gitignore @@ -0,0 +1,3 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ diff --git a/web_test/README.md b/web_test/README.md new file mode 100644 index 00000000..c1528cc7 --- /dev/null +++ b/web_test/README.md @@ -0,0 +1 @@ +This is a bare-bone sample project in order to ensure webdev can compile the SDK. \ No newline at end of file diff --git a/web_test/pubspec.yaml b/web_test/pubspec.yaml new file mode 100644 index 00000000..af7b3090 --- /dev/null +++ b/web_test/pubspec.yaml @@ -0,0 +1,17 @@ +name: web_test +description: A test project for the webdev compiler. +version: 1.0.0 +publish_to: none + +environment: + sdk: ^3.2.0 + +# Add regular dependencies here. +dependencies: + matrix: + path: .. + +dev_dependencies: + build_runner: ^2.4.9 + build_web_compilers: ^4.0.10 + webdev: ^3.4.0 diff --git a/web_test/web/main.dart b/web_test/web/main.dart new file mode 100644 index 00000000..6873007d --- /dev/null +++ b/web_test/web/main.dart @@ -0,0 +1,6 @@ +import 'package:matrix/matrix.dart'; + +Future main() async { + final client = Client('web_test'); + await client.init(); +}