refactor: Move file storage to mixin to not import dart:io
Original issue and former solution by The one with the braid <info@braid.business> Special thanks for pointing out the problem. This fixes that dart:io is imported into the SDK database by moving it into it's own mixin which makes it reusable and platform independent by using conditional imports.
This commit is contained in:
parent
87c0e7fbe8
commit
49e6d55d32
|
|
@ -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<void> 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<Uint8List?> 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<void> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
mixin DatabaseFileStorage {
|
||||
bool get supportsFileStoring => false;
|
||||
|
||||
late final Uri? fileStorageLocation;
|
||||
late final Duration? deleteFilesAfterDuration;
|
||||
|
||||
Future<void> storeFile(Uri mxcUri, Uint8List bytes, int time) async {
|
||||
return;
|
||||
}
|
||||
|
||||
Future<Uint8List?> getFile(Uri mxcUri) async {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> deleteOldFiles(int savedAt) async {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
// This is a stub implementation of all filesystem related calls done
|
||||
// by the matrix SDK database. This fake implementation ensures we can compile
|
||||
// using dart2js.
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
class File extends FileSystemEntry {
|
||||
const File(super.path);
|
||||
|
||||
Future<Uint8List> readAsBytes() async => Uint8List(0);
|
||||
|
||||
Future<void> writeAsBytes(Uint8List data) async => Future.value();
|
||||
}
|
||||
|
||||
class Directory extends FileSystemEntry {
|
||||
const Directory(super.path);
|
||||
|
||||
Stream<FileSystemEntry> list() async* {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class FileSystemEntry {
|
||||
final String path;
|
||||
|
||||
const FileSystemEntry(this.path);
|
||||
|
||||
Future<void> delete() => Future.value();
|
||||
|
||||
Future<FileStat> stat() async => FileStat();
|
||||
|
||||
Future<bool> exists() async => false;
|
||||
}
|
||||
|
||||
class FileStat {
|
||||
final modified = DateTime.fromMillisecondsSinceEpoch(0);
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
export 'dart:io';
|
||||
|
|
@ -19,7 +19,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:sqflite_common/sqflite.dart';
|
||||
|
||||
|
|
@ -35,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:
|
||||
|
|
@ -49,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,8 +102,6 @@ class MatrixSdkDatabase extends DatabaseApi {
|
|||
|
||||
late Box<String> _seenDeviceKeysBox;
|
||||
|
||||
@override
|
||||
bool get supportsFileStoring => fileStorageLocation != null;
|
||||
@override
|
||||
final int maxFileSize;
|
||||
|
||||
|
|
@ -111,18 +111,6 @@ class MatrixSdkDatabase extends DatabaseApi {
|
|||
'Breaks support for web standalone. Use [fileStorageLocation] instead.')
|
||||
Object? get fileStoragePath => fileStorageLocation?.toFilePath();
|
||||
|
||||
/// A [Uri] defining the file storage location.
|
||||
///
|
||||
/// Unless you support custom Uri schemes, this should usually be a
|
||||
/// [Uri.directory] identifier.
|
||||
///
|
||||
/// Using a [Uri] as type here enables users to technically extend the API to
|
||||
/// support file storage on non-io platforms as well - or to use non-File
|
||||
/// based storage mechanisms on io.
|
||||
final Uri? fileStorageLocation;
|
||||
|
||||
final Duration? deleteFilesAfterDuration;
|
||||
|
||||
static const String _clientBoxName = 'box_client';
|
||||
|
||||
static const String _accountDataBoxName = 'box_account_data';
|
||||
|
|
@ -185,10 +173,17 @@ class MatrixSdkDatabase extends DatabaseApi {
|
|||
this.sqfliteFactory,
|
||||
this.maxFileSize = 0,
|
||||
// TODO : remove deprecated member migration on next major release
|
||||
@Deprecated(
|
||||
'Breaks support for web standalone. Use [fileStorageLocation] instead.')
|
||||
dynamic fileStoragePath,
|
||||
Uri? fileStorageLocation,
|
||||
this.deleteFilesAfterDuration,
|
||||
}) : fileStorageLocation = fileStorageLocation ?? fileStoragePath?.path;
|
||||
Duration? deleteFilesAfterDuration,
|
||||
}) {
|
||||
final legacyPath = fileStoragePath?.path;
|
||||
this.fileStorageLocation = fileStorageLocation ??
|
||||
(legacyPath is String ? Uri.tryParse(legacyPath) : null);
|
||||
this.deleteFilesAfterDuration = deleteFilesAfterDuration;
|
||||
}
|
||||
|
||||
Future<void> open() async {
|
||||
_collection = await BoxCollection.open(
|
||||
|
|
@ -327,28 +322,6 @@ class MatrixSdkDatabase extends DatabaseApi {
|
|||
return;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteOldFiles(int savedAt) async {
|
||||
if (_kIsWeb) return;
|
||||
final path = fileStorageLocation?.toFilePath();
|
||||
final deleteFilesAfterDuration = this.deleteFilesAfterDuration;
|
||||
if (!supportsFileStoring ||
|
||||
path == null ||
|
||||
deleteFilesAfterDuration == null) {
|
||||
return;
|
||||
}
|
||||
final dir = Directory(path);
|
||||
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<void> forgetRoom(String roomId) async {
|
||||
await _timelineFragmentsBox.delete(TupleKey(roomId, '').toString());
|
||||
|
|
@ -471,18 +444,6 @@ class MatrixSdkDatabase extends DatabaseApi {
|
|||
return await _getEventsByIds(eventIds.cast<String>(), room);
|
||||
});
|
||||
|
||||
@override
|
||||
Future<Uint8List?> getFile(Uri mxcUri) async {
|
||||
if (_kIsWeb) return null;
|
||||
final path = fileStorageLocation?.toFilePath();
|
||||
if (!supportsFileStoring || path == null) return null;
|
||||
|
||||
final file = File('$path/${mxcUri.toString().split('/').last}');
|
||||
|
||||
if (await file.exists()) return await file.readAsBytes();
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<StoredInboundGroupSession?> getInboundGroupSession(
|
||||
String roomId,
|
||||
|
|
@ -1156,18 +1117,6 @@ class MatrixSdkDatabase extends DatabaseApi {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> storeFile(Uri mxcUri, Uint8List bytes, int time) async {
|
||||
if (_kIsWeb) return;
|
||||
final path = fileStorageLocation?.toFilePath();
|
||||
if (!supportsFileStoring || path == null) return;
|
||||
|
||||
final file = File('$path/${mxcUri.toString().split('/').last}');
|
||||
|
||||
if (await file.exists()) return;
|
||||
await file.writeAsBytes(bytes);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> storeInboundGroupSession(
|
||||
String roomId,
|
||||
|
|
|
|||
Loading…
Reference in New Issue