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:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:sqflite_common/sqflite.dart';
|
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'
|
import 'package:matrix/src/database/indexeddb_box.dart'
|
||||||
if (dart.library.io) 'package:matrix/src/database/sqflite_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
|
/// 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
|
/// have to pass a `Database` object, which can be created with the sqflite
|
||||||
/// package like this:
|
/// 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).
|
/// [sqflite_common_ffi](https://pub.dev/packages/sqflite_common_ffi).
|
||||||
/// Learn more at:
|
/// Learn more at:
|
||||||
/// https://github.com/famedly/matrix-dart-sdk/issues/1642#issuecomment-1865827227
|
/// 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;
|
static const int version = 7;
|
||||||
final String name;
|
final String name;
|
||||||
late BoxCollection _collection;
|
late BoxCollection _collection;
|
||||||
|
|
@ -100,8 +102,6 @@ class MatrixSdkDatabase extends DatabaseApi {
|
||||||
|
|
||||||
late Box<String> _seenDeviceKeysBox;
|
late Box<String> _seenDeviceKeysBox;
|
||||||
|
|
||||||
@override
|
|
||||||
bool get supportsFileStoring => fileStorageLocation != null;
|
|
||||||
@override
|
@override
|
||||||
final int maxFileSize;
|
final int maxFileSize;
|
||||||
|
|
||||||
|
|
@ -111,18 +111,6 @@ class MatrixSdkDatabase extends DatabaseApi {
|
||||||
'Breaks support for web standalone. Use [fileStorageLocation] instead.')
|
'Breaks support for web standalone. Use [fileStorageLocation] instead.')
|
||||||
Object? get fileStoragePath => fileStorageLocation?.toFilePath();
|
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 _clientBoxName = 'box_client';
|
||||||
|
|
||||||
static const String _accountDataBoxName = 'box_account_data';
|
static const String _accountDataBoxName = 'box_account_data';
|
||||||
|
|
@ -185,10 +173,17 @@ class MatrixSdkDatabase extends DatabaseApi {
|
||||||
this.sqfliteFactory,
|
this.sqfliteFactory,
|
||||||
this.maxFileSize = 0,
|
this.maxFileSize = 0,
|
||||||
// TODO : remove deprecated member migration on next major release
|
// TODO : remove deprecated member migration on next major release
|
||||||
|
@Deprecated(
|
||||||
|
'Breaks support for web standalone. Use [fileStorageLocation] instead.')
|
||||||
dynamic fileStoragePath,
|
dynamic fileStoragePath,
|
||||||
Uri? fileStorageLocation,
|
Uri? fileStorageLocation,
|
||||||
this.deleteFilesAfterDuration,
|
Duration? deleteFilesAfterDuration,
|
||||||
}) : fileStorageLocation = fileStorageLocation ?? fileStoragePath?.path;
|
}) {
|
||||||
|
final legacyPath = fileStoragePath?.path;
|
||||||
|
this.fileStorageLocation = fileStorageLocation ??
|
||||||
|
(legacyPath is String ? Uri.tryParse(legacyPath) : null);
|
||||||
|
this.deleteFilesAfterDuration = deleteFilesAfterDuration;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> open() async {
|
Future<void> open() async {
|
||||||
_collection = await BoxCollection.open(
|
_collection = await BoxCollection.open(
|
||||||
|
|
@ -327,28 +322,6 @@ class MatrixSdkDatabase extends DatabaseApi {
|
||||||
return;
|
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
|
@override
|
||||||
Future<void> forgetRoom(String roomId) async {
|
Future<void> forgetRoom(String roomId) async {
|
||||||
await _timelineFragmentsBox.delete(TupleKey(roomId, '').toString());
|
await _timelineFragmentsBox.delete(TupleKey(roomId, '').toString());
|
||||||
|
|
@ -471,18 +444,6 @@ class MatrixSdkDatabase extends DatabaseApi {
|
||||||
return await _getEventsByIds(eventIds.cast<String>(), room);
|
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
|
@override
|
||||||
Future<StoredInboundGroupSession?> getInboundGroupSession(
|
Future<StoredInboundGroupSession?> getInboundGroupSession(
|
||||||
String roomId,
|
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
|
@override
|
||||||
Future<void> storeInboundGroupSession(
|
Future<void> storeInboundGroupSession(
|
||||||
String roomId,
|
String roomId,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue