mirror of
https://github.com/immich-app/immich.git
synced 2025-03-18 02:31:28 -05:00
refactor(mobile): move user service to domain (#16782)
* refactor: user entity * chore: rebase fixes * refactor(mobile): move user service to domain * fix: timeline not visible on album selection page --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
parent
a65ce2ac55
commit
b778a86c99
23 changed files with 408 additions and 183 deletions
|
@ -3,8 +3,12 @@ import 'dart:typed_data';
|
|||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
|
||||
abstract interface class IUserApiRepository {
|
||||
Future<UserDto?> getMyUser();
|
||||
|
||||
Future<List<UserDto>> getAll();
|
||||
Future<({String profileImagePath})> createProfileImage({
|
||||
|
||||
/// Saves the [data] in the server and uses it as the current users profile image
|
||||
Future<String> createProfileImage({
|
||||
required String name,
|
||||
required Uint8List data,
|
||||
});
|
64
mobile/lib/domain/services/user.service.dart
Normal file
64
mobile/lib/domain/services/user.service.dart
Normal file
|
@ -0,0 +1,64 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user_api.repository.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/store.service.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class UserService {
|
||||
final Logger _log = Logger("UserService");
|
||||
final IUserRepository _userRepository;
|
||||
final IUserApiRepository _userApiRepository;
|
||||
final StoreService _storeService;
|
||||
|
||||
UserService({
|
||||
required IUserRepository userRepository,
|
||||
required IUserApiRepository userApiRepository,
|
||||
required StoreService storeService,
|
||||
}) : _userRepository = userRepository,
|
||||
_userApiRepository = userApiRepository,
|
||||
_storeService = storeService;
|
||||
|
||||
UserDto getMyUser() {
|
||||
return _storeService.get(StoreKey.currentUser);
|
||||
}
|
||||
|
||||
UserDto? tryGetMyUser() {
|
||||
return _storeService.tryGet(StoreKey.currentUser);
|
||||
}
|
||||
|
||||
Stream<UserDto?> watchMyUser() {
|
||||
return _storeService.watch(StoreKey.currentUser);
|
||||
}
|
||||
|
||||
Future<UserDto?> refreshMyUser() async {
|
||||
final user = await _userApiRepository.getMyUser();
|
||||
if (user == null) return null;
|
||||
await _storeService.put(StoreKey.currentUser, user);
|
||||
await _userRepository.update(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
Future<String?> createProfileImage(String name, Uint8List image) async {
|
||||
try {
|
||||
return await _userApiRepository.createProfileImage(
|
||||
name: name,
|
||||
data: image,
|
||||
);
|
||||
} catch (e) {
|
||||
_log.warning("Failed to upload profile image", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<UserDto>> getAll() async {
|
||||
return await _userRepository.getAll();
|
||||
}
|
||||
|
||||
Future<void> deleteAll() {
|
||||
return _userRepository.deleteAll();
|
||||
}
|
||||
}
|
11
mobile/lib/infrastructure/repositories/api.repository.dart
Normal file
11
mobile/lib/infrastructure/repositories/api.repository.dart
Normal file
|
@ -0,0 +1,11 @@
|
|||
import 'package:immich_mobile/constants/errors.dart';
|
||||
|
||||
class ApiRepository {
|
||||
const ApiRepository();
|
||||
|
||||
Future<T> checkNull<T>(Future<T?> future) async {
|
||||
final response = await future;
|
||||
if (response == null) throw NoResponseDtoError();
|
||||
return response;
|
||||
}
|
||||
}
|
|
@ -1,41 +1,41 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user_api.repository.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/api.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
|
||||
import 'package:immich_mobile/interfaces/user_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final userApiRepositoryProvider = Provider(
|
||||
(ref) => UserApiRepository(
|
||||
ref.watch(apiServiceProvider).usersApi,
|
||||
),
|
||||
);
|
||||
|
||||
class UserApiRepository extends ApiRepository implements IUserApiRepository {
|
||||
final UsersApi _api;
|
||||
const UserApiRepository(this._api);
|
||||
|
||||
UserApiRepository(this._api);
|
||||
@override
|
||||
Future<UserDto?> getMyUser() async {
|
||||
final (adminDto, preferenceDto) =
|
||||
await (_api.getMyUser(), _api.getMyPreferences()).wait;
|
||||
if (adminDto == null) return null;
|
||||
|
||||
return UserConverter.fromAdminDto(adminDto, preferenceDto);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> createProfileImage({
|
||||
required String name,
|
||||
required Uint8List data,
|
||||
}) async {
|
||||
final res = await checkNull(
|
||||
_api.createProfileImage(
|
||||
MultipartFile.fromBytes('file', data, filename: name),
|
||||
),
|
||||
);
|
||||
return res.profileImagePath;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<UserDto>> getAll() async {
|
||||
final dto = await checkNull(_api.searchUsers());
|
||||
return dto.map(UserConverter.fromSimpleUserDto).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<({String profileImagePath})> createProfileImage({
|
||||
required String name,
|
||||
required Uint8List data,
|
||||
}) async {
|
||||
final response = await checkNull(
|
||||
_api.createProfileImage(
|
||||
MultipartFile.fromBytes('file', data, filename: name),
|
||||
),
|
||||
);
|
||||
return (profileImagePath: response.profileImagePath);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/services/user.service.dart';
|
||||
|
||||
final otherUsersProvider =
|
||||
FutureProvider.autoDispose<List<UserDto>>((ref) async {
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||
import 'package:immich_mobile/providers/memory.provider.dart';
|
||||
import 'package:immich_mobile/services/album.service.dart';
|
||||
import 'package:immich_mobile/services/asset.service.dart';
|
||||
import 'package:immich_mobile/services/etag.service.dart';
|
||||
import 'package:immich_mobile/services/exif.service.dart';
|
||||
import 'package:immich_mobile/services/sync.service.dart';
|
||||
import 'package:immich_mobile/services/user.service.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
final assetProvider = StateNotifierProvider<AssetNotifier, bool>((ref) {
|
||||
|
@ -59,7 +60,7 @@ class AssetNotifier extends StateNotifier<bool> {
|
|||
await clearAllAssets();
|
||||
log.info("Manual refresh requested, cleared assets and albums from db");
|
||||
}
|
||||
final users = await _userService.getUsersFromServer();
|
||||
final users = await _syncService.getUsersFromServer();
|
||||
bool changedUsers = false;
|
||||
if (users != null) {
|
||||
changedUsers = await _syncService.syncUsersFromServer(users);
|
||||
|
@ -86,7 +87,7 @@ class AssetNotifier extends StateNotifier<bool> {
|
|||
_assetService.clearTable(),
|
||||
_exifService.clearTable(),
|
||||
_albumService.clearTable(),
|
||||
_userService.clearTable(),
|
||||
_userService.deleteAll(),
|
||||
_etagService.clearTable(),
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user_api.repository.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'user.provider.g.dart';
|
||||
|
@ -9,3 +14,14 @@ part 'user.provider.g.dart';
|
|||
@Riverpod(keepAlive: true)
|
||||
IUserRepository userRepository(Ref ref) =>
|
||||
IsarUserRepository(ref.watch(isarProvider));
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
IUserApiRepository userApiRepository(Ref ref) =>
|
||||
UserApiRepository(ref.watch(apiServiceProvider).usersApi);
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
UserService userService(Ref ref) => UserService(
|
||||
userRepository: ref.watch(userRepositoryProvider),
|
||||
userApiRepository: ref.watch(userApiRepositoryProvider),
|
||||
storeService: ref.watch(storeServiceProvider),
|
||||
);
|
||||
|
|
|
@ -23,5 +23,38 @@ final userRepositoryProvider = Provider<IUserRepository>.internal(
|
|||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef UserRepositoryRef = ProviderRef<IUserRepository>;
|
||||
String _$userApiRepositoryHash() => r'6b19f2c99fb83162a5ceb91adb8589eaae01bc92';
|
||||
|
||||
/// See also [userApiRepository].
|
||||
@ProviderFor(userApiRepository)
|
||||
final userApiRepositoryProvider = Provider<IUserApiRepository>.internal(
|
||||
userApiRepository,
|
||||
name: r'userApiRepositoryProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$userApiRepositoryHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef UserApiRepositoryRef = ProviderRef<IUserApiRepository>;
|
||||
String _$userServiceHash() => r'4a0873357b7115b4d6bfa8e89b847c0b74ce0d93';
|
||||
|
||||
/// See also [userService].
|
||||
@ProviderFor(userService)
|
||||
final userServiceProvider = Provider<UserService>.internal(
|
||||
userService,
|
||||
name: r'userServiceProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$userServiceHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef UserServiceRef = ProviderRef<UserService>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
|
|
@ -3,8 +3,8 @@ import 'dart:convert';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
import 'package:immich_mobile/services/user.service.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||
|
||||
enum UploadProfileStatus {
|
||||
idle,
|
||||
|
@ -72,7 +72,7 @@ class UploadProfileImageState {
|
|||
|
||||
class UploadProfileImageNotifier
|
||||
extends StateNotifier<UploadProfileImageState> {
|
||||
UploadProfileImageNotifier(this._userSErvice)
|
||||
UploadProfileImageNotifier(this._userService)
|
||||
: super(
|
||||
UploadProfileImageState(
|
||||
profileImagePath: '',
|
||||
|
@ -80,18 +80,21 @@ class UploadProfileImageNotifier
|
|||
),
|
||||
);
|
||||
|
||||
final UserService _userSErvice;
|
||||
final UserService _userService;
|
||||
|
||||
Future<bool> upload(XFile file) async {
|
||||
state = state.copyWith(status: UploadProfileStatus.loading);
|
||||
|
||||
var res = await _userSErvice.uploadProfileImage(file);
|
||||
var profileImagePath = await _userService.createProfileImage(
|
||||
file.name,
|
||||
await file.readAsBytes(),
|
||||
);
|
||||
|
||||
if (res != null) {
|
||||
if (profileImagePath != null) {
|
||||
debugPrint("Successfully upload profile image");
|
||||
state = state.copyWith(
|
||||
status: UploadProfileStatus.success,
|
||||
profileImagePath: res.profileImagePath,
|
||||
profileImagePath: profileImagePath,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -28,12 +28,10 @@ import 'package:immich_mobile/repositories/asset.repository.dart';
|
|||
import 'package:immich_mobile/repositories/backup.repository.dart';
|
||||
import 'package:immich_mobile/services/entity.service.dart';
|
||||
import 'package:immich_mobile/services/sync.service.dart';
|
||||
import 'package:immich_mobile/services/user.service.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
final albumServiceProvider = Provider(
|
||||
(ref) => AlbumService(
|
||||
ref.watch(userServiceProvider),
|
||||
ref.watch(syncServiceProvider),
|
||||
ref.watch(entityServiceProvider),
|
||||
ref.watch(albumRepositoryProvider),
|
||||
|
@ -45,7 +43,6 @@ final albumServiceProvider = Provider(
|
|||
);
|
||||
|
||||
class AlbumService {
|
||||
final UserService _userService;
|
||||
final SyncService _syncService;
|
||||
final EntityService _entityService;
|
||||
final IAlbumRepository _albumRepository;
|
||||
|
@ -58,7 +55,6 @@ class AlbumService {
|
|||
Completer<bool> _remoteCompleter = Completer()..complete(false);
|
||||
|
||||
AlbumService(
|
||||
this._userService,
|
||||
this._syncService,
|
||||
this._entityService,
|
||||
this._albumRepository,
|
||||
|
@ -171,7 +167,7 @@ class AlbumService {
|
|||
final Stopwatch sw = Stopwatch()..start();
|
||||
bool changes = false;
|
||||
try {
|
||||
final users = await _userService.getUsersFromServer();
|
||||
final users = await _syncService.getUsersFromServer();
|
||||
if (users != null) {
|
||||
await _syncService.syncUsersFromServer(users);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@ import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
|
|||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart'
|
||||
hide userServiceProvider;
|
||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
||||
|
@ -30,7 +31,6 @@ import 'package:immich_mobile/services/album.service.dart';
|
|||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:immich_mobile/services/backup.service.dart';
|
||||
import 'package:immich_mobile/services/sync.service.dart';
|
||||
import 'package:immich_mobile/services/user.service.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:maplibre_gl/maplibre_gl.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
@ -45,7 +45,6 @@ final assetServiceProvider = Provider(
|
|||
ref.watch(backupAlbumRepositoryProvider),
|
||||
ref.watch(apiServiceProvider),
|
||||
ref.watch(syncServiceProvider),
|
||||
ref.watch(userServiceProvider),
|
||||
ref.watch(backupServiceProvider),
|
||||
ref.watch(albumServiceProvider),
|
||||
ref.watch(storeServiceProvider),
|
||||
|
@ -62,7 +61,6 @@ class AssetService {
|
|||
final IBackupAlbumRepository _backupRepository;
|
||||
final ApiService _apiService;
|
||||
final SyncService _syncService;
|
||||
final UserService _userService;
|
||||
final BackupService _backupService;
|
||||
final AlbumService _albumService;
|
||||
final StoreService _storeService;
|
||||
|
@ -78,7 +76,6 @@ class AssetService {
|
|||
this._backupRepository,
|
||||
this._apiService,
|
||||
this._syncService,
|
||||
this._userService,
|
||||
this._backupService,
|
||||
this._albumService,
|
||||
this._storeService,
|
||||
|
@ -97,7 +94,6 @@ class AssetService {
|
|||
users: syncedUsers,
|
||||
getChangedAssets: _getRemoteAssetChanges,
|
||||
loadAssets: _getRemoteAssets,
|
||||
refreshUsers: _userService.getUsersFromServer,
|
||||
);
|
||||
debugPrint("refreshRemoteAssets full took ${sw.elapsedMilliseconds}ms");
|
||||
return changes;
|
||||
|
|
|
@ -13,12 +13,14 @@ import 'package:flutter/widgets.dart';
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user_api.repository.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart';
|
||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/partner.interface.dart';
|
||||
import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
|
||||
|
@ -39,7 +41,6 @@ import 'package:immich_mobile/repositories/network.repository.dart';
|
|||
import 'package:immich_mobile/repositories/partner.repository.dart';
|
||||
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/permission.repository.dart';
|
||||
import 'package:immich_mobile/repositories/user_api.repository.dart';
|
||||
import 'package:immich_mobile/services/album.service.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
|
@ -50,7 +51,6 @@ import 'package:immich_mobile/services/hash.service.dart';
|
|||
import 'package:immich_mobile/services/localization.service.dart';
|
||||
import 'package:immich_mobile/services/network.service.dart';
|
||||
import 'package:immich_mobile/services/sync.service.dart';
|
||||
import 'package:immich_mobile/services/user.service.dart';
|
||||
import 'package:immich_mobile/utils/backup_progress.dart';
|
||||
import 'package:immich_mobile/utils/bootstrap.dart';
|
||||
import 'package:immich_mobile/utils/diff.dart';
|
||||
|
@ -390,7 +390,7 @@ class BackgroundService {
|
|||
FileMediaRepository fileMediaRepository = FileMediaRepository();
|
||||
AssetMediaRepository assetMediaRepository = AssetMediaRepository();
|
||||
IUserRepository userRepository = IsarUserRepository(db);
|
||||
UserApiRepository userApiRepository =
|
||||
IUserApiRepository userApiRepository =
|
||||
UserApiRepository(apiService.usersApi);
|
||||
AlbumApiRepository albumApiRepository =
|
||||
AlbumApiRepository(apiService.albumsApi);
|
||||
|
@ -413,14 +413,10 @@ class BackgroundService {
|
|||
userRepository,
|
||||
StoreService.I,
|
||||
eTagRepository,
|
||||
);
|
||||
UserService userService = UserService(
|
||||
partnerApiRepository,
|
||||
userApiRepository,
|
||||
userRepository,
|
||||
);
|
||||
AlbumService albumService = AlbumService(
|
||||
userService,
|
||||
syncSerive,
|
||||
entityService,
|
||||
albumRepository,
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:collection/collection.dart';
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user_api.repository.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/store.service.dart';
|
||||
|
@ -17,6 +18,7 @@ import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
|||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/partner.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||
|
@ -26,6 +28,7 @@ import 'package:immich_mobile/repositories/album_media.repository.dart';
|
|||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||
import 'package:immich_mobile/repositories/etag.repository.dart';
|
||||
import 'package:immich_mobile/repositories/partner.repository.dart';
|
||||
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||
import 'package:immich_mobile/services/entity.service.dart';
|
||||
import 'package:immich_mobile/services/hash.service.dart';
|
||||
import 'package:immich_mobile/utils/async_mutex.dart';
|
||||
|
@ -46,6 +49,8 @@ final syncServiceProvider = Provider(
|
|||
ref.watch(userRepositoryProvider),
|
||||
ref.watch(storeServiceProvider),
|
||||
ref.watch(etagRepositoryProvider),
|
||||
ref.watch(partnerApiRepositoryProvider),
|
||||
ref.watch(userApiRepositoryProvider),
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -61,6 +66,8 @@ class SyncService {
|
|||
final IPartnerRepository _partnerRepository;
|
||||
final StoreService _storeService;
|
||||
final IETagRepository _eTagRepository;
|
||||
final IPartnerApiRepository _partnerApiRepository;
|
||||
final IUserApiRepository _userApiRepository;
|
||||
final AsyncMutex _lock = AsyncMutex();
|
||||
final Logger _log = Logger('SyncService');
|
||||
|
||||
|
@ -76,6 +83,8 @@ class SyncService {
|
|||
this._userRepository,
|
||||
this._storeService,
|
||||
this._eTagRepository,
|
||||
this._partnerApiRepository,
|
||||
this._userApiRepository,
|
||||
);
|
||||
|
||||
// public methods:
|
||||
|
@ -95,12 +104,11 @@ class SyncService {
|
|||
) getChangedAssets,
|
||||
required FutureOr<List<Asset>?> Function(UserDto user, DateTime until)
|
||||
loadAssets,
|
||||
required FutureOr<List<UserDto>?> Function() refreshUsers,
|
||||
}) =>
|
||||
_lock.run(
|
||||
() async =>
|
||||
await _syncRemoteAssetChanges(users, getChangedAssets) ??
|
||||
await _syncRemoteAssetsFull(refreshUsers, loadAssets),
|
||||
await _syncRemoteAssetsFull(getUsersFromServer, loadAssets),
|
||||
);
|
||||
|
||||
/// Syncs remote albums to the database
|
||||
|
@ -853,6 +861,61 @@ class SyncService {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<UserDto>?> getUsersFromServer() async {
|
||||
List<UserDto>? users;
|
||||
try {
|
||||
users = await _userApiRepository.getAll();
|
||||
} catch (e) {
|
||||
_log.warning("Failed to fetch users", e);
|
||||
users = null;
|
||||
}
|
||||
final List<UserDto> sharedBy =
|
||||
await _partnerApiRepository.getAll(Direction.sharedByMe);
|
||||
final List<UserDto> sharedWith =
|
||||
await _partnerApiRepository.getAll(Direction.sharedWithMe);
|
||||
|
||||
if (users == null) {
|
||||
_log.warning("Failed to refresh users");
|
||||
return null;
|
||||
}
|
||||
|
||||
users.sortBy((u) => u.uid);
|
||||
sharedBy.sortBy((u) => u.uid);
|
||||
sharedWith.sortBy((u) => u.uid);
|
||||
|
||||
final updatedSharedBy = <UserDto>[];
|
||||
|
||||
diffSortedListsSync(
|
||||
users,
|
||||
sharedBy,
|
||||
compare: (UserDto a, UserDto b) => a.uid.compareTo(b.uid),
|
||||
both: (UserDto a, UserDto b) {
|
||||
updatedSharedBy.add(a.copyWith(isPartnerSharedBy: true));
|
||||
return true;
|
||||
},
|
||||
onlyFirst: (UserDto a) => updatedSharedBy.add(a),
|
||||
onlySecond: (UserDto b) => updatedSharedBy.add(b),
|
||||
);
|
||||
|
||||
final updatedSharedWith = <UserDto>[];
|
||||
|
||||
diffSortedListsSync(
|
||||
updatedSharedBy,
|
||||
sharedWith,
|
||||
compare: (UserDto a, UserDto b) => a.uid.compareTo(b.uid),
|
||||
both: (UserDto a, UserDto b) {
|
||||
updatedSharedWith.add(
|
||||
a.copyWith(inTimeline: b.inTimeline, isPartnerSharedWith: true),
|
||||
);
|
||||
return true;
|
||||
},
|
||||
onlyFirst: (UserDto a) => updatedSharedWith.add(a),
|
||||
onlySecond: (UserDto b) => updatedSharedWith.add(b),
|
||||
);
|
||||
|
||||
return updatedSharedWith;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a triple(toAdd, toUpdate, toRemove)
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/user_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/user_api.repository.dart';
|
||||
import 'package:immich_mobile/utils/diff.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
final userServiceProvider = Provider(
|
||||
(ref) => UserService(
|
||||
ref.watch(partnerApiRepositoryProvider),
|
||||
ref.watch(userApiRepositoryProvider),
|
||||
ref.watch(userRepositoryProvider),
|
||||
),
|
||||
);
|
||||
|
||||
class UserService {
|
||||
final IPartnerApiRepository _partnerApiRepository;
|
||||
final IUserApiRepository _userApiRepository;
|
||||
final IUserRepository _userRepository;
|
||||
final Logger _log = Logger("UserService");
|
||||
|
||||
UserService(
|
||||
this._partnerApiRepository,
|
||||
this._userApiRepository,
|
||||
this._userRepository,
|
||||
);
|
||||
|
||||
Future<({String profileImagePath})?> uploadProfileImage(XFile image) async {
|
||||
try {
|
||||
return await _userApiRepository.createProfileImage(
|
||||
name: image.name,
|
||||
data: await image.readAsBytes(),
|
||||
);
|
||||
} catch (e) {
|
||||
_log.warning("Failed to upload profile image", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<UserDto>> getAll() async {
|
||||
return await _userRepository.getAll();
|
||||
}
|
||||
|
||||
Future<List<UserDto>?> getUsersFromServer() async {
|
||||
List<UserDto>? users;
|
||||
try {
|
||||
users = await _userApiRepository.getAll();
|
||||
} catch (e) {
|
||||
_log.warning("Failed to fetch users", e);
|
||||
users = null;
|
||||
}
|
||||
final List<UserDto> sharedBy =
|
||||
await _partnerApiRepository.getAll(Direction.sharedByMe);
|
||||
final List<UserDto> sharedWith =
|
||||
await _partnerApiRepository.getAll(Direction.sharedWithMe);
|
||||
|
||||
if (users == null) {
|
||||
_log.warning("Failed to refresh users");
|
||||
return null;
|
||||
}
|
||||
|
||||
users.sortBy((u) => u.uid);
|
||||
sharedBy.sortBy((u) => u.uid);
|
||||
sharedWith.sortBy((u) => u.uid);
|
||||
|
||||
final updatedSharedBy = <UserDto>[];
|
||||
|
||||
diffSortedListsSync(
|
||||
users,
|
||||
sharedBy,
|
||||
compare: (UserDto a, UserDto b) => a.uid.compareTo(b.uid),
|
||||
both: (UserDto a, UserDto b) {
|
||||
updatedSharedBy.add(a.copyWith(isPartnerSharedBy: true));
|
||||
return true;
|
||||
},
|
||||
onlyFirst: (UserDto a) => updatedSharedBy.add(a),
|
||||
onlySecond: (UserDto b) => updatedSharedBy.add(b),
|
||||
);
|
||||
|
||||
final updatedSharedWith = <UserDto>[];
|
||||
|
||||
diffSortedListsSync(
|
||||
updatedSharedBy,
|
||||
sharedWith,
|
||||
compare: (UserDto a, UserDto b) => a.uid.compareTo(b.uid),
|
||||
both: (UserDto a, UserDto b) {
|
||||
updatedSharedWith.add(
|
||||
a.copyWith(inTimeline: b.inTimeline, isPartnerSharedWith: true),
|
||||
);
|
||||
return true;
|
||||
},
|
||||
onlyFirst: (UserDto a) => updatedSharedWith.add(a),
|
||||
onlySecond: (UserDto b) => updatedSharedWith.add(b),
|
||||
);
|
||||
|
||||
return updatedSharedWith;
|
||||
}
|
||||
|
||||
Future<void> clearTable() {
|
||||
return _userRepository.deleteAll();
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||
import 'package:immich_mobile/repositories/activity_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/album_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/person_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/user_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/timeline.repository.dart';
|
||||
|
||||
void invalidateAllApiRepositoryProviders(WidgetRef ref) {
|
||||
ref.invalidate(userApiRepositoryProvider);
|
||||
|
@ -14,5 +14,5 @@ void invalidateAllApiRepositoryProviders(WidgetRef ref) {
|
|||
ref.invalidate(albumApiRepositoryProvider);
|
||||
ref.invalidate(personApiRepositoryProvider);
|
||||
ref.invalidate(assetApiRepositoryProvider);
|
||||
ref.invalidate(timelineUsersIdsProvider);
|
||||
ref.invalidate(timelineRepositoryProvider);
|
||||
}
|
||||
|
|
7
mobile/test/domain/service.mock.dart
Normal file
7
mobile/test/domain/service.mock.dart
Normal file
|
@ -0,0 +1,7 @@
|
|||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockStoreService extends Mock implements StoreService {}
|
||||
|
||||
class MockUserService extends Mock implements UserService {}
|
133
mobile/test/domain/services/user_service_test.dart
Normal file
133
mobile/test/domain/services/user_service_test.dart
Normal file
|
@ -0,0 +1,133 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user_api.repository.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
import '../../fixtures/user.stub.dart';
|
||||
import '../../infrastructure/repository.mock.dart';
|
||||
import '../service.mock.dart';
|
||||
|
||||
void main() {
|
||||
late UserService sut;
|
||||
late IUserRepository mockUserRepo;
|
||||
late IUserApiRepository mockUserApiRepo;
|
||||
late StoreService mockStoreService;
|
||||
|
||||
setUp(() {
|
||||
mockUserRepo = MockUserRepository();
|
||||
mockUserApiRepo = MockUserApiRepository();
|
||||
mockStoreService = MockStoreService();
|
||||
sut = UserService(
|
||||
userRepository: mockUserRepo,
|
||||
userApiRepository: mockUserApiRepo,
|
||||
storeService: mockStoreService,
|
||||
);
|
||||
});
|
||||
|
||||
group('getMyUser', () {
|
||||
test('should return user from store', () {
|
||||
when(() => mockStoreService.get(StoreKey.currentUser))
|
||||
.thenReturn(UserStub.admin);
|
||||
final result = sut.getMyUser();
|
||||
expect(result, UserStub.admin);
|
||||
});
|
||||
|
||||
test('should handle user not found scenario', () {
|
||||
when(() => mockStoreService.get(StoreKey.currentUser))
|
||||
.thenThrow(Exception('User not found'));
|
||||
|
||||
expect(() => sut.getMyUser(), throwsA(isA<Exception>()));
|
||||
});
|
||||
});
|
||||
|
||||
group('tryGetMyUser', () {
|
||||
test('should return user from store', () {
|
||||
when(() => mockStoreService.tryGet(StoreKey.currentUser))
|
||||
.thenReturn(UserStub.admin);
|
||||
final result = sut.tryGetMyUser();
|
||||
expect(result, UserStub.admin);
|
||||
});
|
||||
|
||||
test('should return null if user not found', () {
|
||||
when(() => mockStoreService.tryGet(StoreKey.currentUser))
|
||||
.thenReturn(null);
|
||||
final result = sut.tryGetMyUser();
|
||||
expect(result, isNull);
|
||||
});
|
||||
});
|
||||
|
||||
group('watchMyUser', () {
|
||||
test('should return user stream from store', () {
|
||||
when(() => mockStoreService.watch(StoreKey.currentUser))
|
||||
.thenAnswer((_) => Stream.value(UserStub.admin));
|
||||
final result = sut.watchMyUser();
|
||||
expect(result, emits(UserStub.admin));
|
||||
});
|
||||
|
||||
test('should return an empty stream if user not found', () {
|
||||
when(() => mockStoreService.watch(StoreKey.currentUser))
|
||||
.thenAnswer((_) => const Stream.empty());
|
||||
final result = sut.watchMyUser();
|
||||
expect(result, emitsInOrder([]));
|
||||
});
|
||||
});
|
||||
|
||||
group('refreshMyUser', () {
|
||||
test('should return user from api and store it', () async {
|
||||
when(() => mockUserApiRepo.getMyUser())
|
||||
.thenAnswer((_) async => UserStub.admin);
|
||||
when(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin))
|
||||
.thenAnswer((_) async => true);
|
||||
when(() => mockUserRepo.update(UserStub.admin))
|
||||
.thenAnswer((_) async => UserStub.admin);
|
||||
|
||||
final result = await sut.refreshMyUser();
|
||||
verify(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin))
|
||||
.called(1);
|
||||
verify(() => mockUserRepo.update(UserStub.admin)).called(1);
|
||||
expect(result, UserStub.admin);
|
||||
});
|
||||
|
||||
test('should return null if user not found', () async {
|
||||
when(() => mockUserApiRepo.getMyUser()).thenAnswer((_) async => null);
|
||||
|
||||
final result = await sut.refreshMyUser();
|
||||
verifyNever(
|
||||
() => mockStoreService.put(StoreKey.currentUser, UserStub.admin),
|
||||
);
|
||||
verifyNever(() => mockUserRepo.update(UserStub.admin));
|
||||
expect(result, isNull);
|
||||
});
|
||||
});
|
||||
|
||||
group('createProfileImage', () {
|
||||
test('should return profile image path', () async {
|
||||
when(
|
||||
() => mockUserApiRepo.createProfileImage(
|
||||
name: 'profile.jpg',
|
||||
data: Uint8List(0),
|
||||
),
|
||||
).thenAnswer((_) async => 'profile.jpg');
|
||||
|
||||
final result = await sut.createProfileImage('profile.jpg', Uint8List(0));
|
||||
expect(result, 'profile.jpg');
|
||||
});
|
||||
|
||||
test('should return null if profile image creation fails', () async {
|
||||
when(
|
||||
() => mockUserApiRepo.createProfileImage(
|
||||
name: 'profile.jpg',
|
||||
data: Uint8List(0),
|
||||
),
|
||||
).thenThrow(Exception('Failed to create profile image'));
|
||||
|
||||
final result = await sut.createProfileImage('profile.jpg', Uint8List(0));
|
||||
expect(result, isNull);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,7 +1,14 @@
|
|||
import 'package:immich_mobile/domain/interfaces/log.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/interfaces/user_api.repository.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockStoreRepository extends Mock implements IStoreRepository {}
|
||||
|
||||
class MockLogRepository extends Mock implements ILogRepository {}
|
||||
|
||||
class MockUserRepository extends Mock implements IUserRepository {}
|
||||
|
||||
// API Repos
|
||||
class MockUserApiRepository extends Mock implements IUserApiRepository {}
|
||||
|
|
|
@ -11,9 +11,11 @@ import 'package:immich_mobile/entities/store.entity.dart';
|
|||
import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
||||
import 'package:immich_mobile/services/sync.service.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
import '../../infrastructure/repository.mock.dart';
|
||||
import '../../repository.mocks.dart';
|
||||
import '../../service.mocks.dart';
|
||||
import '../../test_utils.dart';
|
||||
|
@ -56,6 +58,9 @@ void main() {
|
|||
final MockAlbumMediaRepository albumMediaRepository =
|
||||
MockAlbumMediaRepository();
|
||||
final MockAlbumApiRepository albumApiRepository = MockAlbumApiRepository();
|
||||
final MockPartnerApiRepository partnerApiRepository =
|
||||
MockPartnerApiRepository();
|
||||
final MockUserApiRepository userApiRepository = MockUserApiRepository();
|
||||
final MockPartnerRepository partnerRepository = MockPartnerRepository();
|
||||
|
||||
final owner = UserDto(
|
||||
|
@ -98,6 +103,8 @@ void main() {
|
|||
userRepository,
|
||||
StoreService.I,
|
||||
eTagRepository,
|
||||
partnerApiRepository,
|
||||
userApiRepository,
|
||||
);
|
||||
when(() => eTagRepository.get(owner.id))
|
||||
.thenAnswer((_) async => ETag(id: owner.uid, time: DateTime.now()));
|
||||
|
@ -125,6 +132,10 @@ void main() {
|
|||
when(() => assetRepository.transaction<Null>(any())).thenAnswer(
|
||||
(call) => (call.positionalArguments.first as Function).call(),
|
||||
);
|
||||
when(() => userApiRepository.getAll()).thenAnswer((_) async => [owner]);
|
||||
registerFallbackValue(Direction.sharedByMe);
|
||||
when(() => partnerApiRepository.getAll(any()))
|
||||
.thenAnswer((_) async => []);
|
||||
});
|
||||
test('test inserting existing assets', () async {
|
||||
final List<Asset> remoteAssets = [
|
||||
|
@ -136,7 +147,6 @@ void main() {
|
|||
users: [owner],
|
||||
getChangedAssets: _failDiff,
|
||||
loadAssets: (u, d) => remoteAssets,
|
||||
refreshUsers: () => [owner],
|
||||
);
|
||||
expect(c1, isFalse);
|
||||
verifyNever(() => assetRepository.updateAll(any()));
|
||||
|
@ -155,7 +165,6 @@ void main() {
|
|||
users: [owner],
|
||||
getChangedAssets: _failDiff,
|
||||
loadAssets: (u, d) => remoteAssets,
|
||||
refreshUsers: () => [owner],
|
||||
);
|
||||
expect(c1, isTrue);
|
||||
final updatedAsset = initialAssets[3].updatedCopy(remoteAssets[3]);
|
||||
|
@ -178,7 +187,6 @@ void main() {
|
|||
users: [owner],
|
||||
getChangedAssets: _failDiff,
|
||||
loadAssets: (u, d) => remoteAssets,
|
||||
refreshUsers: () => [owner],
|
||||
);
|
||||
expect(c1, isTrue);
|
||||
when(
|
||||
|
@ -191,7 +199,6 @@ void main() {
|
|||
users: [owner],
|
||||
getChangedAssets: _failDiff,
|
||||
loadAssets: (u, d) => remoteAssets,
|
||||
refreshUsers: () => [owner],
|
||||
);
|
||||
expect(c2, isFalse);
|
||||
final currentState = [...remoteAssets];
|
||||
|
@ -206,7 +213,6 @@ void main() {
|
|||
users: [owner],
|
||||
getChangedAssets: _failDiff,
|
||||
loadAssets: (u, d) => remoteAssets,
|
||||
refreshUsers: () => [owner],
|
||||
);
|
||||
expect(c3, isTrue);
|
||||
remoteAssets.add(makeAsset(checksum: "k", remoteId: "2-1e"));
|
||||
|
@ -215,7 +221,6 @@ void main() {
|
|||
users: [owner],
|
||||
getChangedAssets: _failDiff,
|
||||
loadAssets: (u, d) => remoteAssets,
|
||||
refreshUsers: () => [owner],
|
||||
);
|
||||
expect(c4, isTrue);
|
||||
});
|
||||
|
@ -246,7 +251,6 @@ void main() {
|
|||
users: [owner],
|
||||
getChangedAssets: (user, since) async => (toUpsert, toDelete),
|
||||
loadAssets: (user, date) => throw Exception(),
|
||||
refreshUsers: () => throw Exception(),
|
||||
);
|
||||
expect(c, isTrue);
|
||||
verify(() => assetRepository.updateAll(expected));
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/album.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/album_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
||||
|
@ -10,6 +9,7 @@ import 'package:immich_mobile/interfaces/auth_api.interface.dart';
|
|||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/partner.interface.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
|
@ -17,8 +17,6 @@ class MockAlbumRepository extends Mock implements IAlbumRepository {}
|
|||
|
||||
class MockAssetRepository extends Mock implements IAssetRepository {}
|
||||
|
||||
class MockUserRepository extends Mock implements IUserRepository {}
|
||||
|
||||
class MockBackupRepository extends Mock implements IBackupAlbumRepository {}
|
||||
|
||||
class MockExifInfoRepository extends Mock implements IExifInfoRepository {}
|
||||
|
@ -37,4 +35,6 @@ class MockAuthApiRepository extends Mock implements IAuthApiRepository {}
|
|||
|
||||
class MockAuthRepository extends Mock implements IAuthRepository {}
|
||||
|
||||
class MockPartnerApiRepository extends Mock implements IPartnerApiRepository {}
|
||||
|
||||
class MockPartnerRepository extends Mock implements IPartnerRepository {}
|
||||
|
|
|
@ -3,14 +3,11 @@ import 'package:immich_mobile/services/entity.service.dart';
|
|||
import 'package:immich_mobile/services/hash.service.dart';
|
||||
import 'package:immich_mobile/services/network.service.dart';
|
||||
import 'package:immich_mobile/services/sync.service.dart';
|
||||
import 'package:immich_mobile/services/user.service.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class MockApiService extends Mock implements ApiService {}
|
||||
|
||||
class MockUserService extends Mock implements UserService {}
|
||||
|
||||
class MockSyncService extends Mock implements SyncService {}
|
||||
|
||||
class MockHashService extends Mock implements HashService {}
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:immich_mobile/entities/backup_album.entity.dart';
|
|||
import 'package:immich_mobile/services/album.service.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
import '../domain/service.mock.dart';
|
||||
import '../fixtures/album.stub.dart';
|
||||
import '../fixtures/asset.stub.dart';
|
||||
import '../fixtures/user.stub.dart';
|
||||
|
@ -38,7 +39,6 @@ void main() {
|
|||
);
|
||||
|
||||
sut = AlbumService(
|
||||
userService,
|
||||
syncService,
|
||||
entityService,
|
||||
albumRepository,
|
||||
|
@ -84,7 +84,7 @@ void main() {
|
|||
|
||||
group('refreshRemoteAlbums', () {
|
||||
test('is working', () async {
|
||||
when(() => userService.getUsersFromServer()).thenAnswer((_) async => []);
|
||||
when(() => syncService.getUsersFromServer()).thenAnswer((_) async => []);
|
||||
when(() => syncService.syncUsersFromServer(any()))
|
||||
.thenAnswer((_) async => true);
|
||||
when(() => albumApiRepository.getAll(shared: true))
|
||||
|
@ -102,7 +102,7 @@ void main() {
|
|||
).thenAnswer((_) async => true);
|
||||
final result = await sut.refreshRemoteAlbums();
|
||||
expect(result, true);
|
||||
verify(() => userService.getUsersFromServer()).called(1);
|
||||
verify(() => syncService.getUsersFromServer()).called(1);
|
||||
verify(() => syncService.syncUsersFromServer([])).called(1);
|
||||
verify(() => albumApiRepository.getAll(shared: true)).called(1);
|
||||
verify(() => albumApiRepository.getAll(shared: null)).called(1);
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:mocktail/mocktail.dart';
|
|||
|
||||
import '../fixtures/asset.stub.dart';
|
||||
import '../fixtures/user.stub.dart';
|
||||
import '../infrastructure/repository.mock.dart';
|
||||
import '../repository.mocks.dart';
|
||||
|
||||
void main() {
|
||||
|
|
Loading…
Add table
Reference in a new issue