Compare commits

..

1 Commits

Author SHA1 Message Date
bo0tzz
66bde40ef8 chore: use non-default database name for medium tests 2025-06-20 21:17:51 +02:00
145 changed files with 2763 additions and 2784 deletions

View File

@@ -461,7 +461,7 @@ jobs:
if: ${{ !cancelled() }}
- name: Install Playwright Browsers
run: npx playwright install chromium --only-shell
run: npx playwright install --with-deps chromium
if: ${{ !cancelled() }}
- name: Docker build

6
cli/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@immich/cli",
"version": "2.2.72",
"version": "2.2.71",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@immich/cli",
"version": "2.2.72",
"version": "2.2.71",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"chokidar": "^4.0.3",
@@ -54,7 +54,7 @@
},
"../open-api/typescript-sdk": {
"name": "@immich/sdk",
"version": "1.135.3",
"version": "1.135.2",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@immich/cli",
"version": "2.2.72",
"version": "2.2.71",
"description": "Command Line Interface (CLI) for Immich",
"type": "module",
"exports": "./dist/index.js",

View File

@@ -39,8 +39,8 @@ alt="Dot Env Example"
/>
- Change the default `DB_PASSWORD`, and add custom database connection information if necessary.
- Change `DB_DATA_LOCATION` to a folder (absolute path) where the database will be saved to disk.
- Change `UPLOAD_LOCATION` to a folder (absolute path) where media (uploaded and generated) will be stored.
- Change `DB_DATA_LOCATION` to a folder where the database will be saved to disk.
- Change `UPLOAD_LOCATION` to a folder where media (uploaded and generated) will be stored.
11. Click on "**Deploy the stack**".

View File

@@ -1,8 +1,4 @@
[
{
"label": "v1.135.3",
"url": "https://v1.135.3.archive.immich.app"
},
{
"label": "v1.135.2",
"url": "https://v1.135.2.archive.immich.app"

8
e2e/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "immich-e2e",
"version": "1.135.3",
"version": "1.135.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "immich-e2e",
"version": "1.135.3",
"version": "1.135.2",
"license": "GNU Affero General Public License version 3",
"devDependencies": {
"@eslint/eslintrc": "^3.1.0",
@@ -44,7 +44,7 @@
},
"../cli": {
"name": "@immich/cli",
"version": "2.2.72",
"version": "2.2.71",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {
@@ -93,7 +93,7 @@
},
"../open-api/typescript-sdk": {
"name": "@immich/sdk",
"version": "1.135.3",
"version": "1.135.2",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "immich-e2e",
"version": "1.135.3",
"version": "1.135.2",
"description": "",
"main": "index.js",
"type": "module",

View File

@@ -464,6 +464,7 @@
"assets": "Assets",
"assets_added_count": "Added {count, plural, one {# asset} other {# assets}}",
"assets_added_to_album_count": "Added {count, plural, one {# asset} other {# assets}} to the album",
"assets_added_to_name_count": "Added {count, plural, one {# asset} other {# assets}} to {hasName, select, true {<b>{name}</b>} other {new album}}",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} cannot be added to the album",
"assets_count": "{count, plural, one {# asset} other {# assets}}",
"assets_deleted_permanently": "{count} asset(s) deleted permanently",

View File

@@ -35,8 +35,8 @@ platform :android do
task: 'bundle',
build_type: 'Release',
properties: {
"android.injected.version.code" => 204,
"android.injected.version.name" => "1.135.3",
"android.injected.version.code" => 203,
"android.injected.version.name" => "1.135.2",
}
)
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')

View File

@@ -17,14 +17,6 @@ enum AssetType: String, Codable {
case other = "OTHER"
}
struct ServerWellKnown: Codable {
struct APIInfo: Codable{
let endpoint: String
}
let api: APIInfo
}
struct SearchResult: Codable {
let id: String
let type: AssetType
@@ -65,7 +57,7 @@ class ImmichAPI {
init() async throws {
// fetch the credentials from the UserDefaults store that dart placed here
guard let defaults = UserDefaults(suiteName: "group.app.immich.share"),
var serverURL = defaults.string(forKey: "widget_server_url"),
let serverURL = defaults.string(forKey: "widget_server_url"),
let sessionKey = defaults.string(forKey: "widget_auth_token")
else {
throw WidgetError.noLogin
@@ -74,55 +66,13 @@ class ImmichAPI {
if serverURL == "" || sessionKey == "" {
throw WidgetError.noLogin
}
// migrate the server list value to a JSON array if it is not already
if !serverURL.starts(with: "[") {
let newServerList = "[\"\(serverURL)\"]"
defaults.set(newServerList, forKey: "widget_server_url")
serverURL = newServerList
}
guard let urls = try? JSONDecoder().decode([String].self, from: serverURL.data(using: .utf8)!) else {
throw WidgetError.noLogin
}
for url in urls {
guard let endpointURL = URL(string: url) else { continue }
if let apiURL = await Self.validateServer(at: endpointURL) {
serverConfig = ServerConfig(
serverEndpoint: apiURL.absoluteString,
sessionKey: sessionKey
)
return
}
}
throw WidgetError.fetchFailed
serverConfig = ServerConfig(
serverEndpoint: serverURL,
sessionKey: sessionKey
)
}
private static func validateServer(at endpointURL: URL) async -> URL? {
// build a URL that is only scheme, host, and port
var components = URLComponents()
components.scheme = endpointURL.scheme
components.host = endpointURL.host
components.port = endpointURL.port
guard let baseURL = components.url else { return nil }
var pingURL = baseURL
pingURL.appendPathComponent(".well-known")
pingURL.appendPathComponent("immich")
guard let (serverPingJSON, _) = try? await URLSession.shared.data(from: pingURL) else { return nil }
guard let apiInfo = try? JSONDecoder().decode(ServerWellKnown.self, from: serverPingJSON) else { return nil }
var apiURL = baseURL
apiURL.appendPathComponent(apiInfo.api.endpoint)
return apiURL
}
private func buildRequestURL(
serverConfig: ServerConfig,
endpoint: String,

View File

@@ -20,11 +20,8 @@ struct ImmichMemoryProvider: TimelineProvider {
completion: @escaping @Sendable (ImageEntry) -> Void
) {
Task {
var api: ImmichAPI
do {
api = try await ImmichAPI()
} catch let error as WidgetError {
completion(ImageEntry(date: Date(), image: nil, error: error))
guard let api = try? await ImmichAPI() else {
completion(ImageEntry(date: Date(), image: nil, error: .noLogin))
return
}
@@ -82,13 +79,9 @@ struct ImmichMemoryProvider: TimelineProvider {
Task {
var entries: [ImageEntry] = []
let now = Date()
var api: ImmichAPI
do {
api = try await ImmichAPI()
} catch let error as WidgetError {
entries.append(ImageEntry(date: now, image: nil, error: error))
guard let api = try? await ImmichAPI() else {
entries.append(ImageEntry(date: now, image: nil, error: .noLogin))
completion(Timeline(entries: entries, policy: .atEnd))
return
}

View File

@@ -63,15 +63,10 @@ struct ImmichRandomProvider: AppIntentTimelineProvider {
) async
-> ImageEntry
{
var api: ImmichAPI
do {
api = try await ImmichAPI()
} catch let error as WidgetError {
return ImageEntry(date: Date(), image: nil, error: error)
} catch {
return ImageEntry(date: Date(), image: nil, error: .fetchFailed)
guard let api = try? await ImmichAPI() else {
return ImageEntry(date: Date(), image: nil, error: .noLogin)
}
guard
let randomImage = try? await api.fetchSearchResults(
with: SearchFilters(size: 1)
@@ -105,21 +100,15 @@ struct ImmichRandomProvider: AppIntentTimelineProvider {
let now = Date()
// If we don't have a server config, return an entry with an error
var api: ImmichAPI
do {
api = try await ImmichAPI()
} catch let error as WidgetError {
entries.append(ImageEntry(date: now, image: nil, error: error))
return Timeline(entries: entries, policy: .atEnd)
} catch {
entries.append(ImageEntry(date: now, image: nil, error: .fetchFailed))
guard let api = try? await ImmichAPI() else {
entries.append(ImageEntry(date: now, image: nil, error: .noLogin))
return Timeline(entries: entries, policy: .atEnd)
}
// nil if album is NONE or nil
let albumId =
configuration.album?.id != "NONE" ? configuration.album?.id : nil
let albumName: String? = albumId != nil ? configuration.album?.albumName : nil
var albumName: String? = albumId != nil ? configuration.album?.albumName : nil
if albumId != nil {
// make sure the album exists on server, otherwise show error

View File

@@ -22,7 +22,7 @@ platform :ios do
path: "./Runner.xcodeproj",
)
increment_version_number(
version_number: "1.135.3"
version_number: "1.135.2"
)
increment_build_number(
build_number: latest_testflight_build_number + 1,

View File

@@ -11,7 +11,6 @@ const Map<String, Locale> locales = {
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'),
'Chinese Traditional (zh_TW)':
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
'Croatian (hr)': Locale('hr'),
'Czech (cs)': Locale('cs'),
'Danish (da)': Locale('da'),
'Dutch (nl)': Locale('nl'),

View File

@@ -0,0 +1,10 @@
import 'dart:typed_data';
import 'dart:ui';
abstract interface class IAssetMediaRepository {
Future<Uint8List?> getThumbnail(
String id, {
int quality = 80,
Size size = const Size.square(256),
});
}

View File

@@ -0,0 +1,12 @@
import 'dart:async';
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:immich_mobile/domain/models/device_asset.model.dart';
abstract interface class IDeviceAssetRepository implements IDatabaseRepository {
Future<bool> updateAll(List<DeviceAsset> assetHash);
Future<List<DeviceAsset>> getByIds(List<String> localIds);
Future<void> deleteIds(List<String> ids);
}

View File

@@ -0,0 +1,14 @@
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:immich_mobile/domain/models/exif.model.dart';
abstract interface class IExifInfoRepository implements IDatabaseRepository {
Future<ExifInfo?> get(int assetId);
Future<ExifInfo> update(ExifInfo exifInfo);
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos);
Future<void> delete(int assetId);
Future<void> deleteAll();
}

View File

@@ -0,0 +1,33 @@
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/local_album.model.dart';
abstract interface class ILocalAlbumRepository implements IDatabaseRepository {
Future<List<LocalAlbum>> getAll({Set<SortLocalAlbumsBy> sortBy = const {}});
Future<List<LocalAsset>> getAssets(String albumId);
Future<List<String>> getAssetIds(String albumId);
Future<void> upsert(
LocalAlbum album, {
Iterable<LocalAsset> toUpsert = const [],
Iterable<String> toDelete = const [],
});
Future<void> updateAll(Iterable<LocalAlbum> albums);
Future<void> delete(String albumId);
Future<void> processDelta({
required List<LocalAsset> updates,
required List<String> deletes,
required Map<String, List<String>> assetAlbums,
});
Future<void> syncDeletes(String albumId, Iterable<String> assetIdsToKeep);
Future<List<LocalAsset>> getAssetsToHash(String albumId);
}
enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum }

View File

@@ -0,0 +1,6 @@
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
abstract interface class ILocalAssetRepository implements IDatabaseRepository {
Future<void> updateHashes(Iterable<LocalAsset> hashes);
}

View File

@@ -0,0 +1,17 @@
import 'dart:async';
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:immich_mobile/domain/models/log.model.dart';
abstract interface class ILogRepository implements IDatabaseRepository {
Future<bool> insert(LogMessage log);
Future<bool> insertAll(Iterable<LogMessage> logs);
Future<List<LogMessage>> getAll();
Future<bool> deleteAll();
/// Truncates the logs to the most recent [limit]. Defaults to recent 250 logs
Future<void> truncate({int limit = 250});
}

View File

@@ -0,0 +1,7 @@
import 'dart:io';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
abstract interface class IStorageRepository {
Future<File?> getFileForAsset(LocalAsset asset);
}

View File

@@ -0,0 +1,20 @@
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
abstract interface class IStoreRepository implements IDatabaseRepository {
Future<bool> insert<T>(StoreKey<T> key, T value);
Future<T?> tryGet<T>(StoreKey<T> key);
Future<List<StoreDto<Object>>> getAll();
Stream<T?> watch<T>(StoreKey<T> key);
Stream<StoreDto<Object>> watchAll();
Future<bool> update<T>(StoreKey<T> key, T value);
Future<void> delete<T>(StoreKey<T> key);
Future<void> deleteAll();
}

View File

@@ -0,0 +1,12 @@
import 'package:http/http.dart' as http;
import 'package:immich_mobile/domain/models/sync_event.model.dart';
abstract interface class ISyncApiRepository {
Future<void> ack(List<String> data);
Future<void> streamChanges(
Function(List<SyncEvent>, Function() abort) onData, {
int batchSize,
http.Client? httpClient,
});
}

View File

@@ -0,0 +1,27 @@
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
abstract interface class ITimelineRepository implements IDatabaseRepository {
Stream<List<Bucket>> watchMainBucket(
List<String> timelineUsers, {
GroupAssetsBy groupBy = GroupAssetsBy.day,
});
Future<List<BaseAsset>> getMainBucketAssets(
List<String> timelineUsers, {
required int offset,
required int count,
});
Stream<List<Bucket>> watchLocalBucket(
String albumId, {
GroupAssetsBy groupBy = GroupAssetsBy.day,
});
Future<List<BaseAsset>> getLocalBucketAssets(
String albumId, {
required int offset,
required int count,
});
}

View File

@@ -1,10 +1,10 @@
import 'dart:convert';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart';
import 'package:immich_mobile/domain/interfaces/storage.interface.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/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';
@@ -12,16 +12,16 @@ import 'package:logging/logging.dart';
class HashService {
final int batchSizeLimit;
final int batchFileLimit;
final DriftLocalAlbumRepository _localAlbumRepository;
final DriftLocalAssetRepository _localAssetRepository;
final StorageRepository _storageRepository;
final ILocalAlbumRepository _localAlbumRepository;
final ILocalAssetRepository _localAssetRepository;
final IStorageRepository _storageRepository;
final NativeSyncApi _nativeSyncApi;
final _log = Logger('HashService');
HashService({
required DriftLocalAlbumRepository localAlbumRepository,
required DriftLocalAssetRepository localAssetRepository,
required StorageRepository storageRepository,
required ILocalAlbumRepository localAlbumRepository,
required ILocalAssetRepository localAssetRepository,
required IStorageRepository storageRepository,
required NativeSyncApi nativeSyncApi,
this.batchSizeLimit = kBatchHashSizeLimit,
this.batchFileLimit = kBatchHashFileLimit,

View File

@@ -2,11 +2,11 @@ import 'dart:async';
import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart';
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/local_album.model.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/services/store.service.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';
@@ -14,14 +14,14 @@ import 'package:logging/logging.dart';
import 'package:platform/platform.dart';
class LocalSyncService {
final DriftLocalAlbumRepository _localAlbumRepository;
final ILocalAlbumRepository _localAlbumRepository;
final NativeSyncApi _nativeSyncApi;
final Platform _platform;
final StoreService _storeService;
final Logger _log = Logger("DeviceSyncService");
LocalSyncService({
required DriftLocalAlbumRepository localAlbumRepository,
required ILocalAlbumRepository localAlbumRepository,
required NativeSyncApi nativeSyncApi,
required StoreService storeService,
Platform? platform,

View File

@@ -2,10 +2,10 @@ import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/interfaces/log.interface.dart';
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
import 'package:immich_mobile/domain/models/log.model.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
import 'package:logging/logging.dart';
/// Service responsible for handling application logging.
@@ -14,8 +14,8 @@ import 'package:logging/logging.dart';
/// writes them to a persistent [ILogRepository], and manages log levels
/// via [IStoreRepository]
class LogService {
final IsarLogRepository _logRepository;
final IsarStoreRepository _storeRepository;
final ILogRepository _logRepository;
final IStoreRepository _storeRepository;
final List<LogMessage> _msgBuffer = [];
@@ -37,8 +37,8 @@ class LogService {
}
static Future<LogService> init({
required IsarLogRepository logRepository,
required IsarStoreRepository storeRepository,
required ILogRepository logRepository,
required IStoreRepository storeRepository,
bool shouldBuffer = true,
}) async {
_instance ??= await create(
@@ -50,8 +50,8 @@ class LogService {
}
static Future<LogService> create({
required IsarLogRepository logRepository,
required IsarStoreRepository storeRepository,
required ILogRepository logRepository,
required IStoreRepository storeRepository,
bool shouldBuffer = true,
}) async {
final instance = LogService._(logRepository, storeRepository, shouldBuffer);

View File

@@ -1,18 +1,18 @@
import 'dart:async';
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
/// Provides access to a persistent key-value store with an in-memory cache.
/// Listens for repository changes to keep the cache updated.
class StoreService {
final IsarStoreRepository _storeRepository;
final IStoreRepository _storeRepository;
/// In-memory cache. Keys are [StoreKey.id]
final Map<int, Object?> _cache = {};
late final StreamSubscription<StoreDto> _storeUpdateSubscription;
StoreService._({required IsarStoreRepository storeRepository})
StoreService._({required IStoreRepository storeRepository})
: _storeRepository = storeRepository;
// TODO: Temporary typedef to make minimal changes. Remove this and make the presentation layer access store through a provider
@@ -26,14 +26,14 @@ class StoreService {
// TODO: Replace the implementation with the one from create after removing the typedef
static Future<StoreService> init({
required IsarStoreRepository storeRepository,
required IStoreRepository storeRepository,
}) async {
_instance ??= await create(storeRepository: storeRepository);
return _instance!;
}
static Future<StoreService> create({
required IsarStoreRepository storeRepository,
required IStoreRepository storeRepository,
}) async {
final instance = StoreService._(storeRepository: storeRepository);
await instance._populateCache();

View File

@@ -1,7 +1,7 @@
import 'dart:async';
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
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:logging/logging.dart';
import 'package:openapi/api.dart';
@@ -9,12 +9,12 @@ import 'package:openapi/api.dart';
class SyncStreamService {
final Logger _logger = Logger('SyncStreamService');
final SyncApiRepository _syncApiRepository;
final ISyncApiRepository _syncApiRepository;
final SyncStreamRepository _syncStreamRepository;
final bool Function()? _cancelChecker;
SyncStreamService({
required SyncApiRepository syncApiRepository,
required ISyncApiRepository syncApiRepository,
required SyncStreamRepository syncStreamRepository,
bool Function()? cancelChecker,
}) : _syncApiRepository = syncApiRepository,

View File

@@ -3,11 +3,11 @@ import 'dart:math' as math;
import 'package:collection/collection.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/interfaces/timeline.interface.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/setting.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/domain/services/setting.service.dart';
import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart';
import 'package:immich_mobile/utils/async_mutex.dart';
typedef TimelineAssetSource = Future<List<BaseAsset>> Function(
@@ -18,11 +18,11 @@ typedef TimelineAssetSource = Future<List<BaseAsset>> Function(
typedef TimelineBucketSource = Stream<List<Bucket>> Function();
class TimelineFactory {
final DriftTimelineRepository _timelineRepository;
final ITimelineRepository _timelineRepository;
final SettingsService _settingsService;
const TimelineFactory({
required DriftTimelineRepository timelineRepository,
required ITimelineRepository timelineRepository,
required SettingsService settingsService,
}) : _timelineRepository = timelineRepository,
_settingsService = settingsService;

View File

@@ -1,11 +1,13 @@
import 'dart:typed_data';
import 'dart:ui';
import 'package:immich_mobile/domain/interfaces/asset_media.interface.dart';
import 'package:photo_manager/photo_manager.dart';
class AssetMediaRepository {
class AssetMediaRepository implements IAssetMediaRepository {
const AssetMediaRepository();
@override
Future<Uint8List?> getThumbnail(
String id, {
int quality = 80,

View File

@@ -1,19 +1,23 @@
import 'package:immich_mobile/domain/interfaces/device_asset.interface.dart';
import 'package:immich_mobile/domain/models/device_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:isar/isar.dart';
class IsarDeviceAssetRepository extends IsarDatabaseRepository {
class IsarDeviceAssetRepository extends IsarDatabaseRepository
implements IDeviceAssetRepository {
final Isar _db;
const IsarDeviceAssetRepository(this._db) : super(_db);
@override
Future<void> deleteIds(List<String> ids) {
return transaction(() async {
await _db.deviceAssetEntitys.deleteAllByAssetId(ids.toList());
});
}
@override
Future<List<DeviceAsset>> getByIds(List<String> localIds) {
return _db.deviceAssetEntitys
.where()
@@ -22,6 +26,7 @@ class IsarDeviceAssetRepository extends IsarDatabaseRepository {
.then((value) => value.map((e) => e.toModel()).toList());
}
@override
Future<bool> updateAll(List<DeviceAsset> assetHash) {
return transaction(() async {
await _db.deviceAssetEntitys

View File

@@ -1,30 +1,36 @@
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
import 'package:immich_mobile/domain/models/exif.model.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'
as entity;
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:isar/isar.dart';
class IsarExifRepository extends IsarDatabaseRepository {
class IsarExifRepository extends IsarDatabaseRepository
implements IExifInfoRepository {
final Isar _db;
const IsarExifRepository(this._db) : super(_db);
@override
Future<void> delete(int assetId) async {
await transaction(() async {
await _db.exifInfos.delete(assetId);
});
}
@override
Future<void> deleteAll() async {
await transaction(() async {
await _db.exifInfos.clear();
});
}
@override
Future<ExifInfo?> get(int assetId) async {
return (await _db.exifInfos.get(assetId))?.toDto();
}
@override
Future<ExifInfo> update(ExifInfo exifInfo) {
return transaction(() async {
await _db.exifInfos.put(entity.ExifInfo.fromDto(exifInfo));
@@ -32,6 +38,7 @@ class IsarExifRepository extends IsarDatabaseRepository {
});
}
@override
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos) {
return transaction(() async {
await _db.exifInfos.putAll(

View File

@@ -1,4 +1,5 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/local_album.model.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart';
@@ -7,15 +8,15 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.d
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:platform/platform.dart';
enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum }
class DriftLocalAlbumRepository extends DriftDatabaseRepository {
class DriftLocalAlbumRepository extends DriftDatabaseRepository
implements ILocalAlbumRepository {
final Drift _db;
final Platform _platform;
const DriftLocalAlbumRepository(this._db, {Platform? platform})
: _platform = platform ?? const LocalPlatform(),
super(_db);
@override
Future<List<LocalAlbum>> getAll({Set<SortLocalAlbumsBy> sortBy = const {}}) {
final assetCount = _db.localAlbumAssetEntity.assetId.count();
@@ -55,6 +56,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
.get();
}
@override
Future<void> delete(String albumId) => transaction(() async {
// Remove all assets that are only in this particular album
// We cannot remove all assets in the album because they might be in other albums in iOS
@@ -70,6 +72,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
.delete();
});
@override
Future<void> syncDeletes(
String albumId,
Iterable<String> assetIdsToKeep,
@@ -98,6 +101,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
await deleteSmt.go();
}
@override
Future<void> upsert(
LocalAlbum localAlbum, {
Iterable<LocalAsset> toUpsert = const [],
@@ -130,6 +134,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
});
}
@override
Future<void> updateAll(Iterable<LocalAlbum> albums) {
return _db.transaction(() async {
await _db.localAlbumEntity
@@ -180,6 +185,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
});
}
@override
Future<List<LocalAsset>> getAssets(String albumId) {
final query = _db.localAlbumAssetEntity.select().join(
[
@@ -196,6 +202,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
.get();
}
@override
Future<List<String>> getAssetIds(String albumId) {
final query = _db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId])
@@ -205,6 +212,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
.get();
}
@override
Future<void> processDelta({
required List<LocalAsset> updates,
required List<String> deletes,
@@ -245,6 +253,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
});
}
@override
Future<List<LocalAsset>> getAssetsToHash(String albumId) {
final query = _db.localAlbumAssetEntity.select().join(
[

View File

@@ -1,12 +1,15 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
class DriftLocalAssetRepository extends DriftDatabaseRepository {
class DriftLocalAssetRepository extends DriftDatabaseRepository
implements ILocalAssetRepository {
final Drift _db;
const DriftLocalAssetRepository(this._db) : super(_db);
@override
Future<void> updateHashes(Iterable<LocalAsset> hashes) {
if (hashes.isEmpty) {
return Future.value();

View File

@@ -1,23 +1,28 @@
import 'package:immich_mobile/domain/interfaces/log.interface.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';
class IsarLogRepository extends IsarDatabaseRepository {
class IsarLogRepository extends IsarDatabaseRepository
implements ILogRepository {
final Isar _db;
const IsarLogRepository(super.db) : _db = db;
@override
Future<bool> deleteAll() async {
await transaction(() async => await _db.loggerMessages.clear());
return true;
}
@override
Future<List<LogMessage>> getAll() async {
final logs =
await _db.loggerMessages.where().sortByCreatedAtDesc().findAll();
return logs.map((l) => l.toDto()).toList();
}
@override
Future<bool> insert(LogMessage log) async {
final logEntity = LoggerMessage.fromDto(log);
await transaction(() async {
@@ -26,6 +31,7 @@ class IsarLogRepository extends IsarDatabaseRepository {
return true;
}
@override
Future<bool> insertAll(Iterable<LogMessage> logs) async {
await transaction(() async {
final logEntities =
@@ -35,6 +41,7 @@ class IsarLogRepository extends IsarDatabaseRepository {
return true;
}
@override
Future<void> truncate({int limit = 250}) async {
await transaction(() async {
final count = await _db.loggerMessages.count();

View File

@@ -1,12 +1,14 @@
import 'dart:io';
import 'package:immich_mobile/domain/interfaces/storage.interface.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:logging/logging.dart';
import 'package:photo_manager/photo_manager.dart';
class StorageRepository {
class StorageRepository implements IStorageRepository {
final _log = Logger('StorageRepository');
@override
Future<File?> getFileForAsset(LocalAsset asset) async {
File? file;
try {

View File

@@ -1,3 +1,4 @@
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
@@ -5,12 +6,14 @@ import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
import 'package:isar/isar.dart';
class IsarStoreRepository extends IsarDatabaseRepository {
class IsarStoreRepository extends IsarDatabaseRepository
implements IStoreRepository {
final Isar _db;
final validStoreKeys = StoreKey.values.map((e) => e.id).toSet();
IsarStoreRepository(super.db) : _db = db;
@override
Future<bool> deleteAll() async {
return await transaction(() async {
await _db.storeValues.clear();
@@ -18,6 +21,7 @@ class IsarStoreRepository extends IsarDatabaseRepository {
});
}
@override
Stream<StoreDto<Object>> watchAll() {
return _db.storeValues
.filter()
@@ -30,10 +34,12 @@ class IsarStoreRepository extends IsarDatabaseRepository {
);
}
@override
Future<void> delete<T>(StoreKey<T> key) async {
return await transaction(() async => await _db.storeValues.delete(key.id));
}
@override
Future<bool> insert<T>(StoreKey<T> key, T value) async {
return await transaction(() async {
await _db.storeValues.put(await _fromValue(key, value));
@@ -41,6 +47,7 @@ class IsarStoreRepository extends IsarDatabaseRepository {
});
}
@override
Future<T?> tryGet<T>(StoreKey<T> key) async {
final entity = (await _db.storeValues.get(key.id));
if (entity == null) {
@@ -49,6 +56,7 @@ class IsarStoreRepository extends IsarDatabaseRepository {
return await _toValue(key, entity);
}
@override
Future<bool> update<T>(StoreKey<T> key, T value) async {
return await transaction(() async {
await _db.storeValues.put(await _fromValue(key, value));
@@ -56,6 +64,7 @@ class IsarStoreRepository extends IsarDatabaseRepository {
});
}
@override
Stream<T?> watch<T>(StoreKey<T> key) async* {
yield* _db.storeValues
.watchObject(key.id, fireImmediately: true)
@@ -100,6 +109,7 @@ class IsarStoreRepository extends IsarDatabaseRepository {
return StoreValue(key.id, intValue: intValue, strValue: strValue);
}
@override
Future<List<StoreDto<Object>>> getAll() async {
final entities = await _db.storeValues
.filter()

View File

@@ -3,21 +3,24 @@ import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/interfaces/sync_api.interface.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';
class SyncApiRepository {
class SyncApiRepository implements ISyncApiRepository {
final Logger _logger = Logger('SyncApiRepository');
final ApiService _api;
SyncApiRepository(this._api);
@override
Future<void> ack(List<String> data) {
return _api.syncApi.sendSyncAck(SyncAckSetDto(acks: data));
}
@override
Future<void> streamChanges(
Function(List<SyncEvent>, Function() abort) onData, {
int batchSize = kSyncEventBatchSize,

View File

@@ -3,13 +3,15 @@ import 'dart:async';
import 'package:drift/drift.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/interfaces/timeline.interface.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:stream_transform/stream_transform.dart';
class DriftTimelineRepository extends DriftDatabaseRepository {
class DriftTimelineRepository extends DriftDatabaseRepository
implements ITimelineRepository {
final Drift _db;
const DriftTimelineRepository(super._db) : _db = _db;
@@ -26,6 +28,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
return buckets;
}
@override
Stream<List<Bucket>> watchMainBucket(
List<String> userIds, {
GroupAssetsBy groupBy = GroupAssetsBy.day,
@@ -46,6 +49,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
.throttle(const Duration(seconds: 3), trailing: true);
}
@override
Future<List<BaseAsset>> getMainBucketAssets(
List<String> userIds, {
required int offset,
@@ -86,6 +90,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
.get();
}
@override
Stream<List<Bucket>> watchLocalBucket(
String albumId, {
GroupAssetsBy groupBy = GroupAssetsBy.day,
@@ -119,6 +124,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
}).watch();
}
@override
Future<List<BaseAsset>> getLocalBucketAssets(
String albumId, {
required int offset,

View File

@@ -0,0 +1,55 @@
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/interfaces/database.interface.dart';
import 'package:immich_mobile/models/albums/album_search.model.dart';
abstract interface class IAlbumRepository implements IDatabaseRepository {
Future<Album> create(Album album);
Future<Album?> get(int id);
Future<Album?> getByName(
String name, {
bool? shared,
bool? remote,
bool? owner,
});
Future<List<Album>> getAll({
bool? shared,
bool? remote,
int? ownerId,
AlbumSort? sortBy,
});
Future<Album> update(Album album);
Future<void> delete(int albumId);
Future<void> deleteAllLocal();
Future<int> count({bool? local});
Future<void> addUsers(Album album, List<UserDto> users);
Future<void> removeUsers(Album album, List<UserDto> users);
Future<void> addAssets(Album album, List<Asset> assets);
Future<void> removeAssets(Album album, List<Asset> assets);
Future<Album> recalculateMetadata(Album album);
Future<List<Album>> search(String searchTerm, QuickFilterMode filterMode);
Stream<List<Album>> watchRemoteAlbums();
Stream<List<Album>> watchLocalAlbums();
Stream<Album?> watchAlbum(int id);
Future<void> clearTable();
}
enum AlbumSort { remoteId, localId }

View File

@@ -0,0 +1,68 @@
import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/interfaces/database.interface.dart';
abstract interface class IAssetRepository implements IDatabaseRepository {
Future<Asset?> getByRemoteId(String id);
Future<Asset?> getByOwnerIdChecksum(int ownerId, String checksum);
Future<List<Asset>> getAllByRemoteId(
Iterable<String> ids, {
AssetState? state,
});
Future<List<Asset?>> getAllByOwnerIdChecksum(
List<int> ids,
List<String> checksums,
);
Future<List<Asset>> getAll({
required String ownerId,
AssetState? state,
AssetSort? sortBy,
int? limit,
});
Future<List<Asset>> getAllLocal();
Future<List<Asset>> getByAlbum(
Album album, {
Iterable<String> notOwnedBy = const [],
String? ownerId,
AssetState? state,
AssetSort? sortBy,
});
Future<Asset> update(Asset asset);
Future<List<Asset>> updateAll(List<Asset> assets);
Future<void> deleteAllByRemoteId(List<String> ids, {AssetState? state});
Future<void> deleteByIds(List<int> ids);
Future<List<Asset>> getMatches({
required List<Asset> assets,
required String ownerId,
AssetState? state,
int limit = 100,
});
Future<void> upsertDuplicatedAssets(Iterable<String> duplicatedAssets);
Future<List<String>> getAllDuplicatedAssetIds();
Future<List<Asset>> getStackAssets(String stackId);
Future<void> clearTable();
Stream<Asset?> watchAsset(int id, {bool fireImmediately = false});
Future<List<Asset>> getTrashAssets(String userId);
Future<List<Asset>> getRecentlyTakenAssets(String userId);
Future<List<Asset>> getMotionAssets(String userId);
}
enum AssetSort { checksum, ownerIdChecksum }

View File

@@ -0,0 +1,26 @@
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
abstract interface class IAssetApiRepository {
// Future<Asset> get(String id);
// Future<List<Asset>> getAll();
// Future<Asset> create(Asset asset);
Future<Asset> update(
String id, {
String? description,
});
// Future<void> delete(String id);
Future<List<Asset>> search({List<String> personIds = const []});
Future<void> updateVisibility(
List<String> list,
AssetVisibilityEnum visibility,
);
Future<String?> getAssetMIMEType(String id);
}

View File

@@ -0,0 +1,10 @@
import 'package:immich_mobile/entities/asset.entity.dart';
abstract interface class IAssetMediaRepository {
Future<List<String>> deleteAll(List<String> ids);
Future<Asset?> get(String id);
/// Obtaining the correct original filename of the asset
Future<String?> getOriginalFilename(String id);
}

View File

@@ -0,0 +1,11 @@
import 'package:immich_mobile/interfaces/database.interface.dart';
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
abstract interface class IAuthRepository implements IDatabaseRepository {
Future<void> clearLocalData();
String getAccessToken();
bool getEndpointSwitchingFeature();
String? getPreferredWifiName();
String? getLocalEndpoint();
List<AuxilaryEndpoint> getExternalEndpointList();
}

View File

@@ -0,0 +1,14 @@
import 'package:immich_mobile/models/auth/login_response.model.dart';
abstract interface class IAuthApiRepository {
Future<LoginResponse> login(String email, String password);
Future<void> logout();
Future<void> changePassword(String newPassword);
Future<bool> unlockPinCode(String pinCode);
Future<void> lockPinCode();
Future<void> setupPinCode(String pinCode);
}

View File

@@ -0,0 +1,16 @@
import 'package:immich_mobile/entities/backup_album.entity.dart';
import 'package:immich_mobile/interfaces/database.interface.dart';
abstract interface class IBackupAlbumRepository implements IDatabaseRepository {
Future<List<BackupAlbum>> getAll({BackupAlbumSort? sort});
Future<List<String>> getIdsBySelection(BackupSelection backup);
Future<List<BackupAlbum>> getAllBySelection(BackupSelection backup);
Future<void> updateAll(List<BackupAlbum> backupAlbums);
Future<void> deleteAll(List<int> ids);
}
enum BackupAlbumSort { id }

View File

@@ -0,0 +1,27 @@
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/models/cast/cast_manager_state.dart';
abstract interface class ICastDestinationService {
Future<bool> initialize();
CastDestinationType getType();
void Function(bool)? onConnectionState;
void Function(Duration)? onCurrentTime;
void Function(Duration)? onDuration;
void Function(String)? onReceiverName;
void Function(CastState)? onCastState;
Future<void> connect(dynamic device);
void loadMedia(Asset asset, bool reload);
void play();
void pause();
void seekTo(Duration position);
void stop();
Future<void> disconnect();
Future<List<(String, CastDestinationType, dynamic)>> getDevices();
}

View File

@@ -0,0 +1,16 @@
import 'package:immich_mobile/entities/etag.entity.dart';
import 'package:immich_mobile/interfaces/database.interface.dart';
abstract interface class IETagRepository implements IDatabaseRepository {
Future<ETag?> get(String id);
Future<ETag?> getById(String id);
Future<List<String>> getAllIds();
Future<void> upsertAll(List<ETag> etags);
Future<void> deleteByIds(List<String> ids);
Future<void> clearTable();
}

View File

@@ -0,0 +1,36 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:immich_mobile/entities/asset.entity.dart';
abstract interface class IFileMediaRepository {
Future<Asset?> saveImage(
Uint8List data, {
required String title,
String? relativePath,
});
Future<Asset?> saveImageWithFile(
String filePath, {
String? title,
String? relativePath,
});
Future<Asset?> saveVideo(
File file, {
required String title,
String? relativePath,
});
Future<Asset?> saveLivePhoto({
required File image,
required File video,
required String title,
});
Future<void> clearFileCache();
Future<void> enableBackgroundAccess();
Future<void> requestExtendedPermissions();
}

View File

@@ -0,0 +1,6 @@
import 'package:immich_mobile/entities/asset.entity.dart';
abstract interface class IFolderApiRepository {
Future<List<String>> getAllUniquePaths();
Future<List<Asset>> getAssetsForPath(String? path);
}

View File

@@ -0,0 +1,5 @@
abstract interface class ILocalFilesManager {
Future<bool> moveToTrash(List<String> mediaUrls);
Future<bool> restoreFromTrash(String fileName, int type);
Future<bool> requestManageMediaPermission();
}

View File

@@ -0,0 +1,11 @@
import 'package:background_downloader/background_downloader.dart';
abstract interface class IUploadRepository {
void Function(TaskStatusUpdate)? onUploadStatus;
void Function(TaskProgressUpdate)? onTaskProgress;
Future<bool> upload(UploadTask task);
Future<bool> cancel(String id);
Future<void> deleteAllTrackingRecords();
Future<void> deleteRecordsWithIds(List<String> id);
}

View File

@@ -0,0 +1,5 @@
abstract interface class IWidgetRepository {
Future<void> saveData(String key, String value);
Future<void> refresh(String name);
Future<void> setAppGroupId(String appGroupId);
}

View File

@@ -4,13 +4,14 @@ import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:immich_mobile/domain/interfaces/asset_media.interface.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/repositories/asset_media.repository.dart';
import 'package:immich_mobile/presentation/widgets/timeline/constants.dart';
import 'package:immich_mobile/providers/image/cache/thumbnail_image_cache_manager.dart';
class LocalThumbProvider extends ImageProvider<LocalThumbProvider> {
final AssetMediaRepository _assetMediaRepository =
final IAssetMediaRepository _assetMediaRepository =
const AssetMediaRepository();
final CacheManager? cacheManager;

View File

@@ -118,8 +118,10 @@ class AuthNotifier extends StateNotifier<AuthState> {
}) async {
await _apiService.setAccessToken(accessToken);
await _widgetService.writeSessionKey(accessToken);
await _widgetService.writeServerList();
await _widgetService.writeCredentials(
Store.get(StoreKey.serverEndpoint),
accessToken,
);
// Get the deviceid from the store if it exists, otherwise generate a new one
String deviceId =
@@ -188,7 +190,6 @@ class AuthNotifier extends StateNotifier<AuthState> {
Future<void> saveLocalEndpoint(String url) async {
await Store.put(StoreKey.localEndpoint, url);
await _widgetService.writeServerList();
}
String? getSavedWifiName() {

View File

@@ -9,6 +9,8 @@ import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/entities/backup_album.entity.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
import 'package:immich_mobile/interfaces/file_media.interface.dart';
import 'package:immich_mobile/models/auth/auth_state.model.dart';
import 'package:immich_mobile/models/backup/available_album.model.dart';
import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
@@ -22,7 +24,6 @@ import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart';
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
import 'package:immich_mobile/repositories/album_media.repository.dart';
import 'package:immich_mobile/repositories/backup.repository.dart';
import 'package:immich_mobile/repositories/file_media.repository.dart';
import 'package:immich_mobile/services/background.service.dart';
import 'package:immich_mobile/services/backup.service.dart';
@@ -107,7 +108,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
final BackgroundService _backgroundService;
final GalleryPermissionNotifier _galleryPermissionNotifier;
final AlbumMediaRepository _albumMediaRepository;
final FileMediaRepository _fileMediaRepository;
final IFileMediaRepository _fileMediaRepository;
final BackupAlbumService _backupAlbumService;
final Ref ref;

View File

@@ -1,5 +1,6 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/interfaces/cast_destination_service.interface.dart';
import 'package:immich_mobile/models/cast/cast_manager_state.dart';
import 'package:immich_mobile/services/gcast.service.dart';
@@ -9,7 +10,7 @@ final castProvider = StateNotifierProvider<CastNotifier, CastManagerState>(
class CastNotifier extends StateNotifier<CastManagerState> {
// more cast providers can be added here (ie Fcast)
final GCastService _gCastService;
final ICastDestinationService _gCastService;
List<(String, CastDestinationType, dynamic)> discovered = List.empty();

View File

@@ -1,7 +1,8 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
final localAlbumRepository = Provider<DriftLocalAlbumRepository>(
final localAlbumRepository = Provider<ILocalAlbumRepository>(
(ref) => DriftLocalAlbumRepository(ref.watch(driftProvider)),
);

View File

@@ -1,7 +1,8 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart';
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
final localAssetRepository = Provider<DriftLocalAssetRepository>(
final localAssetRepository = Provider<ILocalAssetRepository>(
(ref) => DriftLocalAssetRepository(ref.watch(driftProvider)),
);

View File

@@ -1,7 +1,8 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/interfaces/device_asset.interface.dart';
import 'package:immich_mobile/infrastructure/repositories/device_asset.repository.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
final deviceAssetRepositoryProvider = Provider<IsarDeviceAssetRepository>(
final deviceAssetRepositoryProvider = Provider<IDeviceAssetRepository>(
(ref) => IsarDeviceAssetRepository(ref.watch(isarProvider)),
);

View File

@@ -1,4 +1,5 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -6,5 +7,5 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'exif.provider.g.dart';
@Riverpod(keepAlive: true)
IsarExifRepository exifRepository(Ref ref) =>
IExifInfoRepository exifRepository(Ref ref) =>
IsarExifRepository(ref.watch(isarProvider));

View File

@@ -6,11 +6,11 @@ part of 'exif.provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$exifRepositoryHash() => r'bf4a3f6a50d954a23d317659b4f3e2f381066463';
String _$exifRepositoryHash() => r'f0abe778ed61fbb257001fdf2ac6e17814011fee';
/// See also [exifRepository].
@ProviderFor(exifRepository)
final exifRepositoryProvider = Provider<IsarExifRepository>.internal(
final exifRepositoryProvider = Provider<IExifInfoRepository>.internal(
exifRepository,
name: r'exifRepositoryProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
@@ -22,6 +22,6 @@ final exifRepositoryProvider = Provider<IsarExifRepository>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef ExifRepositoryRef = ProviderRef<IsarExifRepository>;
typedef ExifRepositoryRef = ProviderRef<IExifInfoRepository>;
// 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

View File

@@ -1,6 +1,7 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/interfaces/storage.interface.dart';
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
final storageRepositoryProvider = Provider<StorageRepository>(
final storageRepositoryProvider = Provider<IStorageRepository>(
(ref) => StorageRepository(),
);

View File

@@ -1,4 +1,5 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
import 'package:immich_mobile/domain/services/store.service.dart';
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
@@ -7,7 +8,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'store.provider.g.dart';
@Riverpod(keepAlive: true)
IsarStoreRepository storeRepository(Ref ref) =>
IStoreRepository storeRepository(Ref ref) =>
IsarStoreRepository(ref.watch(isarProvider));
@Riverpod(keepAlive: true)

View File

@@ -6,11 +6,11 @@ part of 'store.provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$storeRepositoryHash() => r'659cb134466e4b0d5f04e2fc93e426350d99545f';
String _$storeRepositoryHash() => r'99d24875d30c5e86b1c6caa352a0026167114e62';
/// See also [storeRepository].
@ProviderFor(storeRepository)
final storeRepositoryProvider = Provider<IsarStoreRepository>.internal(
final storeRepositoryProvider = Provider<IStoreRepository>.internal(
storeRepository,
name: r'storeRepositoryProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
@@ -22,7 +22,7 @@ final storeRepositoryProvider = Provider<IsarStoreRepository>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef StoreRepositoryRef = ProviderRef<IsarStoreRepository>;
typedef StoreRepositoryRef = ProviderRef<IStoreRepository>;
String _$storeServiceHash() => r'250e10497c42df360e9e1f9a618d0b19c1b5b0a0';
/// See also [storeService].

View File

@@ -1,11 +1,12 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/interfaces/timeline.interface.dart';
import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
final timelineRepositoryProvider = Provider<DriftTimelineRepository>(
final timelineRepositoryProvider = Provider<ITimelineRepository>(
(ref) => DriftTimelineRepository(ref.watch(driftProvider)),
);

View File

@@ -6,20 +6,20 @@ import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
as entity;
import 'package:immich_mobile/interfaces/album.interface.dart';
import 'package:immich_mobile/models/albums/album_search.model.dart';
import 'package:immich_mobile/providers/db.provider.dart';
import 'package:immich_mobile/repositories/database.repository.dart';
import 'package:immich_mobile/utils/hash.dart';
import 'package:isar/isar.dart';
enum AlbumSort { remoteId, localId }
final albumRepositoryProvider =
Provider((ref) => AlbumRepository(ref.watch(dbProvider)));
class AlbumRepository extends DatabaseRepository {
class AlbumRepository extends DatabaseRepository implements IAlbumRepository {
AlbumRepository(super.db);
@override
Future<int> count({bool? local}) {
final baseQuery = db.albums.where();
final QueryBuilder<Album, Album, QAfterWhereClause> query = switch (local) {
@@ -30,8 +30,10 @@ class AlbumRepository extends DatabaseRepository {
return query.count();
}
@override
Future<Album> create(Album album) => txn(() => db.albums.store(album));
@override
Future<Album?> getByName(
String name, {
bool? shared,
@@ -56,10 +58,13 @@ class AlbumRepository extends DatabaseRepository {
return query.findFirst();
}
@override
Future<Album> update(Album album) => txn(() => db.albums.store(album));
@override
Future<void> delete(int albumId) => txn(() => db.albums.delete(albumId));
@override
Future<List<Album>> getAll({
bool? shared,
bool? remote,
@@ -91,18 +96,23 @@ class AlbumRepository extends DatabaseRepository {
return query.findAll();
}
@override
Future<Album?> get(int id) => db.albums.get(id);
@override
Future<void> removeUsers(Album album, List<UserDto> users) => txn(
() => album.sharedUsers.update(unlink: users.map(entity.User.fromDto)),
);
@override
Future<void> addAssets(Album album, List<Asset> assets) =>
txn(() => album.assets.update(link: assets));
@override
Future<void> removeAssets(Album album, List<Asset> assets) =>
txn(() => album.assets.update(unlink: assets));
@override
Future<Album> recalculateMetadata(Album album) async {
album.startDate = await album.assets.filter().fileCreatedAtProperty().min();
album.endDate = await album.assets.filter().fileCreatedAtProperty().max();
@@ -111,12 +121,15 @@ class AlbumRepository extends DatabaseRepository {
return album;
}
@override
Future<void> addUsers(Album album, List<UserDto> users) =>
txn(() => album.sharedUsers.update(link: users.map(entity.User.fromDto)));
@override
Future<void> deleteAllLocal() =>
txn(() => db.albums.where().localIdIsNotNull().deleteAll());
@override
Future<List<Album>> search(
String searchTerm,
QuickFilterMode filterMode,
@@ -139,20 +152,24 @@ class AlbumRepository extends DatabaseRepository {
return await query.findAll();
}
@override
Future<void> clearTable() async {
await txn(() async {
await db.albums.clear();
});
}
@override
Stream<List<Album>> watchRemoteAlbums() {
return db.albums.where().remoteIdIsNotNull().watch();
}
@override
Stream<List<Album>> watchLocalAlbums() {
return db.albums.where().localIdIsNotNull().watch();
}
@override
Stream<Album?> watchAlbum(int id) {
return db.albums.watchObject(id, fireImmediately: true);
}

View File

@@ -4,19 +4,19 @@ import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/providers/db.provider.dart';
import 'package:immich_mobile/repositories/database.repository.dart';
import 'package:immich_mobile/utils/hash.dart';
import 'package:isar/isar.dart';
enum AssetSort { checksum, ownerIdChecksum }
final assetRepositoryProvider =
Provider((ref) => AssetRepository(ref.watch(dbProvider)));
class AssetRepository extends DatabaseRepository {
class AssetRepository extends DatabaseRepository implements IAssetRepository {
AssetRepository(super.db);
@override
Future<List<Asset>> getByAlbum(
Album album, {
Iterable<String> notOwnedBy = const [],
@@ -54,13 +54,16 @@ class AssetRepository extends DatabaseRepository {
return sortedQuery.findAll();
}
@override
Future<void> deleteByIds(List<int> ids) => txn(() async {
await db.assets.deleteAll(ids);
await db.exifInfos.deleteAll(ids);
});
@override
Future<Asset?> getByRemoteId(String id) => db.assets.getByRemoteId(id);
@override
Future<List<Asset>> getAllByRemoteId(
Iterable<String> ids, {
AssetState? state,
@@ -85,6 +88,7 @@ class AssetRepository extends DatabaseRepository {
};
}
@override
Future<List<Asset>> getAll({
required String ownerId,
AssetState? state,
@@ -123,11 +127,13 @@ class AssetRepository extends DatabaseRepository {
return limit == null ? query.findAll() : query.limit(limit).findAll();
}
@override
Future<List<Asset>> updateAll(List<Asset> assets) async {
await txn(() => db.assets.putAll(assets));
return assets;
}
@override
Future<List<Asset>> getMatches({
required List<Asset> assets,
required String ownerId,
@@ -148,34 +154,42 @@ class AssetRepository extends DatabaseRepository {
return _getMatchesImpl(query, fastHash(ownerId), assets, limit);
}
@override
Future<Asset> update(Asset asset) async {
await txn(() => asset.put(db));
return asset;
}
@override
Future<void> upsertDuplicatedAssets(Iterable<String> duplicatedAssets) => txn(
() => db.duplicatedAssets
.putAll(duplicatedAssets.map(DuplicatedAsset.new).toList()),
);
@override
Future<List<String>> getAllDuplicatedAssetIds() =>
db.duplicatedAssets.where().idProperty().findAll();
@override
Future<Asset?> getByOwnerIdChecksum(int ownerId, String checksum) =>
db.assets.getByOwnerIdChecksum(ownerId, checksum);
@override
Future<List<Asset?>> getAllByOwnerIdChecksum(
List<int> ownerIds,
List<String> checksums,
) =>
db.assets.getAllByOwnerIdChecksum(ownerIds, checksums);
@override
Future<List<Asset>> getAllLocal() =>
db.assets.where().localIdIsNotNull().findAll();
@override
Future<void> deleteAllByRemoteId(List<String> ids, {AssetState? state}) =>
txn(() => _getAllByRemoteIdImpl(ids, state).deleteAll());
@override
Future<List<Asset>> getStackAssets(String stackId) {
return db.assets
.filter()
@@ -188,16 +202,19 @@ class AssetRepository extends DatabaseRepository {
.findAll();
}
@override
Future<void> clearTable() async {
await txn(() async {
await db.assets.clear();
});
}
@override
Stream<Asset?> watchAsset(int id, {bool fireImmediately = false}) {
return db.assets.watchObject(id, fireImmediately: fireImmediately);
}
@override
Future<List<Asset>> getTrashAssets(String userId) {
return db.assets
.where()
@@ -208,6 +225,7 @@ class AssetRepository extends DatabaseRepository {
.findAll();
}
@override
Future<List<Asset>> getRecentlyTakenAssets(String userId) {
return db.assets
.where()
@@ -218,6 +236,7 @@ class AssetRepository extends DatabaseRepository {
.findAll();
}
@override
Future<List<Asset>> getMotionAssets(String userId) {
return db.assets
.where()

View File

@@ -1,6 +1,7 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/repositories/api.repository.dart';
import 'package:openapi/api.dart';
@@ -12,12 +13,13 @@ final assetApiRepositoryProvider = Provider(
),
);
class AssetApiRepository extends ApiRepository {
class AssetApiRepository extends ApiRepository implements IAssetApiRepository {
final AssetsApi _api;
final SearchApi _searchApi;
AssetApiRepository(this._api, this._searchApi);
@override
Future<Asset> update(String id, {String? description}) async {
final response = await checkNull(
_api.updateAsset(id, UpdateAssetDto(description: description)),
@@ -25,6 +27,7 @@ class AssetApiRepository extends ApiRepository {
return Asset.remote(response);
}
@override
Future<List<Asset>> search({List<String> personIds = const []}) async {
// TODO this always fetches all assets, change API and usage to actually do pagination
final List<Asset> result = [];
@@ -47,6 +50,7 @@ class AssetApiRepository extends ApiRepository {
return result;
}
@override
Future<void> updateVisibility(
List<String> ids,
AssetVisibilityEnum visibility,
@@ -69,6 +73,7 @@ class AssetApiRepository extends ApiRepository {
}
}
@override
Future<String?> getAssetMIMEType(String assetId) async {
final response = await checkNull(_api.getAssetInfo(assetId));

View File

@@ -3,15 +3,18 @@ import 'package:immich_mobile/domain/models/exif.model.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
import 'package:immich_mobile/utils/hash.dart';
import 'package:photo_manager/photo_manager.dart' hide AssetType;
final assetMediaRepositoryProvider = Provider((ref) => AssetMediaRepository());
class AssetMediaRepository {
class AssetMediaRepository implements IAssetMediaRepository {
@override
Future<List<String>> deleteAll(List<String> ids) =>
PhotoManager.editor.deleteWithIds(ids);
@override
Future<Asset?> get(String id) async {
final entity = await AssetEntity.fromId(id);
return toAsset(entity);
@@ -44,6 +47,7 @@ class AssetMediaRepository {
return asset;
}
@override
Future<String?> getOriginalFilename(String id) async {
final entity = await AssetEntity.fromId(id);

View File

@@ -10,20 +10,23 @@ import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/interfaces/auth.interface.dart';
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
import 'package:immich_mobile/providers/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/repositories/database.repository.dart';
final authRepositoryProvider = Provider<AuthRepository>(
(ref) => AuthRepository(ref.watch(dbProvider), ref.watch(driftProvider)),
final authRepositoryProvider = Provider<IAuthRepository>(
(ref) =>
AuthRepository(ref.watch(dbProvider), drift: ref.watch(driftProvider)),
);
class AuthRepository extends DatabaseRepository {
class AuthRepository extends DatabaseRepository implements IAuthRepository {
final Drift _drift;
AuthRepository(super.db, this._drift);
AuthRepository(super.db, {required Drift drift}) : _drift = drift;
@override
Future<void> clearLocalData() {
return db.writeTxn(() {
return Future.wait([
@@ -38,22 +41,27 @@ class AuthRepository extends DatabaseRepository {
});
}
@override
String getAccessToken() {
return Store.get(StoreKey.accessToken);
}
@override
bool getEndpointSwitchingFeature() {
return Store.tryGet(StoreKey.autoEndpointSwitching) ?? false;
}
@override
String? getPreferredWifiName() {
return Store.tryGet(StoreKey.preferredWifiName);
}
@override
String? getLocalEndpoint() {
return Store.tryGet(StoreKey.localEndpoint);
}
@override
List<AuxilaryEndpoint> getExternalEndpointList() {
final jsonString = Store.tryGet(StoreKey.externalEndpointList);

View File

@@ -1,4 +1,5 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/interfaces/auth_api.interface.dart';
import 'package:immich_mobile/models/auth/login_response.model.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/repositories/api.repository.dart';
@@ -8,11 +9,12 @@ import 'package:openapi/api.dart';
final authApiRepositoryProvider =
Provider((ref) => AuthApiRepository(ref.watch(apiServiceProvider)));
class AuthApiRepository extends ApiRepository {
class AuthApiRepository extends ApiRepository implements IAuthApiRepository {
final ApiService _apiService;
AuthApiRepository(this._apiService);
@override
Future<void> changePassword(String newPassword) async {
await _apiService.usersApi.updateMyUser(
UserUpdateMeDto(
@@ -21,6 +23,7 @@ class AuthApiRepository extends ApiRepository {
);
}
@override
Future<LoginResponse> login(String email, String password) async {
final loginResponseDto = await checkNull(
_apiService.authenticationApi.login(
@@ -34,6 +37,7 @@ class AuthApiRepository extends ApiRepository {
return _mapLoginReponse(loginResponseDto);
}
@override
Future<void> logout() async {
await _apiService.authenticationApi
.logout()
@@ -52,6 +56,7 @@ class AuthApiRepository extends ApiRepository {
);
}
@override
Future<bool> unlockPinCode(String pinCode) async {
try {
await _apiService.authenticationApi
@@ -62,11 +67,13 @@ class AuthApiRepository extends ApiRepository {
}
}
@override
Future<void> setupPinCode(String pinCode) {
return _apiService.authenticationApi
.setupPinCode(PinCodeSetupDto(pinCode: pinCode));
}
@override
Future<void> lockPinCode() {
return _apiService.authenticationApi.lockAuthSession();
}

View File

@@ -1,17 +1,18 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/backup_album.entity.dart';
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
import 'package:immich_mobile/providers/db.provider.dart';
import 'package:immich_mobile/repositories/database.repository.dart';
import 'package:isar/isar.dart';
enum BackupAlbumSort { id }
final backupAlbumRepositoryProvider =
Provider((ref) => BackupAlbumRepository(ref.watch(dbProvider)));
class BackupAlbumRepository extends DatabaseRepository {
class BackupAlbumRepository extends DatabaseRepository
implements IBackupAlbumRepository {
BackupAlbumRepository(super.db);
@override
Future<List<BackupAlbum>> getAll({BackupAlbumSort? sort}) {
final baseQuery = db.backupAlbums.where();
final QueryBuilder<BackupAlbum, BackupAlbum, QAfterSortBy> query =
@@ -22,15 +23,19 @@ class BackupAlbumRepository extends DatabaseRepository {
return query.findAll();
}
@override
Future<List<String>> getIdsBySelection(BackupSelection backup) =>
db.backupAlbums.filter().selectionEqualTo(backup).idProperty().findAll();
@override
Future<List<BackupAlbum>> getAllBySelection(BackupSelection backup) =>
db.backupAlbums.filter().selectionEqualTo(backup).findAll();
@override
Future<void> deleteAll(List<int> ids) =>
txn(() => db.backupAlbums.deleteAll(ids));
@override
Future<void> updateAll(List<BackupAlbum> backupAlbums) =>
txn(() => db.backupAlbums.putAll(backupAlbums));
}

View File

@@ -1,5 +1,6 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/etag.entity.dart';
import 'package:immich_mobile/interfaces/etag.interface.dart';
import 'package:immich_mobile/providers/db.provider.dart';
import 'package:immich_mobile/repositories/database.repository.dart';
import 'package:isar/isar.dart';
@@ -7,20 +8,26 @@ import 'package:isar/isar.dart';
final etagRepositoryProvider =
Provider((ref) => ETagRepository(ref.watch(dbProvider)));
class ETagRepository extends DatabaseRepository {
class ETagRepository extends DatabaseRepository implements IETagRepository {
ETagRepository(super.db);
@override
Future<List<String>> getAllIds() => db.eTags.where().idProperty().findAll();
@override
Future<ETag?> get(String id) => db.eTags.getById(id);
@override
Future<void> upsertAll(List<ETag> etags) => txn(() => db.eTags.putAll(etags));
@override
Future<void> deleteByIds(List<String> ids) =>
txn(() => db.eTags.deleteAllById(ids));
@override
Future<ETag?> getById(String id) => db.eTags.getById(id);
@override
Future<void> clearTable() async {
await txn(() async {
await db.eTags.clear();

View File

@@ -3,12 +3,14 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/interfaces/file_media.interface.dart';
import 'package:immich_mobile/repositories/asset_media.repository.dart';
import 'package:photo_manager/photo_manager.dart' hide AssetType;
final fileMediaRepositoryProvider = Provider((ref) => FileMediaRepository());
class FileMediaRepository {
class FileMediaRepository implements IFileMediaRepository {
@override
Future<Asset?> saveImage(
Uint8List data, {
required String title,
@@ -23,6 +25,7 @@ class FileMediaRepository {
return AssetMediaRepository.toAsset(entity);
}
@override
Future<Asset?> saveImageWithFile(
String filePath, {
String? title,
@@ -36,6 +39,7 @@ class FileMediaRepository {
return AssetMediaRepository.toAsset(entity);
}
@override
Future<Asset?> saveLivePhoto({
required File image,
required File video,
@@ -49,6 +53,7 @@ class FileMediaRepository {
return AssetMediaRepository.toAsset(entity);
}
@override
Future<Asset?> saveVideo(
File file, {
required String title,
@@ -62,11 +67,14 @@ class FileMediaRepository {
return AssetMediaRepository.toAsset(entity);
}
@override
Future<void> clearFileCache() => PhotoManager.clearFileCache();
@override
Future<void> enableBackgroundAccess() =>
PhotoManager.setIgnorePermissionCheck(true);
@override
Future<void> requestExtendedPermissions() =>
PhotoManager.requestPermissionExtend();
}

View File

@@ -1,5 +1,6 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/interfaces/folder_api.interface.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/repositories/api.repository.dart';
import 'package:logging/logging.dart';
@@ -11,12 +12,14 @@ final folderApiRepositoryProvider = Provider(
),
);
class FolderApiRepository extends ApiRepository {
class FolderApiRepository extends ApiRepository
implements IFolderApiRepository {
final ViewApi _api;
final Logger _log = Logger("FolderApiRepository");
FolderApiRepository(this._api);
@override
Future<List<String>> getAllUniquePaths() async {
try {
final list = await _api.getUniqueOriginalPaths();
@@ -27,6 +30,7 @@ class FolderApiRepository extends ApiRepository {
}
}
@override
Future<List<Asset>> getAssetsForPath(String? path) async {
try {
final list = await _api.getAssetsByOriginalPath(path ?? '/');

View File

@@ -1,25 +1,25 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/services/local_files_manager.service.dart';
import 'package:immich_mobile/interfaces/local_files_manager.interface.dart';
import 'package:immich_mobile/utils/local_files_manager.dart';
final localFilesManagerRepositoryProvider = Provider(
(ref) =>
LocalFilesManagerRepository(ref.watch(localFileManagerServiceProvider)),
);
final localFilesManagerRepositoryProvider =
Provider((ref) => const LocalFilesManagerRepository());
class LocalFilesManagerRepository {
LocalFilesManagerRepository(this._service);
final LocalFilesManagerService _service;
class LocalFilesManagerRepository implements ILocalFilesManager {
const LocalFilesManagerRepository();
@override
Future<bool> moveToTrash(List<String> mediaUrls) async {
return await _service.moveToTrash(mediaUrls);
return await LocalFilesManager.moveToTrash(mediaUrls);
}
@override
Future<bool> restoreFromTrash(String fileName, int type) async {
return await _service.restoreFromTrash(fileName, type);
return await LocalFilesManager.restoreFromTrash(fileName, type);
}
@override
Future<bool> requestManageMediaPermission() async {
return await _service.requestManageMediaPermission();
return await LocalFilesManager.requestManageMediaPermission();
}
}

View File

@@ -1,12 +1,15 @@
import 'package:background_downloader/background_downloader.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/interfaces/upload.interface.dart';
import 'package:immich_mobile/utils/upload.dart';
final uploadRepositoryProvider = Provider((ref) => UploadRepository());
class UploadRepository {
class UploadRepository implements IUploadRepository {
@override
void Function(TaskStatusUpdate)? onUploadStatus;
@override
void Function(TaskProgressUpdate)? onTaskProgress;
UploadRepository() {
@@ -17,18 +20,22 @@ class UploadRepository {
);
}
@override
Future<bool> upload(UploadTask task) {
return FileDownloader().enqueue(task);
}
@override
Future<void> deleteAllTrackingRecords() {
return FileDownloader().database.deleteAllRecords();
}
@override
Future<bool> cancel(String id) {
return FileDownloader().cancelTaskWithId(id);
}
@override
Future<void> deleteRecordsWithIds(List<String> ids) {
return FileDownloader().database.deleteRecordsWithIds(ids);
}

View File

@@ -1,19 +1,23 @@
import 'package:home_widget/home_widget.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/interfaces/widget.interface.dart';
final widgetRepositoryProvider = Provider((_) => WidgetRepository());
class WidgetRepository {
class WidgetRepository implements IWidgetRepository {
WidgetRepository();
@override
Future<void> saveData(String key, String value) async {
await HomeWidget.saveWidgetData<String>(key, value);
}
@override
Future<void> refresh(String name) async {
await HomeWidget.updateWidget(name: name, iOSName: name);
}
@override
Future<void> setAppGroupId(String appGroupId) async {
await HomeWidget.setAppGroupId(appGroupId);
}

View File

@@ -13,6 +13,9 @@ import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/backup_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
as entity;
import 'package:immich_mobile/interfaces/album.interface.dart';
import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
import 'package:immich_mobile/models/albums/album_add_asset_response.model.dart';
import 'package:immich_mobile/models/albums/album_search.model.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
@@ -43,9 +46,9 @@ class AlbumService {
final SyncService _syncService;
final UserService _userService;
final EntityService _entityService;
final AlbumRepository _albumRepository;
final AssetRepository _assetRepository;
final BackupAlbumRepository _backupAlbumRepository;
final IAlbumRepository _albumRepository;
final IAssetRepository _assetRepository;
final IBackupAlbumRepository _backupAlbumRepository;
final AlbumMediaRepository _albumMediaRepository;
final AlbumApiRepository _albumApiRepository;
final Logger _log = Logger('AlbumService');

View File

@@ -4,12 +4,17 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/domain/services/user.service.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/backup_album.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
import 'package:immich_mobile/interfaces/etag.interface.dart';
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';
@@ -45,18 +50,18 @@ final assetServiceProvider = Provider(
);
class AssetService {
final AssetApiRepository _assetApiRepository;
final AssetRepository _assetRepository;
final IsarExifRepository _exifInfoRepository;
final IAssetApiRepository _assetApiRepository;
final IAssetRepository _assetRepository;
final IExifInfoRepository _exifInfoRepository;
final IsarUserRepository _isarUserRepository;
final ETagRepository _etagRepository;
final BackupAlbumRepository _backupRepository;
final IETagRepository _etagRepository;
final IBackupAlbumRepository _backupRepository;
final ApiService _apiService;
final SyncService _syncService;
final BackupService _backupService;
final AlbumService _albumService;
final UserService _userService;
final AssetMediaRepository _assetMediaRepository;
final IAssetMediaRepository _assetMediaRepository;
final log = Logger('AssetService');
AssetService(

View File

@@ -5,6 +5,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/utils/background_sync.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/interfaces/auth.interface.dart';
import 'package:immich_mobile/interfaces/auth_api.interface.dart';
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
import 'package:immich_mobile/models/auth/login_response.model.dart';
import 'package:immich_mobile/providers/api.provider.dart';
@@ -27,8 +29,8 @@ final authServiceProvider = Provider(
);
class AuthService {
final AuthApiRepository _authApiRepository;
final AuthRepository _authRepository;
final IAuthApiRepository _authApiRepository;
final IAuthRepository _authRepository;
final ApiService _apiService;
final NetworkService _networkService;
final BackgroundSyncManager _backgroundSyncManager;

View File

@@ -14,6 +14,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/backup_album.entity.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
import 'package:immich_mobile/models/backup/current_upload_asset.model.dart';
import 'package:immich_mobile/models/backup/error_upload_asset.model.dart';

View File

@@ -11,6 +11,9 @@ import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/backup_album.entity.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
import 'package:immich_mobile/interfaces/file_media.interface.dart';
import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
import 'package:immich_mobile/models/backup/current_upload_asset.model.dart';
import 'package:immich_mobile/models/backup/error_upload_asset.model.dart';
@@ -49,9 +52,9 @@ class BackupService {
final AppSettingsService _appSetting;
final AlbumService _albumService;
final AlbumMediaRepository _albumMediaRepository;
final FileMediaRepository _fileMediaRepository;
final AssetRepository _assetRepository;
final AssetMediaRepository _assetMediaRepository;
final IFileMediaRepository _fileMediaRepository;
final IAssetRepository _assetRepository;
final IAssetMediaRepository _assetMediaRepository;
BackupService(
this._apiService,

View File

@@ -1,5 +1,6 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/backup_album.entity.dart';
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
import 'package:immich_mobile/repositories/backup.repository.dart';
final backupAlbumServiceProvider = Provider<BackupAlbumService>((ref) {
@@ -7,7 +8,7 @@ final backupAlbumServiceProvider = Provider<BackupAlbumService>((ref) {
});
class BackupAlbumService {
final BackupAlbumRepository _backupAlbumRepository;
final IBackupAlbumRepository _backupAlbumRepository;
BackupAlbumService(this._backupAlbumRepository);

View File

@@ -5,13 +5,15 @@ import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
import 'package:immich_mobile/domain/models/exif.model.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/infrastructure/repositories/exif.repository.dart';
import 'package:immich_mobile/infrastructure/utils/exif.converter.dart';
import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/interfaces/file_media.interface.dart';
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
import 'package:immich_mobile/repositories/asset.repository.dart';
@@ -23,9 +25,9 @@ import 'package:immich_mobile/utils/diff.dart';
/// Finds duplicates originating from missing EXIF information
class BackupVerificationService {
final UserService _userService;
final FileMediaRepository _fileMediaRepository;
final AssetRepository _assetRepository;
final IsarExifRepository _exifInfoRepository;
final IFileMediaRepository _fileMediaRepository;
final IAssetRepository _assetRepository;
final IExifInfoRepository _exifInfoRepository;
const BackupVerificationService(
this._userService,
@@ -121,7 +123,7 @@ class BackupVerificationService {
String auth,
String endpoint,
RootIsolateToken rootIsolateToken,
FileMediaRepository fileMediaRepository,
IFileMediaRepository fileMediaRepository,
}) tuple,
) async {
assert(tuple.deleteCandidates.length == tuple.originals.length);

View File

@@ -6,6 +6,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/interfaces/file_media.interface.dart';
import 'package:immich_mobile/models/download/livephotos_medatada.model.dart';
import 'package:immich_mobile/repositories/download.repository.dart';
import 'package:immich_mobile/repositories/file_media.repository.dart';
@@ -22,7 +23,7 @@ final downloadServiceProvider = Provider(
class DownloadService {
final DownloadRepository _downloadRepository;
final FileMediaRepository _fileMediaRepository;
final IFileMediaRepository _fileMediaRepository;
final Logger _log = Logger("DownloadService");
void Function(TaskStatusUpdate)? onImageDownloadStatus;
void Function(TaskStatusUpdate)? onVideoDownloadStatus;

View File

@@ -2,11 +2,12 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
import 'package:immich_mobile/repositories/asset.repository.dart';
class EntityService {
final AssetRepository _assetRepository;
final IAssetRepository _assetRepository;
final IsarUserRepository _isarUserRepository;
EntityService(
this._assetRepository,

View File

@@ -1,11 +1,12 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/interfaces/etag.interface.dart';
import 'package:immich_mobile/repositories/etag.repository.dart';
final etagServiceProvider =
Provider((ref) => ETagService(ref.watch(etagRepositoryProvider)));
class ETagService {
final ETagRepository _eTagRepository;
final IETagRepository _eTagRepository;
ETagService(this._eTagRepository);

View File

@@ -1,12 +1,12 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart';
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
final exifServiceProvider =
Provider((ref) => ExifService(ref.watch(exifRepositoryProvider)));
class ExifService {
final IsarExifRepository _exifInfoRepository;
final IExifInfoRepository _exifInfoRepository;
const ExifService(this._exifInfoRepository);

View File

@@ -3,6 +3,7 @@ import 'dart:async';
import 'package:cast/session.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/interfaces/cast_destination_service.interface.dart';
import 'package:immich_mobile/models/cast/cast_manager_state.dart';
import 'package:immich_mobile/models/sessions/session_create_response.model.dart';
import 'package:immich_mobile/repositories/asset_api.repository.dart';
@@ -20,7 +21,7 @@ final gCastServiceProvider = Provider(
),
);
class GCastService {
class GCastService implements ICastDestinationService {
final GCastRepository _gCastRepository;
final SessionsAPIRepository _sessionsApiService;
final AssetApiRepository _assetApiRepository;
@@ -31,14 +32,15 @@ class GCastService {
int? _sessionId;
Timer? _mediaStatusPollingTimer;
@override
void Function(bool)? onConnectionState;
@override
void Function(Duration)? onCurrentTime;
@override
void Function(Duration)? onDuration;
@override
void Function(String)? onReceiverName;
@override
void Function(CastState)? onCastState;
GCastService(
@@ -118,21 +120,25 @@ class GCastService {
}
}
@override
Future<void> connect(dynamic device) async {
await _gCastRepository.connect(device);
onReceiverName?.call(device.extras["fn"] ?? "Google Cast");
}
@override
CastDestinationType getType() {
return CastDestinationType.googleCast;
}
@override
Future<bool> initialize() async {
// there is nothing blocking us from using Google Cast that we can check for
return true;
}
@override
Future<void> disconnect() async {
onReceiverName?.call("");
currentAssetId = null;
@@ -156,6 +162,7 @@ class GCastService {
return bufferedExpiration.isAfter(DateTime.now());
}
@override
void loadMedia(Asset asset, bool reload) async {
if (!isConnected) {
return;
@@ -227,6 +234,7 @@ class GCastService {
}
}
@override
void play() {
_gCastRepository.sendMessage(CastSession.kNamespaceMedia, {
"type": "PLAY",
@@ -234,6 +242,7 @@ class GCastService {
});
}
@override
void pause() {
_gCastRepository.sendMessage(CastSession.kNamespaceMedia, {
"type": "PAUSE",
@@ -241,6 +250,7 @@ class GCastService {
});
}
@override
void seekTo(Duration position) {
_gCastRepository.sendMessage(CastSession.kNamespaceMedia, {
"type": "SEEK",
@@ -249,6 +259,7 @@ class GCastService {
});
}
@override
void stop() {
_gCastRepository.sendMessage(CastSession.kNamespaceMedia, {
"type": "STOP",
@@ -262,6 +273,7 @@ class GCastService {
// 0x01 is display capability bitmask
bool isDisplay(int ca) => (ca & 0x01) != 0;
@override
Future<List<(String, CastDestinationType, dynamic)>> getDevices() async {
final dests = await _gCastRepository.listDestinations();

View File

@@ -4,23 +4,23 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/interfaces/device_asset.interface.dart';
import 'package:immich_mobile/domain/models/device_asset.model.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/device_asset.repository.dart';
import 'package:immich_mobile/providers/infrastructure/device_asset.provider.dart';
import 'package:immich_mobile/services/background.service.dart';
import 'package:logging/logging.dart';
class HashService {
HashService({
required IsarDeviceAssetRepository deviceAssetRepository,
required IDeviceAssetRepository deviceAssetRepository,
required BackgroundService backgroundService,
this.batchSizeLimit = kBatchHashSizeLimit,
this.batchFileLimit = kBatchHashFileLimit,
}) : _deviceAssetRepository = deviceAssetRepository,
_backgroundService = backgroundService;
final IsarDeviceAssetRepository _deviceAssetRepository;
final IDeviceAssetRepository _deviceAssetRepository;
final BackgroundService _backgroundService;
final int batchSizeLimit;
final int batchFileLimit;

View File

@@ -1,5 +1,6 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/models/memories/memory.model.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/repositories/asset.repository.dart';
@@ -17,7 +18,7 @@ class MemoryService {
final log = Logger("MemoryService");
final ApiService _apiService;
final AssetRepository _assetRepository;
final IAssetRepository _assetRepository;
MemoryService(this._apiService, this._assetRepository);

View File

@@ -1,6 +1,8 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/person.model.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
import 'package:immich_mobile/repositories/asset.repository.dart';
import 'package:immich_mobile/repositories/asset_api.repository.dart';
import 'package:immich_mobile/repositories/person_api.repository.dart';
@@ -19,8 +21,8 @@ PersonService personService(Ref ref) => PersonService(
class PersonService {
final Logger _log = Logger("PersonService");
final PersonApiRepository _personApiRepository;
final AssetApiRepository _assetApiRepository;
final AssetRepository _assetRepository;
final IAssetApiRepository _assetApiRepository;
final IAssetRepository _assetRepository;
PersonService(
this._personApiRepository,

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/string_extensions.dart';
import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/models/search/search_filter.model.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/models/search/search_result.model.dart';
@@ -19,7 +20,7 @@ final searchServiceProvider = Provider(
class SearchService {
final ApiService _apiService;
final AssetRepository _assetRepository;
final IAssetRepository _assetRepository;
final _log = Logger("SearchService");
SearchService(this._apiService, this._assetRepository);

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/repositories/asset.repository.dart';
import 'package:immich_mobile/services/api.service.dart';
@@ -10,7 +11,7 @@ class StackService {
StackService(this._api, this._assetRepository);
final ApiService _api;
final AssetRepository _assetRepository;
final IAssetRepository _assetRepository;
Future<StackResponseDto?> getStack(String stackId) async {
try {

View File

@@ -4,15 +4,19 @@ import 'dart:io';
import 'package:collection/collection.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/domain/services/user.service.dart';
import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/etag.entity.dart';
import 'package:immich_mobile/extensions/collection_extensions.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/album.interface.dart';
import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/interfaces/etag.interface.dart';
import 'package:immich_mobile/interfaces/local_files_manager.interface.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
@@ -58,19 +62,19 @@ class SyncService {
final EntityService _entityService;
final AlbumMediaRepository _albumMediaRepository;
final AlbumApiRepository _albumApiRepository;
final AlbumRepository _albumRepository;
final AssetRepository _assetRepository;
final IsarExifRepository _exifInfoRepository;
final IAlbumRepository _albumRepository;
final IAssetRepository _assetRepository;
final IExifInfoRepository _exifInfoRepository;
final IsarUserRepository _isarUserRepository;
final UserService _userService;
final PartnerRepository _partnerRepository;
final ETagRepository _eTagRepository;
final IETagRepository _eTagRepository;
final PartnerApiRepository _partnerApiRepository;
final UserApiRepository _userApiRepository;
final AsyncMutex _lock = AsyncMutex();
final Logger _log = Logger('SyncService');
final AppSettingsService _appSettingsService;
final LocalFilesManagerRepository _localFilesManager;
final ILocalFilesManager _localFilesManager;
SyncService(
this._hashService,

View File

@@ -1,6 +1,7 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/services/user.service.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
import 'package:immich_mobile/repositories/asset.repository.dart';
@@ -17,7 +18,7 @@ final trashServiceProvider = Provider<TrashService>((ref) {
class TrashService {
final ApiService _apiService;
final AssetRepository _assetRepository;
final IAssetRepository _assetRepository;
final UserService _userService;
const TrashService(

View File

@@ -4,6 +4,7 @@ import 'package:background_downloader/background_downloader.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/interfaces/upload.interface.dart';
import 'package:immich_mobile/repositories/upload.repository.dart';
import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/utils/upload.dart';
@@ -17,7 +18,7 @@ final uploadServiceProvider = Provider(
);
class UploadService {
final UploadRepository _uploadRepository;
final IUploadRepository _uploadRepository;
// final Logger _log = Logger("UploadService");
void Function(TaskStatusUpdate)? onUploadStatus;
void Function(TaskProgressUpdate)? onTaskProgress;

Some files were not shown because too many files have changed in this diff Show More