chore: bump line length to 120 (#20191)

This commit is contained in:
shenlong
2025-07-25 08:07:22 +05:30
committed by GitHub
parent 977c9b96ba
commit ad65e9011a
517 changed files with 4520 additions and 9514 deletions

View File

@@ -192,8 +192,7 @@ class ActionService {
final result = await _albumApiRepository.removeAssets(albumId, remoteIds);
if (result.removed.isNotEmpty) {
removedCount =
await _remoteAlbumRepository.removeAssets(albumId, result.removed);
removedCount = await _remoteAlbumRepository.removeAssets(albumId, result.removed);
}
return removedCount;

View File

@@ -11,8 +11,7 @@ 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/backup_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
as entity;
import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity;
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';
@@ -76,12 +75,8 @@ class AlbumService {
bool changes = false;
try {
final (selectedIds, excludedIds, onDevice) = await (
_backupAlbumRepository
.getIdsBySelection(BackupSelection.select)
.then((value) => value.toSet()),
_backupAlbumRepository
.getIdsBySelection(BackupSelection.exclude)
.then((value) => value.toSet()),
_backupAlbumRepository.getIdsBySelection(BackupSelection.select).then((value) => value.toSet()),
_backupAlbumRepository.getIdsBySelection(BackupSelection.exclude).then((value) => value.toSet()),
_albumMediaRepository.getAll()
).wait;
_log.info("Found ${onDevice.length} device albums");
@@ -126,8 +121,7 @@ class AlbumService {
onDevice.removeWhere((album) => !selectedIds.contains(album.localId));
_log.info("'Recents' is not selected, keeping only selected albums");
}
changes =
await _syncService.syncLocalAlbumAssetsToDb(onDevice, excludedAssets);
changes = await _syncService.syncLocalAlbumAssetsToDb(onDevice, excludedAssets);
_log.info("Syncing completed. Changes: $changes");
} finally {
_localCompleter.complete(changes);
@@ -141,14 +135,10 @@ class AlbumService {
Set<String> excludedAlbumIds,
) async {
final Set<String> result = HashSet<String>();
for (final batchAlbums in albums
.where((album) => excludedAlbumIds.contains(album.localId))
.slices(5)) {
for (final batchAlbums in albums.where((album) => excludedAlbumIds.contains(album.localId)).slices(5)) {
await batchAlbums
.map(
(album) => _albumMediaRepository
.getAssetIds(album.localId!)
.then((assetIds) => result.addAll(assetIds)),
(album) => _albumMediaRepository.getAssetIds(album.localId!).then((assetIds) => result.addAll(assetIds)),
)
.wait;
}
@@ -244,9 +234,8 @@ class AlbumService {
assets.map((asset) => asset.remoteId!),
);
final List<Asset> addedAssets = result.added
.map((id) => assets.firstWhere((asset) => asset.remoteId == id))
.toList();
final List<Asset> addedAssets =
result.added.map((id) => assets.firstWhere((asset) => asset.remoteId == id)).toList();
await _updateAssets(album.id, add: addedAssets);
@@ -296,8 +285,7 @@ class AlbumService {
await _albumApiRepository.delete(album.remoteId!);
}
if (album.shared) {
final foreignAssets =
await _assetRepository.getByAlbum(album, notOwnedBy: [userId]);
final foreignAssets = await _assetRepository.getByAlbum(album, notOwnedBy: [userId]);
await _albumRepository.delete(album.id);
final List<Album> albums = await _albumRepository.getAll(shared: true);
@@ -307,8 +295,7 @@ class AlbumService {
await _assetRepository.getByAlbum(album, notOwnedBy: [userId]),
);
}
final List<int> idsToRemove =
_syncService.sharedAssetsToRemove(foreignAssets, existing);
final List<int> idsToRemove = _syncService.sharedAssetsToRemove(foreignAssets, existing);
if (idsToRemove.isNotEmpty) {
await _assetRepository.deleteByIds(idsToRemove);
}
@@ -341,8 +328,7 @@ class AlbumService {
album.remoteId!,
assets.map((asset) => asset.remoteId!),
);
final toRemove = result.removed
.map((id) => assets.firstWhere((asset) => asset.remoteId == id));
final toRemove = result.removed.map((id) => assets.firstWhere((asset) => asset.remoteId == id));
await _updateAssets(album.id, remove: toRemove.toList());
return true;
} catch (e) {
@@ -379,8 +365,7 @@ class AlbumService {
List<String> userIds,
) async {
try {
final updatedAlbum =
await _albumApiRepository.addUsers(album.remoteId!, userIds);
final updatedAlbum = await _albumApiRepository.addUsers(album.remoteId!, userIds);
album.sharedUsers.addAll(updatedAlbum.remoteUsers);
album.shared = true;
@@ -503,8 +488,7 @@ class AlbumService {
Future<Album?> updateSortOrder(Album album, SortOrder order) async {
try {
final updateAlbum =
await _albumApiRepository.update(album.remoteId!, sortOrder: order);
final updateAlbum = await _albumApiRepository.update(album.remoteId!, sortOrder: order);
album.sortOrder = updateAlbum.sortOrder;
return _albumRepository.update(album);

View File

@@ -178,13 +178,11 @@ class ApiService implements Authentication {
if (Platform.isIOS) {
final iosInfo = await deviceInfoPlugin.iosInfo;
authenticationApi.apiClient
.addDefaultHeader('deviceModel', iosInfo.utsname.machine);
authenticationApi.apiClient.addDefaultHeader('deviceModel', iosInfo.utsname.machine);
authenticationApi.apiClient.addDefaultHeader('deviceType', 'iOS');
} else if (Platform.isAndroid) {
final androidInfo = await deviceInfoPlugin.androidInfo;
authenticationApi.apiClient
.addDefaultHeader('deviceModel', androidInfo.model);
authenticationApi.apiClient.addDefaultHeader('deviceModel', androidInfo.model);
authenticationApi.apiClient.addDefaultHeader('deviceType', 'Android');
} else {
authenticationApi.apiClient.addDefaultHeader('deviceModel', 'Unknown');

View File

@@ -78,11 +78,8 @@ class AssetService {
/// required. Returns `true` if there were any changes.
Future<bool> refreshRemoteAssets() async {
final syncedUserIds = await _etagRepository.getAllIds();
final List<UserDto> syncedUsers = syncedUserIds.isEmpty
? []
: (await _isarUserRepository.getByUserIds(syncedUserIds))
.nonNulls
.toList();
final List<UserDto> syncedUsers =
syncedUserIds.isEmpty ? [] : (await _isarUserRepository.getByUserIds(syncedUserIds)).nonNulls.toList();
final Stopwatch sw = Stopwatch()..start();
final bool changes = await _syncService.syncRemoteAssetsToDb(
users: syncedUsers,
@@ -94,8 +91,10 @@ class AssetService {
}
/// Returns `(null, null)` if changes are invalid -> requires full sync
Future<(List<Asset>? toUpsert, List<String>? toDelete)>
_getRemoteAssetChanges(List<UserDto> users, DateTime since) async {
Future<(List<Asset>? toUpsert, List<String>? toDelete)> _getRemoteAssetChanges(
List<UserDto> users,
DateTime since,
) async {
final dto = AssetDeltaSyncDto(
updatedAfter: since,
userIds: users.map((e) => e.id).toList(),
@@ -112,8 +111,7 @@ class AssetService {
String remoteId,
) async {
try {
final AssetResponseDto? dto =
await _apiService.assetsApi.getAssetInfo(remoteId);
final AssetResponseDto? dto = await _apiService.assetsApi.getAssetInfo(remoteId);
return dto?.people;
} catch (error, stack) {
@@ -142,8 +140,7 @@ class AssetService {
userId: user.id,
);
log.fine("Requesting $chunkSize assets from $lastId");
final List<AssetResponseDto>? assets =
await _apiService.syncApi.getFullSyncForUser(dto);
final List<AssetResponseDto>? assets = await _apiService.syncApi.getFullSyncForUser(dto);
if (assets == null) return null;
log.fine(
"Received ${assets.length} assets from ${assets.firstOrNull?.id} to ${assets.lastOrNull?.id}",
@@ -172,8 +169,7 @@ class AssetService {
a.exifInfo = newExif;
if (newExif != a.exifInfo) {
if (a.isInDb) {
await _assetRepository
.transaction(() => _assetRepository.update(a));
await _assetRepository.transaction(() => _assetRepository.update(a));
} else {
debugPrint("[loadExif] parameter Asset is not from DB!");
}
@@ -230,16 +226,13 @@ class AssetService {
await updateAssets(
assets,
UpdateAssetDto(
visibility:
isArchived ? AssetVisibility.archive : AssetVisibility.timeline,
visibility: isArchived ? AssetVisibility.archive : AssetVisibility.timeline,
),
);
for (var element in assets) {
element.isArchived = isArchived;
element.visibility = isArchived
? AssetVisibilityEnum.archive
: AssetVisibilityEnum.timeline;
element.visibility = isArchived ? AssetVisibilityEnum.archive : AssetVisibilityEnum.timeline;
}
await _syncService.upsertAssetsWithExif(assets);
@@ -263,8 +256,7 @@ class AssetService {
for (var element in assets) {
element.fileCreatedAt = DateTime.parse(updatedDt);
element.exifInfo = element.exifInfo
?.copyWith(dateTimeOriginal: DateTime.parse(updatedDt));
element.exifInfo = element.exifInfo?.copyWith(dateTimeOriginal: DateTime.parse(updatedDt));
}
await _syncService.upsertAssetsWithExif(assets);
@@ -307,10 +299,8 @@ class AssetService {
Future<void> syncUploadedAssetToAlbums() async {
try {
final selectedAlbums =
await _backupRepository.getAllBySelection(BackupSelection.select);
final excludedAlbums =
await _backupRepository.getAllBySelection(BackupSelection.exclude);
final selectedAlbums = await _backupRepository.getAllBySelection(BackupSelection.select);
final excludedAlbums = await _backupRepository.getAllBySelection(BackupSelection.exclude);
final candidates = await _backupService.buildUploadCandidates(
selectedAlbums,
@@ -375,8 +365,7 @@ class AssetService {
var exifInfo = await _exifInfoRepository.get(localExifId);
if (exifInfo != null) {
await _exifInfoRepository
.update(exifInfo.copyWith(description: description));
await _exifInfoRepository.update(exifInfo.copyWith(description: description));
}
}
}
@@ -429,22 +418,16 @@ class AssetService {
// Delete files from local gallery
final candidates = assets.where((asset) => asset.isLocal);
final deletedIds = await _assetMediaRepository
.deleteAll(candidates.map((asset) => asset.localId!).toList());
final deletedIds = await _assetMediaRepository.deleteAll(candidates.map((asset) => asset.localId!).toList());
// Modify local database by removing the reference to the local assets
if (deletedIds.isNotEmpty) {
// Delete records from local database
final isarIds = assets
.where((asset) => asset.storage == AssetState.local)
.map((asset) => asset.id)
.toList();
final isarIds = assets.where((asset) => asset.storage == AssetState.local).map((asset) => asset.id).toList();
await _assetRepository.deleteByIds(isarIds);
// Modify Merged asset to be remote only
final updatedAssets = assets
.where((asset) => asset.storage == AssetState.merged)
.map((asset) {
final updatedAssets = assets.where((asset) => asset.storage == AssetState.merged).map((asset) {
asset.localId = null;
return asset;
}).toList();
@@ -473,9 +456,7 @@ class AssetService {
/// Update asset info bassed on the deletion type.
final payload = shouldDeletePermanently
? assets
.where((asset) => asset.storage == AssetState.merged)
.map((asset) {
? assets.where((asset) => asset.storage == AssetState.merged).map((asset) {
asset.remoteId = null;
asset.visibility = AssetVisibilityEnum.timeline;
return asset;
@@ -489,10 +470,8 @@ class AssetService {
await _assetRepository.updateAll(payload.toList());
if (shouldDeletePermanently) {
final remoteAssetIds = assets
.where((asset) => asset.storage == AssetState.remote)
.map((asset) => asset.id)
.toList();
final remoteAssetIds =
assets.where((asset) => asset.storage == AssetState.remote).map((asset) => asset.id).toList();
await _assetRepository.deleteByIds(remoteAssetIds);
}
});

View File

@@ -39,10 +39,8 @@ final backgroundServiceProvider = Provider((ref) => BackgroundService());
/// Background backup service
class BackgroundService {
static const String _portNameLock = "immichLock";
static const MethodChannel _foregroundChannel =
MethodChannel('immich/foregroundChannel');
static const MethodChannel _backgroundChannel =
MethodChannel('immich/backgroundChannel');
static const MethodChannel _foregroundChannel = MethodChannel('immich/foregroundChannel');
static const MethodChannel _backgroundChannel = MethodChannel('immich/backgroundChannel');
static const notifyInterval = Duration(milliseconds: 400);
bool _isBackgroundInitialized = false;
CancellationToken? _cancellationToken;
@@ -56,8 +54,7 @@ class BackgroundService {
int _assetsToUploadCount = 0;
String _lastPrintedDetailContent = "";
String? _lastPrintedDetailTitle;
late final ThrottleProgressUpdate _throttledNotifiy =
ThrottleProgressUpdate(_updateProgress, notifyInterval);
late final ThrottleProgressUpdate _throttledNotifiy = ThrottleProgressUpdate(_updateProgress, notifyInterval);
late final ThrottleProgressUpdate _throttledDetailNotify =
ThrottleProgressUpdate(_updateDetailProgress, notifyInterval);
@@ -74,10 +71,8 @@ class BackgroundService {
Future<bool> enableService({bool immediate = false}) async {
try {
final callback = PluginUtilities.getCallbackHandle(_nativeEntry)!;
final String title =
"backup_background_service_default_notification".tr();
final bool ok = await _foregroundChannel
.invokeMethod('enable', [callback.toRawHandle(), title, immediate]);
final String title = "backup_background_service_default_notification".tr();
final bool ok = await _foregroundChannel.invokeMethod('enable', [callback.toRawHandle(), title, immediate]);
return ok;
} catch (error) {
return false;
@@ -133,8 +128,7 @@ class BackgroundService {
return true;
}
try {
return await _foregroundChannel
.invokeMethod('isIgnoringBatteryOptimizations');
return await _foregroundChannel.invokeMethod('isIgnoringBatteryOptimizations');
} catch (error) {
return false;
}
@@ -183,8 +177,7 @@ class BackgroundService {
}) async {
try {
if (_isBackgroundInitialized && _errorGracePeriodExceeded) {
return await _backgroundChannel
.invokeMethod('showError', [title, content, individualTag]);
return await _backgroundChannel.invokeMethod('showError', [title, content, individualTag]);
}
} catch (error) {
debugPrint("[_showErrorNotification] failed to communicate with plugin");
@@ -240,8 +233,7 @@ class BackgroundService {
final bs = tempRp.asBroadcastStream();
while (_wantsLockTime == lockTime) {
other.send(tempSp);
final dynamic answer = await bs.first
.timeout(const Duration(seconds: 3), onTimeout: () => null);
final dynamic answer = await bs.first.timeout(const Duration(seconds: 3), onTimeout: () => null);
if (_wantsLockTime != lockTime) {
break;
}
@@ -257,8 +249,7 @@ class BackgroundService {
} else if (answer == false) {
// other isolate is still active
}
final dynamic isFinished = await bs.first
.timeout(const Duration(seconds: 3), onTimeout: () => false);
final dynamic isFinished = await bs.first.timeout(const Duration(seconds: 3), onTimeout: () => false);
if (isFinished == true) {
break;
}
@@ -358,9 +349,7 @@ class BackgroundService {
);
HttpSSLOptions.apply();
ref
.read(apiServiceProvider)
.setAccessToken(Store.get(StoreKey.accessToken));
ref.read(apiServiceProvider).setAccessToken(Store.get(StoreKey.accessToken));
await ref.read(authServiceProvider).setOpenApiServiceEndpoint();
if (kDebugMode) {
debugPrint(
@@ -368,12 +357,8 @@ class BackgroundService {
);
}
final selectedAlbums = await ref
.read(backupAlbumRepositoryProvider)
.getAllBySelection(BackupSelection.select);
final excludedAlbums = await ref
.read(backupAlbumRepositoryProvider)
.getAllBySelection(BackupSelection.exclude);
final selectedAlbums = await ref.read(backupAlbumRepositoryProvider).getAllBySelection(BackupSelection.select);
final excludedAlbums = await ref.read(backupAlbumRepositoryProvider).getAllBySelection(BackupSelection.exclude);
if (selectedAlbums.isEmpty) {
return true;
}
@@ -392,9 +377,7 @@ class BackgroundService {
final backupAlbums = [...selectedAlbums, ...excludedAlbums];
backupAlbums.sortBy((e) => e.id);
final dbAlbums = await ref
.read(backupAlbumRepositoryProvider)
.getAll(sort: BackupAlbumSort.id);
final dbAlbums = await ref.read(backupAlbumRepositoryProvider).getAll(sort: BackupAlbumSort.id);
final List<int> toDelete = [];
final List<BackupAlbum> toUpsert = [];
// stores the most recent `lastBackup` per album but always keeps the `selection` from the most recent DB state
@@ -403,9 +386,7 @@ class BackgroundService {
backupAlbums,
compare: (BackupAlbum a, BackupAlbum b) => a.id.compareTo(b.id),
both: (BackupAlbum a, BackupAlbum b) {
a.lastBackup = a.lastBackup.isAfter(b.lastBackup)
? a.lastBackup
: b.lastBackup;
a.lastBackup = a.lastBackup.isAfter(b.lastBackup) ? a.lastBackup : b.lastBackup;
toUpsert.add(a);
return true;
},
@@ -419,9 +400,7 @@ class BackgroundService {
return false;
}
// Android should check for new assets added while performing backup
} while (Platform.isAndroid &&
true ==
await _backgroundChannel.invokeMethod<bool>("hasContentChanged"));
} while (Platform.isAndroid && true == await _backgroundChannel.invokeMethod<bool>("hasContentChanged"));
return true;
}
@@ -432,10 +411,8 @@ class BackgroundService {
List<BackupAlbum> excludedAlbums,
) async {
_errorGracePeriodExceeded = _isErrorGracePeriodExceeded(settingsService);
final bool notifyTotalProgress = settingsService
.getSetting<bool>(AppSettingsEnum.backgroundBackupTotalProgress);
final bool notifySingleProgress = settingsService
.getSetting<bool>(AppSettingsEnum.backgroundBackupSingleProgress);
final bool notifyTotalProgress = settingsService.getSetting<bool>(AppSettingsEnum.backgroundBackupTotalProgress);
final bool notifySingleProgress = settingsService.getSetting<bool>(AppSettingsEnum.backgroundBackupSingleProgress);
if (_canceledBySystem) {
return false;
@@ -489,10 +466,8 @@ class BackgroundService {
onSuccess: (result) => _onAssetUploaded(
shouldNotify: notifyTotalProgress,
),
onProgress: (bytes, totalBytes) =>
_onProgress(bytes, totalBytes, shouldNotify: notifySingleProgress),
onCurrentAsset: (asset) =>
_onSetCurrentBackupAsset(asset, shouldNotify: notifySingleProgress),
onProgress: (bytes, totalBytes) => _onProgress(bytes, totalBytes, shouldNotify: notifySingleProgress),
onCurrentAsset: (asset) => _onSetCurrentBackupAsset(asset, shouldNotify: notifySingleProgress),
onError: _onBackupError,
isBackground: true,
);
@@ -527,8 +502,7 @@ class BackgroundService {
}
void _updateDetailProgress(String? title, int progress, int total) {
final String msg =
total > 0 ? humanReadableBytesProgress(progress, total) : "";
final String msg = total > 0 ? humanReadableBytesProgress(progress, total) : "";
// only update if message actually differs (to stop many useless notification updates on large assets or slow connections)
if (msg != _lastPrintedDetailContent || _lastPrintedDetailTitle != title) {
_lastPrintedDetailContent = msg;
@@ -557,8 +531,8 @@ class BackgroundService {
void _onBackupError(ErrorUploadAsset errorAssetInfo) {
_showErrorNotification(
title: "backup_background_service_upload_failure_notification"
.tr(namedArgs: {'filename': errorAssetInfo.fileName}),
title:
"backup_background_service_upload_failure_notification".tr(namedArgs: {'filename': errorAssetInfo.fileName}),
individualTag: errorAssetInfo.id,
);
}
@@ -571,16 +545,14 @@ class BackgroundService {
return;
}
_throttledDetailNotify.title =
"backup_background_service_current_upload_notification"
.tr(namedArgs: {'filename': currentUploadAsset.fileName});
_throttledDetailNotify.title = "backup_background_service_current_upload_notification"
.tr(namedArgs: {'filename': currentUploadAsset.fileName});
_throttledDetailNotify.progress = 0;
_throttledDetailNotify.total = 0;
}
bool _isErrorGracePeriodExceeded(AppSettingsService appSettingsService) {
final int value = appSettingsService
.getSetting(AppSettingsEnum.uploadErrorNotificationGracePeriod);
final int value = appSettingsService.getSetting(AppSettingsEnum.uploadErrorNotificationGracePeriod);
if (value == 0) {
return true;
} else if (value == 5) {

View File

@@ -74,8 +74,7 @@ class BackupService {
}
}
Future<void> _saveDuplicatedAssetIds(List<String> deviceAssetIds) =>
_assetRepository.transaction(
Future<void> _saveDuplicatedAssetIds(List<String> deviceAssetIds) => _assetRepository.transaction(
() => _assetRepository.upsertDuplicatedAssets(deviceAssetIds),
);
@@ -127,8 +126,7 @@ class BackupService {
continue;
}
if (useTimeFilter &&
localAlbum.modifiedAt.isBefore(backupAlbum.lastBackup)) {
if (useTimeFilter && localAlbum.modifiedAt.isBefore(backupAlbum.lastBackup)) {
continue;
}
final List<Asset> assets;
@@ -189,8 +187,7 @@ class BackupService {
final Set<String> existing = {};
try {
final String deviceId = Store.get(StoreKey.deviceId);
final CheckExistingAssetsResponseDto? duplicates =
await _apiService.assetsApi.checkExistingAssets(
final CheckExistingAssetsResponseDto? duplicates = await _apiService.assetsApi.checkExistingAssets(
CheckExistingAssetsDto(
deviceAssetIds: candidates.map((c) => c.asset.localId!).toList(),
deviceId: deviceId,
@@ -215,8 +212,7 @@ class BackupService {
}
Future<bool> _checkPermissions() async {
if (Platform.isAndroid &&
!(await pm.Permission.accessMediaLocation.status).isGranted) {
if (Platform.isAndroid && !(await pm.Permission.accessMediaLocation.status).isGranted) {
// double check that permission is granted here, to guard against
// uploading corrupt assets without EXIF information
_log.warning("Media location permission is not granted. "
@@ -255,8 +251,7 @@ class BackupService {
required void Function(CurrentUploadAsset asset) onCurrentAsset,
required void Function(ErrorUploadAsset error) onError,
}) async {
final bool isIgnoreIcloudAssets =
_appSetting.getSetting(AppSettingsEnum.ignoreIcloudAssets);
final bool isIgnoreIcloudAssets = _appSetting.getSetting(AppSettingsEnum.ignoreIcloudAssets);
final shouldSyncAlbums = _appSetting.getSetting(AppSettingsEnum.syncAlbums);
final String deviceId = Store.get(StoreKey.deviceId);
final String savedEndpoint = Store.get(StoreKey.serverEndpoint);
@@ -279,8 +274,7 @@ class BackupService {
File? livePhotoFile;
try {
final isAvailableLocally =
await asset.local!.isLocallyAvailable(isOrigin: true);
final isAvailableLocally = await asset.local!.isLocallyAvailable(isOrigin: true);
// Handle getting files from iCloud
if (!isAvailableLocally && Platform.isIOS) {
@@ -292,17 +286,14 @@ class BackupService {
onCurrentAsset(
CurrentUploadAsset(
id: asset.localId!,
fileCreatedAt: asset.fileCreatedAt.year == 1970
? asset.fileModifiedAt
: asset.fileCreatedAt,
fileCreatedAt: asset.fileCreatedAt.year == 1970 ? asset.fileModifiedAt : asset.fileCreatedAt,
fileName: asset.fileName,
fileType: _getAssetType(asset.type),
iCloudAsset: true,
),
);
file =
await asset.local!.loadFile(progressHandler: pmProgressHandler);
file = await asset.local!.loadFile(progressHandler: pmProgressHandler);
if (asset.local!.isLivePhoto) {
livePhotoFile = await asset.local!.loadFile(
withSubtype: true,
@@ -310,18 +301,15 @@ class BackupService {
);
}
} else {
file =
await asset.local!.originFile.timeout(const Duration(seconds: 5));
file = await asset.local!.originFile.timeout(const Duration(seconds: 5));
if (asset.local!.isLivePhoto) {
livePhotoFile = await asset.local!.originFileWithSubtype
.timeout(const Duration(seconds: 5));
livePhotoFile = await asset.local!.originFileWithSubtype.timeout(const Duration(seconds: 5));
}
}
if (file != null) {
String? originalFileName =
await _assetMediaRepository.getOriginalFilename(asset.localId!);
String? originalFileName = await _assetMediaRepository.getOriginalFilename(asset.localId!);
originalFileName ??= asset.fileName;
if (asset.local!.isLivePhoto) {
@@ -349,10 +337,8 @@ class BackupService {
baseRequest.headers.addAll(ApiService.getRequestHeaders());
baseRequest.fields['deviceAssetId'] = asset.localId!;
baseRequest.fields['deviceId'] = deviceId;
baseRequest.fields['fileCreatedAt'] =
asset.fileCreatedAt.toUtc().toIso8601String();
baseRequest.fields['fileModifiedAt'] =
asset.fileModifiedAt.toUtc().toIso8601String();
baseRequest.fields['fileCreatedAt'] = asset.fileCreatedAt.toUtc().toIso8601String();
baseRequest.fields['fileModifiedAt'] = asset.fileModifiedAt.toUtc().toIso8601String();
baseRequest.fields['isFavorite'] = asset.isFavorite.toString();
baseRequest.fields['duration'] = asset.duration.toString();
baseRequest.files.add(assetRawUploadData);
@@ -360,9 +346,7 @@ class BackupService {
onCurrentAsset(
CurrentUploadAsset(
id: asset.localId!,
fileCreatedAt: asset.fileCreatedAt.year == 1970
? asset.fileModifiedAt
: asset.fileCreatedAt,
fileCreatedAt: asset.fileCreatedAt.year == 1970 ? asset.fileModifiedAt : asset.fileCreatedAt,
fileName: originalFileName,
fileType: _getAssetType(asset.type),
fileSize: file.lengthSync(),
@@ -389,8 +373,7 @@ class BackupService {
cancellationToken: cancelToken,
);
final responseBody =
jsonDecode(await response.stream.bytesToString());
final responseBody = jsonDecode(await response.stream.bytesToString());
if (![200, 201].contains(response.statusCode)) {
final error = responseBody;

View File

@@ -181,10 +181,8 @@ class BackupVerificationService {
// for images: make sure they are pixel-wise identical
// (skip first few KBs containing metadata)
final Uint64List localImage =
_fakeDecodeImg(await file.readAsBytes());
final res = await apiService.assetsApi
.downloadAssetWithHttpInfo(remote.remoteId!);
final Uint64List localImage = _fakeDecodeImg(await file.readAsBytes());
final res = await apiService.assetsApi.downloadAssetWithHttpInfo(remote.remoteId!);
final Uint64List remoteImage = _fakeDecodeImg(res.bodyBytes);
final eq = const ListEquality().equals(remoteImage, localImage);
@@ -198,9 +196,7 @@ class BackupVerificationService {
static Uint64List _fakeDecodeImg(Uint8List bytes) {
const headerLength = 131072; // assume header is at most 128 KB
final start = bytes.length < headerLength * 2
? (bytes.length ~/ (4 * 8)) * 8
: headerLength;
final start = bytes.length < headerLength * 2 ? (bytes.length ~/ (4 * 8)) * 8 : headerLength;
return bytes.buffer.asUint64List(start);
}

View File

@@ -67,8 +67,7 @@ class DeepLinkService {
) async {
final path = link.uri.path;
const uuidRegex =
r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
const uuidRegex = r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
final assetRegex = RegExp('/photos/($uuidRegex)');
final albumRegex = RegExp('/albums/($uuidRegex)');

View File

@@ -35,8 +35,7 @@ class DownloadService {
) {
_downloadRepository.onImageDownloadStatus = _onImageDownloadCallback;
_downloadRepository.onVideoDownloadStatus = _onVideoDownloadCallback;
_downloadRepository.onLivePhotoDownloadStatus =
_onLivePhotoDownloadCallback;
_downloadRepository.onLivePhotoDownloadStatus = _onLivePhotoDownloadCallback;
_downloadRepository.onTaskProgress = _onTaskProgressCallback;
}
@@ -108,10 +107,8 @@ class DownloadService {
return false;
}
final imageRecord =
_findTaskRecord(records, livePhotosId, LivePhotosPart.image);
final videoRecord =
_findTaskRecord(records, livePhotosId, LivePhotosPart.video);
final imageRecord = _findTaskRecord(records, livePhotosId, LivePhotosPart.image);
final videoRecord = _findTaskRecord(records, livePhotosId, LivePhotosPart.video);
final imageFilePath = await imageRecord.task.filePath();
final videoFilePath = await videoRecord.task.filePath();
@@ -126,8 +123,7 @@ class DownloadService {
} on PlatformException catch (error, stack) {
// Handle saving MotionPhotos on iOS
if (error.code == 'PHPhotosErrorDomain (-1)') {
final result = await _fileMediaRepository
.saveImageWithFile(imageFilePath, title: task.filename);
final result = await _fileMediaRepository.saveImageWithFile(imageFilePath, title: task.filename);
return result != null;
}
_log.severe("Error saving live photo", error, stack);
@@ -158,8 +154,7 @@ class DownloadService {
}
Future<List<bool>> downloadAll(List<Asset> assets) async {
return await _downloadRepository
.downloadAll(assets.expand(_createDownloadTasks).toList());
return await _downloadRepository.downloadAll(assets.expand(_createDownloadTasks).toList());
}
Future<void> download(Asset asset) async {
@@ -181,9 +176,7 @@ class DownloadService {
),
_buildDownloadTask(
asset.livePhotoVideoId!,
asset.fileName
.toUpperCase()
.replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'),
asset.fileName.toUpperCase().replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'),
group: kDownloadGroupLivePhoto,
metadata: LivePhotosMetadata(
part: LivePhotosPart.video,

View File

@@ -144,8 +144,7 @@ class DriftBackupService {
}
final response = jsonDecode(update.responseBody!);
final localAsset =
await _localAssetRepository.getById(metadata.localAssetId);
final localAsset = await _localAssetRepository.getById(metadata.localAssetId);
if (localAsset == null) {
return;
}
@@ -313,6 +312,5 @@ class UploadTaskMetadata {
}
@override
int get hashCode =>
localAssetId.hashCode ^ isLivePhotos.hashCode ^ livePhotoVideoId.hashCode;
int get hashCode => localAssetId.hashCode ^ isLivePhotos.hashCode ^ livePhotoVideoId.hashCode;
}

View File

@@ -20,25 +20,21 @@ class EntityService {
final user = await _isarUserRepository.getByUserId(ownerId);
album.owner.value = user == null ? null : User.fromDto(user);
}
final thumbnailAssetId =
album.remoteThumbnailAssetId ?? album.thumbnail.value?.remoteId;
final thumbnailAssetId = album.remoteThumbnailAssetId ?? album.thumbnail.value?.remoteId;
if (thumbnailAssetId != null) {
// set thumbnail with asset from database
album.thumbnail.value =
await _assetRepository.getByRemoteId(thumbnailAssetId);
album.thumbnail.value = await _assetRepository.getByRemoteId(thumbnailAssetId);
}
if (album.remoteUsers.isNotEmpty) {
// replace all users with users from database
final users = await _isarUserRepository
.getByUserIds(album.remoteUsers.map((user) => user.id).toList());
final users = await _isarUserRepository.getByUserIds(album.remoteUsers.map((user) => user.id).toList());
album.sharedUsers.clear();
album.sharedUsers.addAll(users.nonNulls.map(User.fromDto));
album.shared = true;
}
if (album.remoteAssets.isNotEmpty) {
// replace all assets with assets from database
final assets = await _assetRepository
.getAllByRemoteId(album.remoteAssets.map((asset) => asset.remoteId!));
final assets = await _assetRepository.getAllByRemoteId(album.remoteAssets.map((asset) => asset.remoteId!));
album.assets.clear();
album.assets.addAll(assets);
}

View File

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

View File

@@ -2,8 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart';
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
final exifServiceProvider =
Provider((ref) => ExifService(ref.watch(exifRepositoryProvider)));
final exifServiceProvider = Provider((ref) => ExifService(ref.watch(exifRepositoryProvider)));
class ExifService {
final IsarExifRepository _exifInfoRepository;

View File

@@ -30,15 +30,13 @@ class FolderService {
fullPath = '/$fullPath';
}
List<String> segments = fullPath.split('/')
..removeWhere((s) => s.isEmpty);
List<String> segments = fullPath.split('/')..removeWhere((s) => s.isEmpty);
String currentPath = '';
for (int i = 0; i < segments.length; i++) {
String parentPath = currentPath.isEmpty ? '_root_' : currentPath;
currentPath =
i == 0 ? '/${segments[i]}' : '$currentPath/${segments[i]}';
currentPath = i == 0 ? '/${segments[i]}' : '$currentPath/${segments[i]}';
if (!folderMap.containsKey(parentPath)) {
folderMap[parentPath] = [];
@@ -54,26 +52,20 @@ class FolderService {
);
// Sort folders based on order parameter
folderMap[parentPath]!.sort(
(a, b) => order == SortOrder.desc
? b.name.compareTo(a.name)
: a.name.compareTo(b.name),
(a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name),
);
}
}
}
void attachSubfolders(RecursiveFolder folder) {
String fullPath = folder.path.isEmpty
? '/${folder.name}'
: '${folder.path}/${folder.name}';
String fullPath = folder.path.isEmpty ? '/${folder.name}' : '${folder.path}/${folder.name}';
if (folderMap.containsKey(fullPath)) {
folder.subfolders.addAll(folderMap[fullPath]!);
// Sort subfolders based on order parameter
folder.subfolders.sort(
(a, b) => order == SortOrder.desc
? b.name.compareTo(a.name)
: a.name.compareTo(b.name),
(a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name),
);
for (var subfolder in folder.subfolders) {
attachSubfolders(subfolder);
@@ -84,9 +76,7 @@ class FolderService {
List<RecursiveFolder> rootSubfolders = folderMap['_root_'] ?? [];
// Sort root subfolders based on order parameter
rootSubfolders.sort(
(a, b) => order == SortOrder.desc
? b.name.compareTo(a.name)
: a.name.compareTo(b.name),
(a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name),
);
for (var folder in rootSubfolders) {
@@ -105,8 +95,7 @@ class FolderService {
) async {
try {
if (folder is RecursiveFolder) {
String fullPath =
folder.path.isEmpty ? folder.name : '${folder.path}/${folder.name}';
String fullPath = folder.path.isEmpty ? folder.name : '${folder.path}/${folder.name}';
fullPath = fullPath[0] == '/' ? fullPath.substring(1) : fullPath;
var result = await _folderApiRepository.getAssetsForPath(fullPath);

View File

@@ -71,8 +71,7 @@ class GCastService {
}
void _handleMediaStatus(Map<String, dynamic> message) {
final statusList =
(message['status'] as List).whereType<Map<String, dynamic>>().toList();
final statusList = (message['status'] as List).whereType<Map<String, dynamic>>().toList();
if (statusList.isEmpty) {
return;
@@ -112,8 +111,7 @@ class GCastService {
}
if (status["currentTime"] != null) {
final currentTime =
Duration(milliseconds: (status["currentTime"] * 1000 ?? 0).toInt());
final currentTime = Duration(milliseconds: (status["currentTime"] * 1000 ?? 0).toInt());
onCurrentTime?.call(currentTime);
}
}
@@ -150,8 +148,7 @@ class GCastService {
// we want to make sure we have at least 10 seconds remaining in the session
// this is to account for network latency and other delays when sending the request
final bufferedExpiration =
tokenExpiration.subtract(const Duration(seconds: 10));
final bufferedExpiration = tokenExpiration.subtract(const Duration(seconds: 10));
return bufferedExpiration.isAfter(DateTime.now());
}
@@ -181,8 +178,7 @@ class GCastService {
type: AssetMediaSize.fullsize,
);
final authenticatedURL =
"$unauthenticatedUrl&sessionKey=${sessionKey?.token}";
final authenticatedURL = "$unauthenticatedUrl&sessionKey=${sessionKey?.token}";
// get image mime type
final mimeType = await _assetApiRepository.getAssetMIMEType(asset.id);
@@ -210,8 +206,7 @@ class GCastService {
_mediaStatusPollingTimer?.cancel();
if (asset.isVideo) {
_mediaStatusPollingTimer =
Timer.periodic(const Duration(milliseconds: 500), (timer) {
_mediaStatusPollingTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) {
if (isConnected) {
_gCastRepository.sendMessage(CastSession.kNamespaceMedia, {
"type": "GET_STATUS",
@@ -264,11 +259,7 @@ class GCastService {
return dests
.map(
(device) => (
device.extras["fn"] ?? "Google Cast",
CastDestinationType.googleCast,
device
),
(device) => (device.extras["fn"] ?? "Google Cast", CastDestinationType.googleCast, device),
)
.where((device) {
final caString = device.$3.extras["ca"];

View File

@@ -13,8 +13,7 @@ class LocalFilesManagerService {
Future<bool> moveToTrash(List<String> mediaUrls) async {
try {
return await _channel
.invokeMethod('moveToTrash', {'mediaUrls': mediaUrls});
return await _channel.invokeMethod('moveToTrash', {'mediaUrls': mediaUrls});
} catch (e, s) {
_logger.warning('Error moving file to trash', e, s);
return false;

View File

@@ -13,8 +13,7 @@ final localNotificationService = Provider(
);
class LocalNotificationService {
final FlutterLocalNotificationsPlugin _localNotificationPlugin =
FlutterLocalNotificationsPlugin();
final FlutterLocalNotificationsPlugin _localNotificationPlugin = FlutterLocalNotificationsPlugin();
final PermissionStatus _permissionStatus;
final Ref ref;
@@ -29,17 +28,14 @@ class LocalNotificationService {
static const cancelUploadActionID = 'cancel_upload';
Future<void> setup() async {
const androidSetting =
AndroidInitializationSettings('@drawable/notification_icon');
const androidSetting = AndroidInitializationSettings('@drawable/notification_icon');
const iosSetting = DarwinInitializationSettings();
const initSettings =
InitializationSettings(android: androidSetting, iOS: iosSetting);
const initSettings = InitializationSettings(android: androidSetting, iOS: iosSetting);
await _localNotificationPlugin.initialize(
initSettings,
onDidReceiveNotificationResponse:
_onDidReceiveForegroundNotificationResponse,
onDidReceiveNotificationResponse: _onDidReceiveForegroundNotificationResponse,
);
}

View File

@@ -35,8 +35,7 @@ class MemoryService {
List<Memory> memories = [];
for (final memory in data) {
final dbAssets = await _assetRepository
.getAllByRemoteId(memory.assets.map((e) => e.id));
final dbAssets = await _assetRepository.getAllByRemoteId(memory.assets.map((e) => e.id));
final yearsAgo = now.year - memory.data.year;
if (dbAssets.isNotEmpty) {
final String title = 'years_ago'.t(
@@ -67,8 +66,7 @@ class MemoryService {
if (memoryResponse == null) {
return null;
}
final dbAssets = await _assetRepository
.getAllByRemoteId(memoryResponse.assets.map((e) => e.id));
final dbAssets = await _assetRepository.getAllByRemoteId(memoryResponse.assets.map((e) => e.id));
if (dbAssets.isEmpty) {
log.warning("No assets found for memory with ID: $id");
return null;

View File

@@ -45,8 +45,7 @@ class PartnerService {
Future<bool> removePartner(UserDto partner) async {
try {
await _partnerApiRepository.delete(partner.id);
await _isarUserRepository
.update(partner.copyWith(isPartnerSharedBy: false));
await _isarUserRepository.update(partner.copyWith(isPartnerSharedBy: false));
} catch (e) {
_log.warning("Failed to remove partner ${partner.id}", e);
return false;
@@ -57,8 +56,7 @@ class PartnerService {
Future<bool> addPartner(UserDto partner) async {
try {
await _partnerApiRepository.create(partner.id);
await _isarUserRepository
.update(partner.copyWith(isPartnerSharedBy: true));
await _isarUserRepository.update(partner.copyWith(isPartnerSharedBy: true));
return true;
} catch (e) {
_log.warning("Failed to add partner ${partner.id}", e);
@@ -75,8 +73,7 @@ class PartnerService {
partner.id,
inTimeline: inTimeline,
);
await _isarUserRepository
.update(partner.copyWith(inTimeline: dto.inTimeline));
await _isarUserRepository.update(partner.copyWith(inTimeline: dto.inTimeline));
return true;
} catch (e) {
_log.warning("Failed to update partner ${partner.id}", e);

View File

@@ -40,8 +40,7 @@ class PersonService {
Future<List<Asset>> getPersonAssets(String id) async {
try {
final assets = await _assetApiRepository.search(personIds: [id]);
return await _assetRepository
.getAllByRemoteId(assets.map((a) => a.remoteId!));
return await _assetRepository.getAllByRemoteId(assets.map((a) => a.remoteId!));
} catch (error, stack) {
_log.severe("Error while fetching person assets", error, stack);
}

View File

@@ -61,8 +61,7 @@ class SearchService {
}
return SearchResult(
assets: await _assetRepository
.getAllByRemoteId(response.assets.items.map((e) => e.id)),
assets: await _assetRepository.getAllByRemoteId(response.assets.items.map((e) => e.id)),
nextPage: response.assets.nextPage?.toInt(),
);
} catch (error, stackTrace) {

View File

@@ -10,8 +10,7 @@ import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';
import 'api.service.dart';
final shareServiceProvider =
Provider((ref) => ShareService(ref.watch(apiServiceProvider)));
final shareServiceProvider = Provider((ref) => ShareService(ref.watch(apiServiceProvider)));
class ShareService {
final ApiService _apiService;
@@ -37,8 +36,7 @@ class ShareService {
final tempDir = await getTemporaryDirectory();
final fileName = asset.fileName;
final tempFile = await File('${tempDir.path}/$fileName').create();
final res = await _apiService.assetsApi
.downloadAssetWithHttpInfo(asset.remoteId!);
final res = await _apiService.assetsApi.downloadAssetWithHttpInfo(asset.remoteId!);
if (res.statusCode != 200) {
_log.severe(

View File

@@ -18,9 +18,7 @@ class SharedLinkService {
Future<AsyncValue<List<SharedLink>>> getAllSharedLinks() async {
try {
final list = await _apiService.sharedLinksApi.getAllSharedLinks();
return list != null
? AsyncData(list.map(SharedLink.fromDto).toList())
: const AsyncData([]);
return list != null ? AsyncData(list.map(SharedLink.fromDto).toList()) : const AsyncData([]);
} catch (e, stack) {
_log.severe("Failed to fetch shared links", e, stack);
return AsyncError(e, stack);
@@ -46,8 +44,7 @@ class SharedLinkService {
DateTime? expiresAt,
}) async {
try {
final type =
albumId != null ? SharedLinkType.ALBUM : SharedLinkType.INDIVIDUAL;
final type = albumId != null ? SharedLinkType.ALBUM : SharedLinkType.INDIVIDUAL;
SharedLinkCreateDto? dto;
if (type == SharedLinkType.ALBUM) {
dto = SharedLinkCreateDto(
@@ -74,8 +71,7 @@ class SharedLinkService {
}
if (dto != null) {
final responseDto =
await _apiService.sharedLinksApi.createSharedLink(dto);
final responseDto = await _apiService.sharedLinksApi.createSharedLink(dto);
if (responseDto != null) {
return SharedLink.fromDto(responseDto);
}

View File

@@ -60,8 +60,7 @@ class StackService {
removeAssets.add(asset);
}
await _assetRepository
.transaction(() => _assetRepository.updateAll(removeAssets));
await _assetRepository.transaction(() => _assetRepository.updateAll(removeAssets));
} catch (error) {
debugPrint("Error while deleting stack: $error");
}

View File

@@ -94,8 +94,7 @@ class SyncService {
/// Syncs users from the server to the local database
/// Returns `true`if there were any changes
Future<bool> syncUsersFromServer(List<UserDto> users) =>
_lock.run(() => _syncUsersFromServer(users));
Future<bool> syncUsersFromServer(List<UserDto> users) => _lock.run(() => _syncUsersFromServer(users));
/// Syncs remote assets owned by the logged-in user to the DB
/// Returns `true` if there were any changes
@@ -105,8 +104,7 @@ class SyncService {
List<UserDto> users,
DateTime since,
) getChangedAssets,
required FutureOr<List<Asset>?> Function(UserDto user, DateTime until)
loadAssets,
required FutureOr<List<Asset>?> Function(UserDto user, DateTime until) loadAssets,
}) =>
_lock.run(
() async =>
@@ -139,18 +137,13 @@ class SyncService {
}
deleteCandidates.sort(Asset.compareById);
existing.sort(Asset.compareById);
return _diffAssets(existing, deleteCandidates, compare: Asset.compareById)
.$3
.map((e) => e.id)
.toList();
return _diffAssets(existing, deleteCandidates, compare: Asset.compareById).$3.map((e) => e.id).toList();
}
/// Syncs a new asset to the db. Returns `true` if successful
Future<bool> syncNewAssetToDb(Asset newAsset) =>
_lock.run(() => _syncNewAssetToDb(newAsset));
Future<bool> syncNewAssetToDb(Asset newAsset) => _lock.run(() => _syncNewAssetToDb(newAsset));
Future<bool> removeAllLocalAlbumsAndAssets() =>
_lock.run(_removeAllLocalAlbumsAndAssets);
Future<bool> removeAllLocalAlbumsAndAssets() => _lock.run(_removeAllLocalAlbumsAndAssets);
// private methods:
@@ -189,8 +182,7 @@ class SyncService {
/// Syncs a new asset to the db. Returns `true` if successful
Future<bool> _syncNewAssetToDb(Asset a) async {
final Asset? inDb =
await _assetRepository.getByOwnerIdChecksum(a.ownerId, a.checksum);
final Asset? inDb = await _assetRepository.getByOwnerIdChecksum(a.ownerId, a.checksum);
if (inDb != null) {
// unify local/remote assets by replacing the
// local-only asset in the DB with a local&remote asset
@@ -214,8 +206,7 @@ class SyncService {
) getChangedAssets,
) async {
final currentUser = _userService.getMyUser();
final DateTime? since =
(await _eTagRepository.get(currentUser.id))?.time?.toUtc();
final DateTime? since = (await _eTagRepository.get(currentUser.id))?.time?.toUtc();
if (since == null) return null;
final DateTime now = DateTime.now();
final (toUpsert, toDelete) = await getChangedAssets(users, since);
@@ -244,13 +235,10 @@ class SyncService {
Future<void> _moveToTrashMatchedAssets(Iterable<String> idsToDelete) async {
final List<Asset> localAssets = await _assetRepository.getAllLocal();
final List<Asset> matchedAssets = localAssets
.where((asset) => idsToDelete.contains(asset.remoteId))
.toList();
final List<Asset> matchedAssets = localAssets.where((asset) => idsToDelete.contains(asset.remoteId)).toList();
final mediaUrls = await Future.wait(
matchedAssets
.map((asset) => asset.local?.getMediaUrl() ?? Future.value(null)),
matchedAssets.map((asset) => asset.local?.getMediaUrl() ?? Future.value(null)),
);
await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList());
@@ -371,10 +359,8 @@ class SyncService {
final bool changes = await diffSortedLists(
remoteAlbums,
dbAlbums,
compare: (remoteAlbum, dbAlbum) =>
remoteAlbum.remoteId!.compareTo(dbAlbum.remoteId!),
both: (remoteAlbum, dbAlbum) =>
_syncRemoteAlbum(remoteAlbum, dbAlbum, toDelete, existing),
compare: (remoteAlbum, dbAlbum) => remoteAlbum.remoteId!.compareTo(dbAlbum.remoteId!),
both: (remoteAlbum, dbAlbum) => _syncRemoteAlbum(remoteAlbum, dbAlbum, toDelete, existing),
onlyFirst: (remoteAlbum) => _addAlbumFromServer(remoteAlbum, existing),
onlySecond: (dbAlbum) => _removeAlbumFromDb(dbAlbum, toDelete),
);
@@ -421,11 +407,9 @@ class SyncService {
);
// update shared users
final List<UserDto> sharedUsers =
album.sharedUsers.map((u) => u.toDto()).toList(growable: false);
final List<UserDto> sharedUsers = album.sharedUsers.map((u) => u.toDto()).toList(growable: false);
sharedUsers.sort((a, b) => a.id.compareTo(b.id));
final List<UserDto> users = dto.remoteUsers.map((u) => u.toDto()).toList()
..sort((a, b) => a.id.compareTo(b.id));
final List<UserDto> users = dto.remoteUsers.map((u) => u.toDto()).toList()..sort((a, b) => a.id.compareTo(b.id));
final List<String> userIdsToAdd = [];
final List<UserDto> usersToUnlink = [];
diffSortedListsSync(
@@ -456,10 +440,8 @@ class SyncService {
album.sortOrder = dto.sortOrder;
final remoteThumbnailAssetId = dto.remoteThumbnailAssetId;
if (remoteThumbnailAssetId != null &&
album.thumbnail.value?.remoteId != remoteThumbnailAssetId) {
album.thumbnail.value =
await _assetRepository.getByRemoteId(remoteThumbnailAssetId);
if (remoteThumbnailAssetId != null && album.thumbnail.value?.remoteId != remoteThumbnailAssetId) {
album.thumbnail.value = await _assetRepository.getByRemoteId(remoteThumbnailAssetId);
}
// write & commit all changes to DB
@@ -480,8 +462,7 @@ class SyncService {
if (album.shared || dto.shared) {
final userId = (_userService.getMyUser()).id;
final foreign =
await _assetRepository.getByAlbum(album, notOwnedBy: [userId]);
final foreign = await _assetRepository.getByAlbum(album, notOwnedBy: [userId]);
existing.addAll(foreign);
// delete assets in DB unless they belong to this user or part of some other shared album
@@ -505,16 +486,14 @@ class SyncService {
if (album.remoteAssetCount == album.remoteAssets.length) {
// in case an album contains assets not yet present in local DB:
// put missing album assets into local DB
final (existingInDb, updated) =
await _linkWithExistingFromDb(album.remoteAssets.toList());
final (existingInDb, updated) = await _linkWithExistingFromDb(album.remoteAssets.toList());
existing.addAll(existingInDb);
await upsertAssetsWithExif(updated);
await _entityService.fillAlbumWithDatabaseEntities(album);
await _albumRepository.create(album);
} else {
_log.warning(
"Failed to add album from server: assetCount ${album.remoteAssetCount} != "
_log.warning("Failed to add album from server: assetCount ${album.remoteAssetCount} != "
"asset array length ${album.remoteAssets.length} for album ${album.name}");
}
}
@@ -534,8 +513,7 @@ class SyncService {
} else if (album.shared) {
// delete assets in DB unless they belong to this user or are part of some other shared album or belong to a partner
final userIds = (await _getAllAccessibleUsers()).map((user) => user.id);
final orphanedAssets =
await _assetRepository.getByAlbum(album, notOwnedBy: userIds);
final orphanedAssets = await _assetRepository.getByAlbum(album, notOwnedBy: userIds);
deleteCandidates.addAll(orphanedAssets);
}
try {
@@ -553,8 +531,7 @@ class SyncService {
Set<String>? excludedAssets,
]) async {
onDevice.sort((a, b) => a.localId!.compareTo(b.localId!));
final inDb =
await _albumRepository.getAll(remote: false, sortBy: AlbumSort.localId);
final inDb = await _albumRepository.getAll(remote: false, sortBy: AlbumSort.localId);
final List<Asset> deleteCandidates = [];
final List<Asset> existing = [];
final bool anyChanges = await diffSortedLists(
@@ -574,8 +551,7 @@ class SyncService {
_log.fine(
"Syncing all local albums almost done. Collected ${deleteCandidates.length} asset candidates to delete",
);
final (toDelete, toUpdate) =
_handleAssetRemoval(deleteCandidates, existing, remote: false);
final (toDelete, toUpdate) = _handleAssetRemoval(deleteCandidates, existing, remote: false);
_log.fine(
"${toDelete.length} assets to delete, ${toUpdate.length} to update",
);
@@ -610,9 +586,7 @@ class SyncService {
return false;
}
_log.info("Local album ${deviceAlbum.name} has changed. Syncing...");
if (!forceRefresh &&
excludedAssets == null &&
await _syncDeviceAlbumFast(deviceAlbum, dbAlbum)) {
if (!forceRefresh && excludedAssets == null && await _syncDeviceAlbumFast(deviceAlbum, dbAlbum)) {
_log.info("Fast synced local album ${deviceAlbum.name} to DB");
return true;
}
@@ -624,8 +598,7 @@ class SyncService {
);
assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!");
final int assetCountOnDevice =
await _albumMediaRepository.getAssetCount(deviceAlbum.localId!);
final int assetCountOnDevice = await _albumMediaRepository.getAssetCount(deviceAlbum.localId!);
final List<Asset> onDevice = await _getHashedAssets(
deviceAlbum,
excludedAssets: excludedAssets,
@@ -643,9 +616,7 @@ class SyncService {
_log.info(
"Only excluded assets in local album ${deviceAlbum.name} changed. Stopping sync.",
);
if (assetCountOnDevice !=
(await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))
?.assetCount) {
if (assetCountOnDevice != (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount) {
await _eTagRepository.upsertAll([
ETag(
id: deviceAlbum.eTagKeyAssetCount,
@@ -667,8 +638,7 @@ class SyncService {
dbAlbum.name = deviceAlbum.name;
dbAlbum.description = deviceAlbum.description;
dbAlbum.modifiedAt = deviceAlbum.modifiedAt;
if (dbAlbum.thumbnail.value != null &&
toDelete.contains(dbAlbum.thumbnail.value)) {
if (dbAlbum.thumbnail.value != null && toDelete.contains(dbAlbum.thumbnail.value)) {
dbAlbum.thumbnail.value = null;
}
try {
@@ -702,12 +672,8 @@ class SyncService {
);
return false;
}
final int totalOnDevice =
await _albumMediaRepository.getAssetCount(deviceAlbum.localId!);
final int lastKnownTotal =
(await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))
?.assetCount ??
0;
final int totalOnDevice = await _albumMediaRepository.getAssetCount(deviceAlbum.localId!);
final int lastKnownTotal = (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount ?? 0;
if (totalOnDevice <= lastKnownTotal) {
_log.info(
"Local album ${deviceAlbum.name} totalOnDevice is less than lastKnownTotal. Skipping sync.",
@@ -776,8 +742,7 @@ class SyncService {
album.thumbnail.value = thumb;
try {
await _albumRepository.create(album);
final int assetCount =
await _albumMediaRepository.getAssetCount(album.localId!);
final int assetCount = await _albumMediaRepository.getAssetCount(album.localId!);
await _eTagRepository.upsertAll([
ETag(id: album.eTagKeyAssetCount, assetCount: assetCount),
]);
@@ -913,9 +878,8 @@ class SyncService {
modifiedFrom: modifiedFrom,
modifiedUntil: modifiedUntil,
);
final filtered = excludedAssets == null
? entities
: entities.where((e) => !excludedAssets.contains(e.localId!)).toList();
final filtered =
excludedAssets == null ? entities : entities.where((e) => !excludedAssets.contains(e.localId!)).toList();
return _hashService.hashAssets(filtered);
}
@@ -942,15 +906,13 @@ class SyncService {
deviceAlbum.description != dbAlbum.description ||
!deviceAlbum.modifiedAt.isAtSameMomentAs(dbAlbum.modifiedAt) ||
await _albumMediaRepository.getAssetCount(deviceAlbum.localId!) !=
(await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))
?.assetCount;
(await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount;
}
Future<bool> _removeAllLocalAlbumsAndAssets() async {
try {
final assets = await _assetRepository.getAllLocal();
final (toDelete, toUpdate) =
_handleAssetRemoval(assets, [], remote: false);
final (toDelete, toUpdate) = _handleAssetRemoval(assets, [], remote: false);
await _assetRepository.transaction(() async {
await _assetRepository.deleteByIds(toDelete);
await _assetRepository.updateAll(toUpdate);
@@ -971,10 +933,8 @@ class SyncService {
_log.warning("Failed to fetch users", e);
users = null;
}
final List<UserDto> sharedBy =
await _partnerApiRepository.getAll(Direction.sharedByMe);
final List<UserDto> sharedWith =
await _partnerApiRepository.getAll(Direction.sharedWithMe);
final List<UserDto> sharedBy = await _partnerApiRepository.getAll(Direction.sharedByMe);
final List<UserDto> sharedWith = await _partnerApiRepository.getAll(Direction.sharedWithMe);
if (users == null) {
_log.warning("Failed to refresh users");

View File

@@ -103,8 +103,7 @@ class TimelineService {
}
GroupAssetsBy _getGroupByOption() {
return GroupAssetsBy
.values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)];
return GroupAssetsBy.values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)];
}
Stream<RenderList> watchLockedTimelineProvider() async* {

View File

@@ -20,14 +20,11 @@ class UploadService {
void Function(TaskStatusUpdate)? onUploadStatus;
void Function(TaskProgressUpdate)? onTaskProgress;
final StreamController<TaskStatusUpdate> _taskStatusController =
StreamController<TaskStatusUpdate>.broadcast();
final StreamController<TaskProgressUpdate> _taskProgressController =
StreamController<TaskProgressUpdate>.broadcast();
final StreamController<TaskStatusUpdate> _taskStatusController = StreamController<TaskStatusUpdate>.broadcast();
final StreamController<TaskProgressUpdate> _taskProgressController = StreamController<TaskProgressUpdate>.broadcast();
Stream<TaskStatusUpdate> get taskStatusStream => _taskStatusController.stream;
Stream<TaskProgressUpdate> get taskProgressStream =>
_taskProgressController.stream;
Stream<TaskProgressUpdate> get taskProgressStream => _taskProgressController.stream;
UploadService(
this._uploadRepository,
@@ -103,8 +100,7 @@ class UploadService {
final headers = ApiService.getRequestHeaders();
final deviceId = Store.get(StoreKey.deviceId);
final (baseDirectory, directory, filename) =
await Task.split(filePath: file.path);
final (baseDirectory, directory, filename) = await Task.split(filePath: file.path);
final stats = await file.stat();
final fileCreatedAt = stats.changed;
final fileModifiedAt = stats.modified;