chore: bump dart sdk to 3.8 (#20355)

* chore: bump dart sdk to 3.8

* chore: make build

* make pigeon

* chore: format files

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong
2025-07-29 00:34:03 +05:30
committed by GitHub
parent 9b3718120b
commit e52b9d15b5
643 changed files with 32561 additions and 35292 deletions
@@ -13,9 +13,9 @@ class AssetService {
const AssetService({
required RemoteAssetRepository remoteAssetRepository,
required DriftLocalAssetRepository localAssetRepository,
}) : _remoteAssetRepository = remoteAssetRepository,
_localAssetRepository = localAssetRepository,
_platform = const LocalPlatform();
}) : _remoteAssetRepository = remoteAssetRepository,
_localAssetRepository = localAssetRepository,
_platform = const LocalPlatform();
Stream<BaseAsset?> watchAsset(BaseAsset asset) {
final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).id;
+5 -8
View File
@@ -25,19 +25,16 @@ class HashService {
required NativeSyncApi nativeSyncApi,
this.batchSizeLimit = kBatchHashSizeLimit,
this.batchFileLimit = kBatchHashFileLimit,
}) : _localAlbumRepository = localAlbumRepository,
_localAssetRepository = localAssetRepository,
_storageRepository = storageRepository,
_nativeSyncApi = nativeSyncApi;
}) : _localAlbumRepository = localAlbumRepository,
_localAssetRepository = localAssetRepository,
_storageRepository = storageRepository,
_nativeSyncApi = nativeSyncApi;
Future<void> hashAssets() async {
final Stopwatch stopwatch = Stopwatch()..start();
// Sorted by backupSelection followed by isCloud
final localAlbums = await _localAlbumRepository.getAll(
sortBy: {
SortLocalAlbumsBy.backupSelection,
SortLocalAlbumsBy.isIosSharedAlbum,
},
sortBy: {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum},
);
for (final album in localAlbums) {
@@ -21,9 +21,9 @@ class LocalSyncService {
required DriftLocalAlbumRepository localAlbumRepository,
required NativeSyncApi nativeSyncApi,
Platform? platform,
}) : _localAlbumRepository = localAlbumRepository,
_nativeSyncApi = nativeSyncApi,
_platform = platform ?? const LocalPlatform();
}) : _localAlbumRepository = localAlbumRepository,
_nativeSyncApi = nativeSyncApi,
_platform = platform ?? const LocalPlatform();
Future<void> sync({bool full = false}) async {
final Stopwatch stopwatch = Stopwatch()..start();
@@ -70,9 +70,7 @@ class LocalSyncService {
for (final album in cloudAlbums) {
final dbAlbum = dbAlbums.firstWhereOrNull((a) => a.id == album.id);
if (dbAlbum == null) {
_log.warning(
"Cloud album ${album.name} not found in local database. Skipping sync.",
);
_log.warning("Cloud album ${album.name} not found in local database. Skipping sync.");
continue;
}
await updateAlbum(dbAlbum, album);
@@ -120,10 +118,7 @@ class LocalSyncService {
final assets = album.assetCount > 0 ? await _nativeSyncApi.getAssetsForAlbum(album.id) : <PlatformAsset>[];
await _localAlbumRepository.upsert(
album,
toUpsert: assets.toLocalAssets(),
);
await _localAlbumRepository.upsert(album, toUpsert: assets.toLocalAssets());
_log.fine("Successfully added device album ${album.name}");
} catch (e, s) {
_log.warning("Error while adding device album", e, s);
@@ -146,9 +141,7 @@ class LocalSyncService {
_log.fine("Syncing device album ${dbAlbum.name}");
if (_albumsEqual(deviceAlbum, dbAlbum)) {
_log.fine(
"Device album ${dbAlbum.name} has not changed. Skipping sync.",
);
_log.fine("Device album ${dbAlbum.name} has not changed. Skipping sync.");
return false;
}
@@ -172,10 +165,7 @@ class LocalSyncService {
@visibleForTesting
// The [deviceAlbum] is expected to be refreshed before calling this method
// with modified time and asset count
Future<bool> checkAddition(
LocalAlbum dbAlbum,
LocalAlbum deviceAlbum,
) async {
Future<bool> checkAddition(LocalAlbum dbAlbum, LocalAlbum deviceAlbum) async {
try {
_log.fine("Fast syncing device album ${dbAlbum.name}");
// Assets has been modified
@@ -189,9 +179,7 @@ class LocalSyncService {
// Early return if no new assets were found
if (newAssetsCount == 0) {
_log.fine(
"No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}",
);
_log.fine("No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}");
return false;
}
@@ -201,10 +189,7 @@ class LocalSyncService {
return false;
}
final newAssets = await _nativeSyncApi.getAssetsForAlbum(
deviceAlbum.id,
updatedTimeCond: updatedTime,
);
final newAssets = await _nativeSyncApi.getAssetsForAlbum(deviceAlbum.id, updatedTimeCond: updatedTime);
await _localAlbumRepository.upsert(
deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection),
@@ -229,9 +214,7 @@ class LocalSyncService {
final assetsInDb = dbAlbum.assetCount > 0 ? await _localAlbumRepository.getAssets(dbAlbum.id) : <LocalAsset>[];
if (deviceAlbum.assetCount == 0) {
_log.fine(
"Device album ${deviceAlbum.name} is empty. Removing assets from DB.",
);
_log.fine("Device album ${deviceAlbum.name} is empty. Removing assets from DB.");
await _localAlbumRepository.upsert(
deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection),
toDelete: assetsInDb.map((a) => a.id),
@@ -239,18 +222,11 @@ class LocalSyncService {
return true;
}
final updatedDeviceAlbum = deviceAlbum.copyWith(
backupSelection: dbAlbum.backupSelection,
);
final updatedDeviceAlbum = deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection);
if (dbAlbum.assetCount == 0) {
_log.fine(
"Device album ${deviceAlbum.name} is empty. Adding assets to DB.",
);
await _localAlbumRepository.upsert(
updatedDeviceAlbum,
toUpsert: assetsInDevice,
);
_log.fine("Device album ${deviceAlbum.name} is empty. Adding assets to DB.");
await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsInDevice);
return true;
}
@@ -282,18 +258,12 @@ class LocalSyncService {
);
if (assetsToUpsert.isEmpty && assetsToDelete.isEmpty) {
_log.fine(
"No asset changes detected in album ${deviceAlbum.name}. Updating metadata.",
);
_log.fine("No asset changes detected in album ${deviceAlbum.name}. Updating metadata.");
_localAlbumRepository.upsert(updatedDeviceAlbum);
return true;
}
await _localAlbumRepository.upsert(
updatedDeviceAlbum,
toUpsert: assetsToUpsert,
toDelete: assetsToDelete,
);
await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsToUpsert, toDelete: assetsToDelete);
return true;
} catch (e, s) {
+2 -9
View File
@@ -61,11 +61,7 @@ class LogService {
return instance;
}
LogService._(
this._logRepository,
this._storeRepository,
this._shouldBuffer,
) {
LogService._(this._logRepository, this._storeRepository, this._shouldBuffer) {
_logSubscription = Logger.root.onRecord.listen(_handleLogRecord);
}
@@ -89,10 +85,7 @@ class LogService {
if (_shouldBuffer) {
_msgBuffer.add(record);
_flushTimer ??= Timer(
const Duration(seconds: 5),
() => unawaited(flushBuffer()),
);
_flushTimer ??= Timer(const Duration(seconds: 5), () => unawaited(flushBuffer()));
} else {
unawaited(_logRepository.insert(record));
}
@@ -7,10 +7,7 @@ class DriftPartnerService {
final DriftPartnerRepository _driftPartnerRepository;
final PartnerApiRepository _partnerApiRepository;
const DriftPartnerService(
this._driftPartnerRepository,
this._partnerApiRepository,
);
const DriftPartnerService(this._driftPartnerRepository, this._partnerApiRepository);
Future<List<PartnerUserDto>> getSharedWith(String userId) {
return _driftPartnerRepository.getSharedWith(userId);
@@ -20,9 +17,7 @@ class DriftPartnerService {
return _driftPartnerRepository.getSharedBy(userId);
}
Future<List<PartnerUserDto>> getAvailablePartners(
String currentUserId,
) async {
Future<List<PartnerUserDto>> getAvailablePartners(String currentUserId) async {
final otherUsers = await _driftPartnerRepository.getAvailablePartners(currentUserId);
final currentPartners = await _driftPartnerRepository.getSharedBy(currentUserId);
final available = otherUsers.where((user) {
@@ -39,10 +34,7 @@ class DriftPartnerService {
return;
}
await _partnerApiRepository.update(
partnerId,
inTimeline: !partner.inTimeline,
);
await _partnerApiRepository.update(partnerId, inTimeline: !partner.inTimeline);
await _driftPartnerRepository.toggleShowInTimeline(partner, userId);
}
@@ -26,11 +26,7 @@ class RemoteAlbumService {
return _repository.get(albumId);
}
List<RemoteAlbum> sortAlbums(
List<RemoteAlbum> albums,
RemoteAlbumSortMode sortMode, {
bool isReverse = false,
}) {
List<RemoteAlbum> sortAlbums(List<RemoteAlbum> albums, RemoteAlbumSortMode sortMode, {bool isReverse = false}) {
return sortMode.sortFn(albums, isReverse);
}
@@ -69,16 +65,8 @@ class RemoteAlbumService {
return filtered;
}
Future<RemoteAlbum> createAlbum({
required String title,
required List<String> assetIds,
String? description,
}) async {
final album = await _albumApiRepository.createDriftAlbum(
title,
description: description,
assetIds: assetIds,
);
Future<RemoteAlbum> createAlbum({required String title, required List<String> assetIds, String? description}) async {
final album = await _albumApiRepository.createDriftAlbum(title, description: description, assetIds: assetIds);
await _repository.create(album, assetIds);
@@ -120,14 +108,8 @@ class RemoteAlbumService {
return _repository.getAssets(albumId);
}
Future<int> addAssets({
required String albumId,
required List<String> assetIds,
}) async {
final album = await _albumApiRepository.addAssets(
albumId,
assetIds,
);
Future<int> addAssets({required String albumId, required List<String> assetIds}) async {
final album = await _albumApiRepository.addAssets(albumId, assetIds);
await _repository.addAssets(albumId, album.added);
@@ -140,10 +122,7 @@ class RemoteAlbumService {
await _repository.deleteAlbum(albumId);
}
Future<void> addUsers({
required String albumId,
required List<String> userIds,
}) async {
Future<void> addUsers({required String albumId, required List<String> userIds}) async {
await _albumApiRepository.addUsers(albumId, userIds);
return _repository.addUsers(albumId, userIds);
@@ -83,10 +83,10 @@ extension on AssetResponseDto {
extension on AssetTypeEnum {
AssetType toAssetType() => switch (this) {
AssetTypeEnum.IMAGE => AssetType.image,
AssetTypeEnum.VIDEO => AssetType.video,
AssetTypeEnum.AUDIO => AssetType.audio,
AssetTypeEnum.OTHER => AssetType.other,
_ => throw Exception('Unknown AssetType value: $this'),
};
AssetTypeEnum.IMAGE => AssetType.image,
AssetTypeEnum.VIDEO => AssetType.video,
AssetTypeEnum.AUDIO => AssetType.audio,
AssetTypeEnum.OTHER => AssetType.other,
_ => throw Exception('Unknown AssetType value: $this'),
};
}
@@ -24,16 +24,12 @@ class StoreService {
}
// TODO: Replace the implementation with the one from create after removing the typedef
static Future<StoreService> init({
required IsarStoreRepository storeRepository,
}) async {
static Future<StoreService> init({required IsarStoreRepository storeRepository}) async {
_instance ??= await create(storeRepository: storeRepository);
return _instance!;
}
static Future<StoreService> create({
required IsarStoreRepository storeRepository,
}) async {
static Future<StoreService> create({required IsarStoreRepository storeRepository}) async {
final instance = StoreService._(storeRepository: storeRepository);
await instance._populateCache();
instance._storeUpdateSubscription = instance._listenForChange();
@@ -48,8 +44,8 @@ class StoreService {
}
StreamSubscription<StoreDto> _listenForChange() => _storeRepository.watchAll().listen((event) {
_cache[event.key.id] = event.value;
});
_cache[event.key.id] = event.value;
});
/// Disposes the store and cancels the subscription. To reuse the store call init() again
void dispose() async {
@@ -18,9 +18,9 @@ class SyncStreamService {
required SyncApiRepository syncApiRepository,
required SyncStreamRepository syncStreamRepository,
bool Function()? cancelChecker,
}) : _syncApiRepository = syncApiRepository,
_syncStreamRepository = syncStreamRepository,
_cancelChecker = cancelChecker;
}) : _syncApiRepository = syncApiRepository,
_syncStreamRepository = syncStreamRepository,
_cancelChecker = cancelChecker;
bool get isCancelled => _cancelChecker?.call() ?? false;
@@ -34,9 +34,7 @@ class SyncStreamService {
Future<void> handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) async {
if (batchData.isEmpty) return;
_logger.info(
'Processing batch of ${batchData.length} AssetUploadReadyV1 events',
);
_logger.info('Processing batch of ${batchData.length} AssetUploadReadyV1 events');
final List<SyncAssetV1> assets = [];
final List<SyncAssetExifV1> exifs = [];
@@ -65,22 +63,12 @@ class SyncStreamService {
}
if (assets.isNotEmpty && exifs.isNotEmpty) {
await _syncStreamRepository.updateAssetsV1(
assets,
debugLabel: 'websocket-batch',
);
await _syncStreamRepository.updateAssetsExifV1(
exifs,
debugLabel: 'websocket-batch',
);
await _syncStreamRepository.updateAssetsV1(assets, debugLabel: 'websocket-batch');
await _syncStreamRepository.updateAssetsExifV1(exifs, debugLabel: 'websocket-batch');
_logger.info('Successfully processed ${assets.length} assets in batch');
}
} catch (error, stackTrace) {
_logger.severe(
"Error processing AssetUploadReadyV1 websocket batch events",
error,
stackTrace,
);
_logger.severe("Error processing AssetUploadReadyV1 websocket batch events", error, stackTrace);
}
}
@@ -114,10 +102,7 @@ class SyncStreamService {
batch.clear();
}
Future<void> _handleSyncData(
SyncEntityType type,
Iterable<Object> data,
) async {
Future<void> _handleSyncData(SyncEntityType type, Iterable<Object> data) async {
_logger.fine("Processing sync data for $type of length ${data.length}");
switch (type) {
case SyncEntityType.userV1:
@@ -135,30 +120,15 @@ class SyncStreamService {
case SyncEntityType.assetExifV1:
return _syncStreamRepository.updateAssetsExifV1(data.cast());
case SyncEntityType.partnerAssetV1:
return _syncStreamRepository.updateAssetsV1(
data.cast(),
debugLabel: 'partner',
);
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner');
case SyncEntityType.partnerAssetBackfillV1:
return _syncStreamRepository.updateAssetsV1(
data.cast(),
debugLabel: 'partner backfill',
);
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner backfill');
case SyncEntityType.partnerAssetDeleteV1:
return _syncStreamRepository.deleteAssetsV1(
data.cast(),
debugLabel: "partner",
);
return _syncStreamRepository.deleteAssetsV1(data.cast(), debugLabel: "partner");
case SyncEntityType.partnerAssetExifV1:
return _syncStreamRepository.updateAssetsExifV1(
data.cast(),
debugLabel: 'partner',
);
return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner');
case SyncEntityType.partnerAssetExifBackfillV1:
return _syncStreamRepository.updateAssetsExifV1(
data.cast(),
debugLabel: 'partner backfill',
);
return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner backfill');
case SyncEntityType.albumV1:
return _syncStreamRepository.updateAlbumsV1(data.cast());
case SyncEntityType.albumDeleteV1:
@@ -166,39 +136,21 @@ class SyncStreamService {
case SyncEntityType.albumUserV1:
return _syncStreamRepository.updateAlbumUsersV1(data.cast());
case SyncEntityType.albumUserBackfillV1:
return _syncStreamRepository.updateAlbumUsersV1(
data.cast(),
debugLabel: 'backfill',
);
return _syncStreamRepository.updateAlbumUsersV1(data.cast(), debugLabel: 'backfill');
case SyncEntityType.albumUserDeleteV1:
return _syncStreamRepository.deleteAlbumUsersV1(data.cast());
case SyncEntityType.albumAssetV1:
return _syncStreamRepository.updateAssetsV1(
data.cast(),
debugLabel: 'album',
);
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album');
case SyncEntityType.albumAssetBackfillV1:
return _syncStreamRepository.updateAssetsV1(
data.cast(),
debugLabel: 'album backfill',
);
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album backfill');
case SyncEntityType.albumAssetExifV1:
return _syncStreamRepository.updateAssetsExifV1(
data.cast(),
debugLabel: 'album',
);
return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album');
case SyncEntityType.albumAssetExifBackfillV1:
return _syncStreamRepository.updateAssetsExifV1(
data.cast(),
debugLabel: 'album backfill',
);
return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album backfill');
case SyncEntityType.albumToAssetV1:
return _syncStreamRepository.updateAlbumToAssetsV1(data.cast());
case SyncEntityType.albumToAssetBackfillV1:
return _syncStreamRepository.updateAlbumToAssetsV1(
data.cast(),
debugLabel: 'backfill',
);
return _syncStreamRepository.updateAlbumToAssetsV1(data.cast(), debugLabel: 'backfill');
case SyncEntityType.albumToAssetDeleteV1:
return _syncStreamRepository.deleteAlbumToAssetsV1(data.cast());
// No-op. SyncAckV1 entities are checkpoints in the sync stream
@@ -218,28 +170,15 @@ class SyncStreamService {
case SyncEntityType.stackDeleteV1:
return _syncStreamRepository.deleteStacksV1(data.cast());
case SyncEntityType.partnerStackV1:
return _syncStreamRepository.updateStacksV1(
data.cast(),
debugLabel: 'partner',
);
return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner');
case SyncEntityType.partnerStackBackfillV1:
return _syncStreamRepository.updateStacksV1(
data.cast(),
debugLabel: 'partner backfill',
);
return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner backfill');
case SyncEntityType.partnerStackDeleteV1:
return _syncStreamRepository.deleteStacksV1(
data.cast(),
debugLabel: 'partner',
);
return _syncStreamRepository.deleteStacksV1(data.cast(), debugLabel: 'partner');
case SyncEntityType.userMetadataV1:
return _syncStreamRepository.updateUserMetadatasV1(
data.cast(),
);
return _syncStreamRepository.updateUserMetadatasV1(data.cast());
case SyncEntityType.userMetadataDeleteV1:
return _syncStreamRepository.deleteUserMetadatasV1(
data.cast(),
);
return _syncStreamRepository.deleteUserMetadatasV1(data.cast());
case SyncEntityType.personV1:
return _syncStreamRepository.updatePeopleV1(data.cast());
case SyncEntityType.personDeleteV1:
@@ -11,27 +11,19 @@ import 'package:immich_mobile/domain/utils/event_stream.dart';
import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart';
import 'package:immich_mobile/utils/async_mutex.dart';
typedef TimelineAssetSource = Future<List<BaseAsset>> Function(
int index,
int count,
);
typedef TimelineAssetSource = Future<List<BaseAsset>> Function(int index, int count);
typedef TimelineBucketSource = Stream<List<Bucket>> Function();
typedef TimelineQuery = ({
TimelineAssetSource assetSource,
TimelineBucketSource bucketSource,
});
typedef TimelineQuery = ({TimelineAssetSource assetSource, TimelineBucketSource bucketSource});
class TimelineFactory {
final DriftTimelineRepository _timelineRepository;
final SettingsService _settingsService;
const TimelineFactory({
required DriftTimelineRepository timelineRepository,
required SettingsService settingsService,
}) : _timelineRepository = timelineRepository,
_settingsService = settingsService;
const TimelineFactory({required DriftTimelineRepository timelineRepository, required SettingsService settingsService})
: _timelineRepository = timelineRepository,
_settingsService = settingsService;
GroupAssetsBy get groupBy {
final group = GroupAssetsBy.values[_settingsService.get(Setting.groupAssetsBy)];
@@ -75,17 +67,11 @@ class TimelineService {
int _totalAssets = 0;
int get totalAssets => _totalAssets;
TimelineService(TimelineQuery query)
: this._(
assetSource: query.assetSource,
bucketSource: query.bucketSource,
);
TimelineService(TimelineQuery query) : this._(assetSource: query.assetSource, bucketSource: query.bucketSource);
TimelineService._({
required TimelineAssetSource assetSource,
required TimelineBucketSource bucketSource,
}) : _assetSource = assetSource,
_bucketSource = bucketSource {
TimelineService._({required TimelineAssetSource assetSource, required TimelineBucketSource bucketSource})
: _assetSource = assetSource,
_bucketSource = bucketSource {
_bucketSubscription = _bucketSource().listen((buckets) {
_mutex.run(() async {
final totalAssets = buckets.fold<int>(0, (acc, bucket) => acc + bucket.assetCount);
@@ -103,10 +89,7 @@ class TimelineService {
count = kTimelineAssetLoadBatchSize;
} else {
offset = _bufferOffset;
count = math.min(
_buffer.length,
totalAssets - _bufferOffset,
);
count = math.min(_buffer.length, totalAssets - _bufferOffset);
}
_buffer = await _assetSource(offset, count);
_bufferOffset = offset;
@@ -134,10 +117,7 @@ class TimelineService {
// make sure to load a meaningful amount of data (and not only the requested slice)
// otherwise, each call to [loadAssets] would result in DB call trashing performance
// fills small requests to [kTimelineAssetLoadBatchSize], adds some legroom into the opposite scroll direction for large requests
final len = math.max(
kTimelineAssetLoadBatchSize,
count + kTimelineAssetLoadOppositeSize,
);
final len = math.max(kTimelineAssetLoadBatchSize, count + kTimelineAssetLoadOppositeSize);
// when scrolling forward, start shortly before the requested offset
// when scrolling backward, end shortly after the requested offset to guard against the user scrolling
// in the other direction a tiny bit resulting in another required load from the DB
+4 -7
View File
@@ -18,9 +18,9 @@ class UserService {
required IsarUserRepository isarUserRepository,
required UserApiRepository userApiRepository,
required StoreService storeService,
}) : _isarUserRepository = isarUserRepository,
_userApiRepository = userApiRepository,
_storeService = storeService;
}) : _isarUserRepository = isarUserRepository,
_userApiRepository = userApiRepository,
_storeService = storeService;
UserDto getMyUser() {
return _storeService.get(StoreKey.currentUser);
@@ -44,10 +44,7 @@ class UserService {
Future<String?> createProfileImage(String name, Uint8List image) async {
try {
final path = await _userApiRepository.createProfileImage(
name: name,
data: image,
);
final path = await _userApiRepository.createProfileImage(name: name, data: image);
final updatedUser = getMyUser().copyWith(profileImagePath: path);
await _storeService.put(StoreKey.currentUser, updatedUser);
await _isarUserRepository.update(updatedUser);