diff --git a/mobile/build.yaml b/mobile/build.yaml index 76cc0a998..cb718d9d3 100644 --- a/mobile/build.yaml +++ b/mobile/build.yaml @@ -19,6 +19,7 @@ targets: - lib/infrastructure/entities/*.dart - lib/infrastructure/entities/*.drift - lib/infrastructure/repositories/db.repository.dart + - lib/infrastructure/repositories/logger_db.repository.dart drift_dev:modular: enabled: true options: *drift_options diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index d984b468d..616a306d9 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -3,7 +3,7 @@ const double downloadCompleted = -1; const double downloadFailed = -2; // Number of log entries to retain on app start -const int kLogTruncateLimit = 250; +const int kLogTruncateLimit = 2000; // Sync const int kSyncEventBatchSize = 5000; diff --git a/mobile/lib/domain/services/hash.service.dart b/mobile/lib/domain/services/hash.service.dart index 36a4b9efb..a8eea2c25 100644 --- a/mobile/lib/domain/services/hash.service.dart +++ b/mobile/lib/domain/services/hash.service.dart @@ -6,7 +6,6 @@ import 'package:immich_mobile/infrastructure/repositories/local_album.repository import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:logging/logging.dart'; class HashService { @@ -46,7 +45,6 @@ class HashService { stopwatch.stop(); _log.info("Hashing took - ${stopwatch.elapsedMilliseconds}ms"); - DLog.log("Hashing took - ${stopwatch.elapsedMilliseconds}ms"); } /// Processes a list of [LocalAsset]s, storing their hash and updating the assets in the DB @@ -101,7 +99,6 @@ class HashService { } _log.fine("Hashed ${hashed.length}/${toHash.length} assets"); - DLog.log("Hashed ${hashed.length}/${toHash.length} assets"); await _localAssetRepository.updateHashes(hashed); await _storageRepository.clearCache(); diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart index 13ebecfd4..119954cb4 100644 --- a/mobile/lib/domain/services/local_sync.service.dart +++ b/mobile/lib/domain/services/local_sync.service.dart @@ -6,7 +6,6 @@ import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:logging/logging.dart'; import 'package:platform/platform.dart'; @@ -30,19 +29,17 @@ class LocalSyncService { try { if (full || await _nativeSyncApi.shouldFullSync()) { _log.fine("Full sync request from ${full ? "user" : "native"}"); - DLog.log("Full sync request from ${full ? "user" : "native"}"); return await fullSync(); } final delta = await _nativeSyncApi.getMediaChanges(); if (!delta.hasChanges) { _log.fine("No media changes detected. Skipping sync"); - DLog.log("No media changes detected. Skipping sync"); return; } - DLog.log("Delta updated: ${delta.updates.length}"); - DLog.log("Delta deleted: ${delta.deletes.length}"); + _log.fine("Delta updated: ${delta.updates.length}"); + _log.fine("Delta deleted: ${delta.deletes.length}"); final deviceAlbums = await _nativeSyncApi.getAlbums(); await _localAlbumRepository.updateAll(deviceAlbums.toLocalAlbums()); @@ -83,7 +80,6 @@ class LocalSyncService { } finally { stopwatch.stop(); _log.info("Device sync took - ${stopwatch.elapsedMilliseconds}ms"); - DLog.log("Device sync took - ${stopwatch.elapsedMilliseconds}ms"); } } @@ -106,7 +102,6 @@ class LocalSyncService { await _nativeSyncApi.checkpointSync(); stopwatch.stop(); _log.info("Full device sync took - ${stopwatch.elapsedMilliseconds}ms"); - DLog.log("Full device sync took - ${stopwatch.elapsedMilliseconds}ms"); } catch (e, s) { _log.severe("Error performing full device sync", e, s); } @@ -150,7 +145,6 @@ class LocalSyncService { // Faster path - only new assets added if (await checkAddition(dbAlbum, deviceAlbum)) { _log.fine("Fast synced device album ${dbAlbum.name}"); - DLog.log("Fast synced device album ${dbAlbum.name}"); return true; } diff --git a/mobile/lib/domain/services/log.service.dart b/mobile/lib/domain/services/log.service.dart index ff72ec550..98cb24d9c 100644 --- a/mobile/lib/domain/services/log.service.dart +++ b/mobile/lib/domain/services/log.service.dart @@ -14,7 +14,7 @@ import 'package:logging/logging.dart'; /// writes them to a persistent [ILogRepository], and manages log levels /// via [IStoreRepository] class LogService { - final IsarLogRepository _logRepository; + final LogRepository _logRepository; final IsarStoreRepository _storeRepository; final List _msgBuffer = []; @@ -37,7 +37,7 @@ class LogService { } static Future init({ - required IsarLogRepository logRepository, + required LogRepository logRepository, required IsarStoreRepository storeRepository, bool shouldBuffer = true, }) async { @@ -50,7 +50,7 @@ class LogService { } static Future create({ - required IsarLogRepository logRepository, + required LogRepository logRepository, required IsarStoreRepository storeRepository, bool shouldBuffer = true, }) async { @@ -85,7 +85,7 @@ class LogService { if (_shouldBuffer) { _msgBuffer.add(record); - _flushTimer ??= Timer(const Duration(seconds: 5), () => unawaited(flushBuffer())); + _flushTimer ??= Timer(const Duration(seconds: 5), () => unawaited(_flushBuffer())); } else { unawaited(_logRepository.insert(record)); } @@ -108,20 +108,18 @@ class LogService { await _logRepository.deleteAll(); } - void flush() { + Future flush() { _flushTimer?.cancel(); - // TODO: Rename enable this after moving to sqlite - #16504 - // await _flushBufferToDatabase(); + return _flushBuffer(); } Future dispose() { _flushTimer?.cancel(); _logSubscription.cancel(); - return flushBuffer(); + return _flushBuffer(); } - // TOOD: Move this to private once Isar is removed - Future flushBuffer() async { + Future _flushBuffer() async { _flushTimer = null; final buffer = [..._msgBuffer]; _msgBuffer.clear(); diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index 7c25fbb34..5625635e4 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:immich_mobile/domain/models/sync_event.model.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; @@ -26,7 +25,6 @@ class SyncStreamService { Future sync() { _logger.info("Remote sync request for user"); - DLog.log("Remote sync request for user"); // Start the sync stream and handle events return _syncApiRepository.streamChanges(_handleEvents); } diff --git a/mobile/lib/infrastructure/entities/log.entity.dart b/mobile/lib/infrastructure/entities/log.entity.dart index 6a38924e2..e57845982 100644 --- a/mobile/lib/infrastructure/entities/log.entity.dart +++ b/mobile/lib/infrastructure/entities/log.entity.dart @@ -1,47 +1,29 @@ -import 'package:immich_mobile/domain/models/log.model.dart'; -import 'package:isar/isar.dart'; +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart'; +import 'package:immich_mobile/domain/models/log.model.dart' as domain; -part 'log.entity.g.dart'; +class LogMessageEntity extends Table { + const LogMessageEntity(); -@Collection(inheritance: false) -class LoggerMessage { - final Id id = Isar.autoIncrement; - final String message; - final String? details; - @Enumerated(EnumType.ordinal) - final LogLevel level; - final DateTime createdAt; - final String? context1; - final String? context2; + @override + String get tableName => 'logger_messages'; - const LoggerMessage({ - required this.message, - required this.details, - this.level = LogLevel.info, - required this.createdAt, - required this.context1, - required this.context2, - }); - - LogMessage toDto() { - return LogMessage( - message: message, - level: level, - createdAt: createdAt, - logger: context1, - error: details, - stack: context2, - ); - } - - static LoggerMessage fromDto(LogMessage log) { - return LoggerMessage( - message: log.message, - details: log.error, - level: log.level, - createdAt: log.createdAt, - context1: log.logger, - context2: log.stack, - ); - } + IntColumn get id => integer().autoIncrement()(); + TextColumn get message => text()(); + TextColumn get details => text().nullable()(); + IntColumn get level => intEnum()(); + DateTimeColumn get createdAt => dateTime()(); + TextColumn get logger => text().nullable()(); + TextColumn get stack => text().nullable()(); +} + +extension LogMessageEntityDataDomainEx on LogMessageEntityData { + domain.LogMessage toDto() => domain.LogMessage( + message: message, + level: level, + createdAt: createdAt, + logger: logger, + error: details, + stack: stack, + ); } diff --git a/mobile/lib/infrastructure/entities/log.entity.drift.dart b/mobile/lib/infrastructure/entities/log.entity.drift.dart new file mode 100644 index 000000000..d04cd5b7a Binary files /dev/null and b/mobile/lib/infrastructure/entities/log.entity.drift.dart differ diff --git a/mobile/lib/infrastructure/entities/log.entity.g.dart b/mobile/lib/infrastructure/entities/log.entity.g.dart deleted file mode 100644 index 02fa817a0..000000000 Binary files a/mobile/lib/infrastructure/entities/log.entity.g.dart and /dev/null differ diff --git a/mobile/lib/infrastructure/repositories/log.repository.dart b/mobile/lib/infrastructure/repositories/log.repository.dart index eefe2b0ab..9b3985ba4 100644 --- a/mobile/lib/infrastructure/repositories/log.repository.dart +++ b/mobile/lib/infrastructure/repositories/log.repository.dart @@ -1,27 +1,43 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; -import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; -import 'package:isar/isar.dart'; +import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; -class IsarLogRepository extends IsarDatabaseRepository { - final Isar _db; - const IsarLogRepository(super.db) : _db = db; +class LogRepository { + final DriftLogger _db; + const LogRepository(this._db); Future deleteAll() async { - await transaction(() async => await _db.loggerMessages.clear()); + await _db.logMessageEntity.deleteAll(); return true; } - Future> getAll() async { - final logs = await _db.loggerMessages.where().sortByCreatedAtDesc().findAll(); - return logs.map((l) => l.toDto()).toList(); + Future> getAll({int limit = 250}) async { + final query = _db.logMessageEntity.select() + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(limit); + + return query.map((log) => log.toDto()).get(); + } + + LogMessageEntityCompanion _toEntityCompanion(LogMessage log) { + return LogMessageEntityCompanion.insert( + message: log.message, + level: log.level, + createdAt: log.createdAt, + logger: Value(log.logger), + details: Value(log.error), + stack: Value(log.stack), + ); } Future insert(LogMessage log) async { - final logEntity = LoggerMessage.fromDto(log); + final logEntity = _toEntityCompanion(log); try { - await transaction(() => _db.loggerMessages.put(logEntity)); + await _db.logMessageEntity.insertOne(logEntity); } catch (e) { return false; } @@ -30,19 +46,30 @@ class IsarLogRepository extends IsarDatabaseRepository { } Future insertAll(Iterable logs) async { - await transaction(() async { - final logEntities = logs.map((log) => LoggerMessage.fromDto(log)).toList(); - await _db.loggerMessages.putAll(logEntities); - }); + final logEntities = logs.map(_toEntityCompanion).toList(); + await _db.logMessageEntity.insertAll(logEntities); + return true; } - Future truncate({int limit = 250}) async { - await transaction(() async { - final count = await _db.loggerMessages.count(); - if (count <= limit) return; - final toRemove = count - limit; - await _db.loggerMessages.where().limit(toRemove).deleteAll(); - }); + Future deleteByLogger(String logger) async { + await _db.logMessageEntity.deleteWhere((row) => row.logger.equals(logger)); + } + + Stream> watchMessages(String logger) { + final query = _db.logMessageEntity.select() + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..where((row) => row.logger.equals(logger)); + + return query.watch().map((rows) => rows.map((row) => row.toDto()).toList()); + } + + Future truncate({int limit = kLogTruncateLimit}) async { + final totalCount = await _db.managers.logMessageEntity.count(); + if (totalCount > limit) { + final rowsToDelete = totalCount - limit; + + await _db.managers.logMessageEntity.orderBy((o) => o.createdAt.asc()).limit(rowsToDelete).delete(); + } } } diff --git a/mobile/lib/infrastructure/repositories/logger_db.repository.dart b/mobile/lib/infrastructure/repositories/logger_db.repository.dart new file mode 100644 index 000000000..583fc4281 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/logger_db.repository.dart @@ -0,0 +1,27 @@ +import 'package:drift/drift.dart'; +import 'package:drift_flutter/drift_flutter.dart'; +import 'package:immich_mobile/domain/interfaces/db.interface.dart'; +import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; + +import 'logger_db.repository.drift.dart'; + +@DriftDatabase(tables: [LogMessageEntity]) +class DriftLogger extends $DriftLogger implements IDatabaseRepository { + DriftLogger([QueryExecutor? executor]) + : super( + executor ?? driftDatabase(name: 'immich_logs', native: const DriftNativeOptions(shareAcrossIsolates: true)), + ); + + @override + int get schemaVersion => 1; + + @override + MigrationStrategy get migration => MigrationStrategy( + beforeOpen: (details) async { + await customStatement('PRAGMA foreign_keys = ON'); + await customStatement('PRAGMA synchronous = NORMAL'); + await customStatement('PRAGMA journal_mode = WAL'); + await customStatement('PRAGMA busy_timeout = 500'); + }, + ); +} diff --git a/mobile/lib/infrastructure/repositories/logger_db.repository.drift.dart b/mobile/lib/infrastructure/repositories/logger_db.repository.drift.dart new file mode 100644 index 000000000..8389d3a82 Binary files /dev/null and b/mobile/lib/infrastructure/repositories/logger_db.repository.drift.dart differ diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart index 48c38cf64..2175e77e8 100644 --- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart @@ -4,7 +4,6 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/sync_event.model.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; @@ -107,7 +106,6 @@ class SyncApiRepository { } stopwatch.stop(); _logger.info("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms"); - DLog.log("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms"); } List _parseLines(List lines) { diff --git a/mobile/lib/pages/common/app_log_detail.page.dart b/mobile/lib/pages/common/app_log_detail.page.dart index a9cf634fa..c9773f36e 100644 --- a/mobile/lib/pages/common/app_log_detail.page.dart +++ b/mobile/lib/pages/common/app_log_detail.page.dart @@ -65,7 +65,7 @@ class AppLogDetailPage extends HookConsumerWidget { ); } - buildLogContext1(String context1) { + buildLogContext(String logger) { return Padding( padding: const EdgeInsets.all(8.0), child: Column( @@ -86,7 +86,7 @@ class AppLogDetailPage extends HookConsumerWidget { child: Padding( padding: const EdgeInsets.all(8.0), child: SelectableText( - context1.toString(), + logger.toString(), style: const TextStyle(fontSize: 12.0, fontWeight: FontWeight.bold, fontFamily: "Inconsolata"), ), ), @@ -103,7 +103,7 @@ class AppLogDetailPage extends HookConsumerWidget { children: [ buildTextWithCopyButton("MESSAGE", logMessage.message), if (logMessage.error != null) buildTextWithCopyButton("DETAILS", logMessage.error.toString()), - if (logMessage.logger != null) buildLogContext1(logMessage.logger.toString()), + if (logMessage.logger != null) buildLogContext(logMessage.logger.toString()), if (logMessage.stack != null) buildTextWithCopyButton("STACK TRACE", logMessage.stack.toString()), ], ), diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index 159a5b4fe..87ea7849c 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -49,7 +49,6 @@ class SplashScreenPageState extends ConsumerState { final wsProvider = ref.read(websocketProvider.notifier); ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then( (a) { - log.info('Successfully updated auth info with access token: $accessToken'); try { wsProvider.connect(); infoProvider.getServerInfo(); diff --git a/mobile/lib/presentation/pages/dev/dev_logger.dart b/mobile/lib/presentation/pages/dev/dev_logger.dart deleted file mode 100644 index ab9849f87..000000000 --- a/mobile/lib/presentation/pages/dev/dev_logger.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:immich_mobile/domain/models/log.model.dart'; -import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; -import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; -// ignore: import_rule_isar -import 'package:isar/isar.dart'; - -const kDevLoggerTag = 'DEV'; - -abstract final class DLog { - const DLog(); - - static Stream> watchLog() { - final db = Isar.getInstance(); - if (db == null) { - return const Stream.empty(); - } - - return db.loggerMessages - .filter() - .context1EqualTo(kDevLoggerTag) - .sortByCreatedAtDesc() - .watch(fireImmediately: true) - .map((logs) => logs.map((log) => log.toDto()).toList()); - } - - static void clearLog() { - final db = Isar.getInstance(); - if (db == null) { - return; - } - - db.writeTxnSync(() { - db.loggerMessages.filter().context1EqualTo(kDevLoggerTag).deleteAllSync(); - }); - } - - static void log(String message, [Object? error, StackTrace? stackTrace]) { - if (!Platform.environment.containsKey('FLUTTER_TEST')) { - debugPrint('[$kDevLoggerTag] [${DateTime.now()}] $message'); - } - if (error != null) { - debugPrint('Error: $error'); - } - if (stackTrace != null) { - debugPrint('StackTrace: $stackTrace'); - } - - final isar = Isar.getInstance(); - if (isar == null) { - return; - } - - final record = LogMessage( - message: message, - level: LogLevel.info, - createdAt: DateTime.now(), - logger: kDevLoggerTag, - error: error?.toString(), - stack: stackTrace?.toString(), - ); - - unawaited(IsarLogRepository(isar).insert(record)); - } -} diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index 2ab1eeaaa..d3f0e3c1b 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -2,19 +2,16 @@ import 'dart:async'; import 'package:auto_route/auto_route.dart'; import 'package:drift/drift.dart' hide Column; -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/extensions/theme_extensions.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:logging/logging.dart'; final _features = [ _Feature( @@ -37,7 +34,7 @@ final _features = [ DriftAssetSelectionTimelineRoute(lockedSelectionAssets: assets.toSet()), ); - DLog.log("Selected ${selectedAssets?.length ?? 0} assets"); + Logger("FeaturesInDevelopment").fine("Selected ${selectedAssets?.length ?? 0} assets"); return Future.value(); }, @@ -159,7 +156,6 @@ class FeatInDevPage extends StatelessWidget { ), ), const Divider(height: 0), - const Flexible(child: _DevLogs()), ], ), ); @@ -174,57 +170,3 @@ class _Feature { final TextStyle? style; final Future Function(BuildContext, WidgetRef _) onTap; } - -class _DevLogs extends StatelessWidget { - const _DevLogs(); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - automaticallyImplyLeading: false, - actions: [ - IconButton( - onPressed: DLog.clearLog, - icon: Icon( - Icons.delete_outline_rounded, - size: 20.0, - color: context.primaryColor, - semanticLabel: "Clear logs", - ), - ), - ], - centerTitle: true, - ), - body: StreamBuilder( - initialData: [], - stream: DLog.watchLog(), - builder: (_, logMessages) { - return ListView.separated( - itemBuilder: (ctx, index) { - final logMessage = logMessages.data![index]; - return ListTile( - title: Text( - logMessage.message, - style: TextStyle(color: ctx.colorScheme.onSurface, fontSize: 14.0, overflow: TextOverflow.ellipsis), - ), - subtitle: Text( - "at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)}", - style: TextStyle(color: ctx.colorScheme.onSurfaceSecondary, fontSize: 12.0), - ), - dense: true, - visualDensity: VisualDensity.compact, - tileColor: Colors.transparent, - minLeadingWidth: 10, - ); - }, - separatorBuilder: (_, index) { - return const Divider(height: 0); - }, - itemCount: logMessages.data?.length ?? 0, - ); - }, - ), - ); - } -} diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index 8c4ca077c..9cab9caf9 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -12,10 +12,10 @@ import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:isar/isar.dart'; import 'package:path_provider/path_provider.dart'; @@ -36,7 +36,6 @@ abstract final class Bootstrap { UserSchema, BackupAlbumSchema, DuplicatedAssetSchema, - LoggerMessageSchema, ETagSchema, if (Platform.isAndroid) AndroidDeviceAssetSchema, if (Platform.isIOS) IOSDeviceAssetSchema, @@ -49,9 +48,13 @@ abstract final class Bootstrap { } static Future initDomain(Isar db, {bool shouldBufferLogs = true}) async { + // load drift dbs + final loggerDb = DriftLogger(); + await StoreService.init(storeRepository: IsarStoreRepository(db)); + await LogService.init( - logRepository: IsarLogRepository(db), + logRepository: LogRepository(loggerDb), storeRepository: IsarStoreRepository(db), shouldBuffer: shouldBufferLogs, ); diff --git a/mobile/lib/utils/isolate.dart b/mobile/lib/utils/isolate.dart index a57c3ebbd..01903cfc7 100644 --- a/mobile/lib/utils/isolate.dart +++ b/mobile/lib/utils/isolate.dart @@ -56,7 +56,7 @@ Cancelable runInIsolateGentle({ log.severe("Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", error, stack); } finally { try { - await LogService.I.flushBuffer(); + await LogService.I.flush(); await ref.read(driftProvider).close(); // Close Isar safely diff --git a/mobile/test/domain/services/log_service_test.dart b/mobile/test/domain/services/log_service_test.dart index 87b32b829..b4feac4e2 100644 --- a/mobile/test/domain/services/log_service_test.dart +++ b/mobile/test/domain/services/log_service_test.dart @@ -28,7 +28,7 @@ final _kWarnLog = LogMessage( void main() { late LogService sut; - late IsarLogRepository mockLogRepo; + late LogRepository mockLogRepo; late IsarStoreRepository mockStoreRepo; setUp(() async { diff --git a/mobile/test/infrastructure/repository.mock.dart b/mobile/test/infrastructure/repository.mock.dart index ed20f177b..29ef9462a 100644 --- a/mobile/test/infrastructure/repository.mock.dart +++ b/mobile/test/infrastructure/repository.mock.dart @@ -12,7 +12,7 @@ import 'package:mocktail/mocktail.dart'; class MockStoreRepository extends Mock implements IsarStoreRepository {} -class MockLogRepository extends Mock implements IsarLogRepository {} +class MockLogRepository extends Mock implements LogRepository {} class MockIsarUserRepository extends Mock implements IsarUserRepository {} diff --git a/mobile/test/modules/shared/sync_service_test.dart b/mobile/test/modules/shared/sync_service_test.dart index 22fd3cacf..767a52b8d 100644 --- a/mobile/test/modules/shared/sync_service_test.dart +++ b/mobile/test/modules/shared/sync_service_test.dart @@ -1,3 +1,5 @@ +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/constants/enums.dart'; @@ -9,9 +11,10 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; -import 'package:immich_mobile/repositories/partner_api.repository.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; +import 'package:immich_mobile/repositories/partner_api.repository.dart'; import 'package:immich_mobile/services/sync.service.dart'; import 'package:mocktail/mocktail.dart'; @@ -49,6 +52,28 @@ void main() { ); } + final owner = UserDto( + id: "1", + updatedAt: DateTime.now(), + email: "a@b.c", + name: "first last", + isAdmin: false, + profileChangedAt: DateTime.now(), + ); + + setUpAll(() async { + final loggerDb = DriftLogger(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); + final LogRepository logRepository = LogRepository(loggerDb); + + WidgetsFlutterBinding.ensureInitialized(); + final db = await TestUtils.initIsar(); + + db.writeTxnSync(() => db.clearSync()); + await StoreService.init(storeRepository: IsarStoreRepository(db)); + await Store.put(StoreKey.currentUser, owner); + await LogService.init(logRepository: logRepository, storeRepository: IsarStoreRepository(db)); + }); + group('Test SyncService grouped', () { final MockHashService hs = MockHashService(); final MockEntityService entityService = MockEntityService(); @@ -74,16 +99,9 @@ void main() { isAdmin: false, profileChangedAt: DateTime(2021), ); - late SyncService s; - setUpAll(() async { - WidgetsFlutterBinding.ensureInitialized(); - final db = await TestUtils.initIsar(); - db.writeTxnSync(() => db.clearSync()); - await StoreService.init(storeRepository: IsarStoreRepository(db)); - await Store.put(StoreKey.currentUser, owner); - await LogService.init(logRepository: IsarLogRepository(db), storeRepository: IsarStoreRepository(db)); - }); + late SyncService s; + final List initialAssets = [ makeAsset(checksum: "a", remoteId: "0-1"), makeAsset(checksum: "b", remoteId: "2-1"), diff --git a/mobile/test/test_utils.dart b/mobile/test/test_utils.dart index d932e2ffc..9b59773d3 100644 --- a/mobile/test/test_utils.dart +++ b/mobile/test/test_utils.dart @@ -14,7 +14,6 @@ import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:isar/isar.dart'; @@ -48,7 +47,6 @@ abstract final class TestUtils { UserSchema, BackupAlbumSchema, DuplicatedAssetSchema, - LoggerMessageSchema, ETagSchema, AndroidDeviceAssetSchema, IOSDeviceAssetSchema,