more refactors
This commit is contained in:
@@ -11,4 +11,7 @@ abstract interface class IAlbumRepository {
|
||||
|
||||
/// Removes album with the given [id]
|
||||
FutureOr<void> deleteId(int id);
|
||||
|
||||
/// Removes all albums
|
||||
FutureOr<void> deleteAll();
|
||||
}
|
||||
|
||||
@@ -14,4 +14,7 @@ abstract interface class IAlbumToAssetRepository {
|
||||
|
||||
/// Removes album with the given [albumId]
|
||||
FutureOr<void> deleteAlbumId(int albumId);
|
||||
|
||||
/// Removes all album to asset mappings
|
||||
FutureOr<void> deleteAll();
|
||||
}
|
||||
|
||||
@@ -8,4 +8,7 @@ abstract interface class IAlbumETagRepository {
|
||||
|
||||
/// Fetches the album etag for the given [albumId]
|
||||
FutureOr<AlbumETag?> get(int albumId);
|
||||
|
||||
/// Removes all album eTags
|
||||
FutureOr<void> deleteAll();
|
||||
}
|
||||
|
||||
@@ -8,4 +8,7 @@ abstract interface class IUserRepository {
|
||||
|
||||
/// Fetches user
|
||||
FutureOr<User?> getForId(String userId);
|
||||
|
||||
/// Removes all users
|
||||
FutureOr<void> deleteAll();
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class AlbumETag {
|
||||
required this.modifiedTime,
|
||||
});
|
||||
|
||||
factory AlbumETag.empty() {
|
||||
factory AlbumETag.initial() {
|
||||
return AlbumETag(
|
||||
albumId: -1,
|
||||
assetCount: 0,
|
||||
|
||||
@@ -18,6 +18,35 @@ sealed class RenderListElement {
|
||||
int get hashCode => date.hashCode;
|
||||
}
|
||||
|
||||
/// Used to pad the render list elements
|
||||
class RenderListPaddingElement extends RenderListElement {
|
||||
final double topPadding;
|
||||
|
||||
const RenderListPaddingElement({
|
||||
required this.topPadding,
|
||||
required super.date,
|
||||
});
|
||||
|
||||
factory RenderListPaddingElement.beforeElement({
|
||||
required double top,
|
||||
RenderListElement? before,
|
||||
}) =>
|
||||
RenderListPaddingElement(
|
||||
topPadding: top,
|
||||
date: before?.date ?? DateTime.now(),
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(covariant RenderListPaddingElement other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return super == other && other.topPadding == topPadding;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => super.hashCode ^ topPadding.hashCode;
|
||||
}
|
||||
|
||||
class RenderListMonthHeaderElement extends RenderListElement {
|
||||
late final String header;
|
||||
|
||||
|
||||
@@ -50,7 +50,12 @@ class AlbumRepository with LogMixin implements IAlbumRepository {
|
||||
|
||||
@override
|
||||
FutureOr<void> deleteId(int id) async {
|
||||
await _db.asset.deleteWhere((row) => row.id.equals(id));
|
||||
await _db.album.deleteWhere((row) => row.id.equals(id));
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> deleteAll() async {
|
||||
await _db.album.deleteAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,4 +75,9 @@ class AlbumToAssetRepository with LogMixin implements IAlbumToAssetRepository {
|
||||
FutureOr<void> deleteAlbumId(int albumId) async {
|
||||
await _db.albumToAsset.deleteWhere((row) => row.albumId.equals(albumId));
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> deleteAll() async {
|
||||
await _db.albumToAsset.deleteAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@ class AlbumETagRepository with LogMixin implements IAlbumETagRepository {
|
||||
..where((r) => r.albumId.equals(albumId));
|
||||
return await query.map(_toModel).getSingleOrNull();
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> deleteAll() async {
|
||||
await _db.albumETag.deleteAll();
|
||||
}
|
||||
}
|
||||
|
||||
AlbumETagCompanion _toEntity(AlbumETag albumETag) {
|
||||
|
||||
@@ -44,6 +44,11 @@ class UserRepository with LogMixin implements IUserRepository {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> deleteAll() async {
|
||||
await _db.user.deleteAll();
|
||||
}
|
||||
}
|
||||
|
||||
User _toModel(UserData user) {
|
||||
|
||||
@@ -22,6 +22,7 @@ class AlbumSyncService with LogMixin {
|
||||
|
||||
Future<bool> performFullDeviceSync() async {
|
||||
try {
|
||||
final Stopwatch stopwatch = Stopwatch()..start();
|
||||
final deviceAlbums = await di<IDeviceAlbumRepository>().getAll();
|
||||
final dbAlbums = await di<IAlbumRepository>().getAll(localOnly: true);
|
||||
final hasChange = await CollectionUtil.diffLists(
|
||||
@@ -34,6 +35,7 @@ class AlbumSyncService with LogMixin {
|
||||
onlySecond: _addDeviceAlbum,
|
||||
);
|
||||
|
||||
log.i("Full device sync took - ${stopwatch.elapsedMilliseconds}ms");
|
||||
return hasChange;
|
||||
} catch (e, s) {
|
||||
log.e("Error performing full device sync", e, s);
|
||||
@@ -47,8 +49,8 @@ class AlbumSyncService with LogMixin {
|
||||
DateTime? modifiedUntil,
|
||||
}) async {
|
||||
assert(dbAlbum.id != null, "Album ID from DB is null");
|
||||
final albumEtag =
|
||||
await di<IAlbumETagRepository>().get(dbAlbum.id!) ?? AlbumETag.empty();
|
||||
final albumEtag = await di<IAlbumETagRepository>().get(dbAlbum.id!) ??
|
||||
AlbumETag.initial();
|
||||
final assetCountInDevice =
|
||||
await di<IDeviceAlbumRepository>().getAssetCount(deviceAlbum.localId!);
|
||||
|
||||
@@ -66,6 +68,7 @@ class AlbumSyncService with LogMixin {
|
||||
|
||||
Future<void> _addDeviceAlbum(Album album, {DateTime? modifiedUntil}) async {
|
||||
try {
|
||||
log.i("Syncing device album ${album.name}");
|
||||
final albumId = (await di<IAlbumRepository>().upsert(album))?.id;
|
||||
// break fast if we cannot add an album
|
||||
if (albumId == null) {
|
||||
@@ -115,6 +118,7 @@ class AlbumSyncService with LogMixin {
|
||||
|
||||
Future<void> _removeDeviceAlbum(Album album) async {
|
||||
assert(album.id != null, "Album ID from DB is null");
|
||||
log.i("Removing device album ${album.name}");
|
||||
final albumId = album.id!;
|
||||
try {
|
||||
await di<IDatabaseRepository>().txn(() async {
|
||||
|
||||
@@ -35,6 +35,7 @@ class AssetSyncService with LogMixin {
|
||||
int? limit,
|
||||
}) async {
|
||||
try {
|
||||
final Stopwatch stopwatch = Stopwatch()..start();
|
||||
final db = di<IDatabaseRepository>();
|
||||
final assetRepo = di<IAssetRepository>();
|
||||
final syncApiRepo = di<ISyncApiRepository>();
|
||||
@@ -74,6 +75,7 @@ class AssetSyncService with LogMixin {
|
||||
if (assetsFromServer.length != chunkSize) break;
|
||||
}
|
||||
|
||||
log.i("Full remote sync took - ${stopwatch.elapsedMilliseconds}ms");
|
||||
return true;
|
||||
} catch (e, s) {
|
||||
log.e("Error performing full remote sync for user - ${user.name}", e, s);
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_web_auth_2/flutter_web_auth_2.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/album.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/album_asset.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/album_etag.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/api/authentication_api.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/api/server_api.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/api/user_api.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/domain/services/album_sync.service.dart';
|
||||
import 'package:immich_mobile/domain/services/asset_sync.service.dart';
|
||||
import 'package:immich_mobile/presentation/states/gallery_permission.state.dart';
|
||||
import 'package:immich_mobile/presentation/states/server_feature_config.state.dart';
|
||||
import 'package:immich_mobile/service_locator.dart';
|
||||
import 'package:immich_mobile/utils/immich_api_client.dart';
|
||||
import 'package:immich_mobile/utils/mixins/log.mixin.dart';
|
||||
@@ -99,6 +110,38 @@ class LoginService with LogMixin {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> handlePostUrlResolution(String serverEndpoint) async {
|
||||
await ServiceLocator.registerApiClient(serverEndpoint);
|
||||
ServiceLocator.registerPostGlobalStates();
|
||||
|
||||
// Fetch server features
|
||||
await di<ServerFeatureConfigProvider>().getFeatures();
|
||||
}
|
||||
|
||||
Future<User?> handlePostLogin() async {
|
||||
final user = await di<IUserApiRepository>().getMyUser().timeout(
|
||||
const Duration(seconds: 10),
|
||||
// ignore: function-always-returns-null
|
||||
onTimeout: () {
|
||||
log.w("Timedout while fetching user details using saved credentials");
|
||||
return null;
|
||||
},
|
||||
);
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ServiceLocator.registerCurrentUser(user);
|
||||
|
||||
// sync assets in background
|
||||
unawaited(di<AssetSyncService>().performFullRemoteSyncIsolate(user));
|
||||
if (di<GalleryPermissionProvider>().hasPermission) {
|
||||
unawaited(di<AlbumSyncService>().performFullDeviceSyncIsolate());
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
Future<bool> tryAutoLogin() async {
|
||||
final serverEndpoint =
|
||||
await di<IStoreRepository>().tryGet(StoreKey.serverEndpoint);
|
||||
@@ -106,8 +149,7 @@ class LoginService with LogMixin {
|
||||
return false;
|
||||
}
|
||||
|
||||
await ServiceLocator.registerApiClient(serverEndpoint);
|
||||
ServiceLocator.registerPostGlobalStates();
|
||||
await handlePostUrlResolution(serverEndpoint);
|
||||
|
||||
final accessToken =
|
||||
await di<IStoreRepository>().tryGet(StoreKey.accessToken);
|
||||
@@ -118,19 +160,20 @@ class LoginService with LogMixin {
|
||||
// Set token to interceptor
|
||||
await di<ImApiClient>().init(accessToken: accessToken);
|
||||
|
||||
final user = await di<IUserApiRepository>().getMyUser().timeout(
|
||||
const Duration(seconds: 10),
|
||||
// ignore: function-always-returns-null
|
||||
onTimeout: () {
|
||||
log.w("Timedout while fetching user details using saved credentials");
|
||||
return null;
|
||||
},
|
||||
);
|
||||
final user = await handlePostLogin();
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ServiceLocator.registerCurrentUser(user);
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> logout() async {
|
||||
// Remove existing assets
|
||||
await di<IAssetRepository>().deleteAll();
|
||||
await di<IAlbumRepository>().deleteAll();
|
||||
await di<IAlbumToAssetRepository>().deleteAll();
|
||||
await di<IAlbumETagRepository>().deleteAll();
|
||||
await di<IUserRepository>().deleteAll();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user