mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
Merge branch 'main' into feat/custom-date-range
This commit is contained in:
@@ -16,9 +16,8 @@ class ScrollToDateEvent extends Event {
|
||||
}
|
||||
|
||||
// Asset Viewer Events
|
||||
class ViewerOpenBottomSheetEvent extends Event {
|
||||
final bool activitiesMode;
|
||||
const ViewerOpenBottomSheetEvent({this.activitiesMode = false});
|
||||
class ViewerShowDetailsEvent extends Event {
|
||||
const ViewerShowDetailsEvent();
|
||||
}
|
||||
|
||||
class ViewerReloadAssetEvent extends Event {
|
||||
|
||||
@@ -73,9 +73,12 @@ enum StoreKey<T> {
|
||||
autoPlayVideo<bool>._(139),
|
||||
albumGridView<bool>._(140),
|
||||
|
||||
// Image viewer navigation settings
|
||||
tapToNavigate<bool>._(141),
|
||||
|
||||
// Map custom time range settings
|
||||
mapCustomFrom<String>._(141),
|
||||
mapCustomTo<String>._(142),
|
||||
mapCustomFrom<String>._(142),
|
||||
mapCustomTo<String>._(143),
|
||||
|
||||
// Experimental stuff
|
||||
photoManagerCustomFilter<bool>._(1000),
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class Tag {
|
||||
final String id;
|
||||
final String value;
|
||||
|
||||
const Tag({required this.id, required this.value});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Tag(id: $id, value: $value)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant Tag other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.id == id && other.value == value;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return id.hashCode ^ value.hashCode;
|
||||
}
|
||||
|
||||
static Tag fromDto(TagResponseDto dto) {
|
||||
return Tag(id: dto.id, value: dto.value);
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,8 @@ class RemoteAlbumService {
|
||||
AlbumSortMode.title => albums.sortedBy((album) => album.name),
|
||||
AlbumSortMode.lastModified => albums.sortedBy((album) => album.updatedAt),
|
||||
AlbumSortMode.assetCount => albums.sortedBy((album) => album.assetCount),
|
||||
AlbumSortMode.mostRecent => await _sortByNewestAsset(albums),
|
||||
AlbumSortMode.mostOldest => await _sortByOldestAsset(albums),
|
||||
AlbumSortMode.mostRecent => await _sortByAssetDate(albums, aggregation: AssetDateAggregation.end),
|
||||
AlbumSortMode.mostOldest => await _sortByAssetDate(albums, aggregation: AssetDateAggregation.start),
|
||||
};
|
||||
final effectiveOrder = isReverse ? sortMode.defaultOrder.reverse() : sortMode.defaultOrder;
|
||||
|
||||
@@ -172,46 +172,25 @@ class RemoteAlbumService {
|
||||
return _repository.getAlbumsContainingAsset(assetId);
|
||||
}
|
||||
|
||||
Future<List<RemoteAlbum>> _sortByNewestAsset(List<RemoteAlbum> albums) async {
|
||||
// map album IDs to their newest asset dates
|
||||
final Map<String, Future<DateTime?>> assetTimestampFutures = {};
|
||||
for (final album in albums) {
|
||||
assetTimestampFutures[album.id] = _repository.getNewestAssetTimestamp(album.id);
|
||||
Future<List<RemoteAlbum>> _sortByAssetDate(
|
||||
List<RemoteAlbum> albums, {
|
||||
required AssetDateAggregation aggregation,
|
||||
}) async {
|
||||
if (albums.isEmpty) return [];
|
||||
|
||||
final albumIds = albums.map((e) => e.id).toList();
|
||||
final sortedIds = await _repository.getSortedAlbumIds(albumIds, aggregation: aggregation);
|
||||
|
||||
final albumMap = Map<String, RemoteAlbum>.fromEntries(albums.map((a) => MapEntry(a.id, a)));
|
||||
|
||||
final sortedAlbums = sortedIds.map((id) => albumMap[id]).whereType<RemoteAlbum>().toList();
|
||||
|
||||
if (sortedAlbums.length < albums.length) {
|
||||
final returnedIdSet = sortedIds.toSet();
|
||||
final emptyAlbums = albums.where((a) => !returnedIdSet.contains(a.id));
|
||||
sortedAlbums.addAll(emptyAlbums);
|
||||
}
|
||||
|
||||
// await all database queries
|
||||
final entries = await Future.wait(
|
||||
assetTimestampFutures.entries.map((entry) async => MapEntry(entry.key, await entry.value)),
|
||||
);
|
||||
final assetTimestamps = Map.fromEntries(entries);
|
||||
|
||||
final sorted = albums.sorted((a, b) {
|
||||
final aDate = assetTimestamps[a.id] ?? DateTime.fromMillisecondsSinceEpoch(0);
|
||||
final bDate = assetTimestamps[b.id] ?? DateTime.fromMillisecondsSinceEpoch(0);
|
||||
return aDate.compareTo(bDate);
|
||||
});
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
Future<List<RemoteAlbum>> _sortByOldestAsset(List<RemoteAlbum> albums) async {
|
||||
// map album IDs to their oldest asset dates
|
||||
final Map<String, Future<DateTime?>> assetTimestampFutures = {
|
||||
for (final album in albums) album.id: _repository.getOldestAssetTimestamp(album.id),
|
||||
};
|
||||
|
||||
// await all database queries
|
||||
final entries = await Future.wait(
|
||||
assetTimestampFutures.entries.map((entry) async => MapEntry(entry.key, await entry.value)),
|
||||
);
|
||||
final assetTimestamps = Map.fromEntries(entries);
|
||||
|
||||
final sorted = albums.sorted((a, b) {
|
||||
final aDate = assetTimestamps[a.id] ?? DateTime.fromMillisecondsSinceEpoch(0);
|
||||
final bDate = assetTimestamps[b.id] ?? DateTime.fromMillisecondsSinceEpoch(0);
|
||||
return aDate.compareTo(bDate);
|
||||
});
|
||||
|
||||
return sorted;
|
||||
return sortedAlbums;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,12 +68,12 @@ class SyncStreamService {
|
||||
return false;
|
||||
}
|
||||
|
||||
final semVer = SemVer(major: serverVersion.major, minor: serverVersion.minor, patch: serverVersion.patch_);
|
||||
final serverSemVer = SemVer(major: serverVersion.major, minor: serverVersion.minor, patch: serverVersion.patch_);
|
||||
|
||||
final value = Store.get(StoreKey.syncMigrationStatus, "[]");
|
||||
final migrations = (jsonDecode(value) as List).cast<String>();
|
||||
int previousLength = migrations.length;
|
||||
await _runPreSyncTasks(migrations, semVer);
|
||||
await _runPreSyncTasks(migrations, serverSemVer);
|
||||
|
||||
if (migrations.length != previousLength) {
|
||||
_logger.info("Updated pre-sync migration status: $migrations");
|
||||
@@ -82,10 +82,14 @@ class SyncStreamService {
|
||||
|
||||
// Start the sync stream and handle events
|
||||
bool shouldReset = false;
|
||||
await _syncApiRepository.streamChanges(_handleEvents, onReset: () => shouldReset = true);
|
||||
await _syncApiRepository.streamChanges(
|
||||
_handleEvents,
|
||||
serverVersion: serverSemVer,
|
||||
onReset: () => shouldReset = true,
|
||||
);
|
||||
if (shouldReset) {
|
||||
_logger.info("Resetting sync state as requested by server");
|
||||
await _syncApiRepository.streamChanges(_handleEvents);
|
||||
await _syncApiRepository.streamChanges(_handleEvents, serverVersion: serverSemVer);
|
||||
}
|
||||
|
||||
previousLength = migrations.length;
|
||||
@@ -282,6 +286,8 @@ class SyncStreamService {
|
||||
return _syncStreamRepository.deletePeopleV1(data.cast());
|
||||
case SyncEntityType.assetFaceV1:
|
||||
return _syncStreamRepository.updateAssetFacesV1(data.cast());
|
||||
case SyncEntityType.assetFaceV2:
|
||||
return _syncStreamRepository.updateAssetFacesV2(data.cast());
|
||||
case SyncEntityType.assetFaceDeleteV1:
|
||||
return _syncStreamRepository.deleteAssetFacesV1(data.cast());
|
||||
default:
|
||||
|
||||
@@ -183,8 +183,8 @@ class TimelineService {
|
||||
return _buffer.slice(start, start + count);
|
||||
}
|
||||
|
||||
// Pre-cache assets around the given index for asset viewer
|
||||
Future<void> preCacheAssets(int index) => _mutex.run(() => _loadAssets(index, math.min(5, _totalAssets - index)));
|
||||
// Preload assets around the given index for asset viewer
|
||||
Future<void> preloadAssets(int index) => _mutex.run(() => _loadAssets(index, math.min(5, _totalAssets - index)));
|
||||
|
||||
BaseAsset getRandomAsset() => _buffer.elementAt(math.Random().nextInt(_buffer.length));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user