This commit is contained in:
wuzihao051119 2025-06-04 19:25:50 +08:00
parent d7f445b78c
commit 02b62e5183
45 changed files with 138 additions and 186 deletions

View File

@ -1,12 +0,0 @@
enum SortOrder {
asc,
desc,
}
enum TextSearchType {
context,
filename,
description,
}
enum AssetVisibilityEnum { timeline, hidden, archive, locked }

View File

@ -1,12 +1,5 @@
part of 'base_asset.model.dart';
enum AssetVisibility {
timeline,
hidden,
archive,
locked,
}
// Model for an asset stored in the server
class Asset extends BaseAsset {
final String id;

View File

@ -1,14 +1,8 @@
import 'package:openapi/api.dart';
part 'asset.model.dart';
part 'local_asset.model.dart';
enum AssetType {
// do not change this order!
other,
image,
video,
audio,
}
sealed class BaseAsset {
final String name;
final String? checksum;

View File

@ -33,7 +33,7 @@ enum StoreKey<T> {
thumbnailCacheSize<int>._(110),
imageCacheSize<int>._(111),
albumThumbnailCacheSize<int>._(112),
selectedAlbumSortOrder<int>._(113),
selectedAlbumAssetOrder<int>._(113),
advancedTroubleshooting<bool>._(114),
logLevel<int>._(115),
preferRemoteImage<bool>._(116),

View File

@ -12,6 +12,7 @@ import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
import 'package:immich_mobile/utils/diff.dart';
import 'package:logging/logging.dart';
import 'package:platform/platform.dart';
import 'package:openapi/api.dart';
class LocalSyncService {
final ILocalAlbumRepository _localAlbumRepository;
@ -365,7 +366,7 @@ extension on Iterable<PlatformAsset> {
(e) => LocalAsset(
id: e.id,
name: e.name,
type: AssetType.values.elementAtOrNull(e.type) ?? AssetType.other,
type: AssetType.values.elementAtOrNull(e.type) ?? AssetType.OTHER,
createdAt: e.createdAt == null
? DateTime.now()
: DateTime.fromMillisecondsSinceEpoch(e.createdAt! * 1000),

View File

@ -24,7 +24,7 @@ class Album {
this.lastModifiedAssetTimestamp,
required this.shared,
required this.activityEnabled,
this.sortOrder = SortOrder.desc,
this.sortOrder = AssetOrder.desc,
});
// fields stored in DB
@ -42,7 +42,7 @@ class Album {
bool shared;
bool activityEnabled;
@enumerated
SortOrder sortOrder;
AssetOrder assetOrder;
final IsarLink<User> owner = IsarLink<User>();
final IsarLink<Asset> thumbnail = IsarLink<Asset>();
final IsarLinks<User> sharedUsers = IsarLinks<User>();
@ -160,7 +160,7 @@ class Album {
a.owner.value = await db.users.getById(dto.ownerId);
if (dto.order != null) {
a.sortOrder =
dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc;
dto.order == AssetOrder.asc ? AssetOrder.asc : AssetOrder.desc;
}
if (dto.albumThumbnailAssetId != null) {

View File

@ -26,7 +26,7 @@ class Asset {
fileModifiedAt = remote.fileModifiedAt,
updatedAt = remote.updatedAt,
durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0,
type = remote.type.toAssetType(),
type = remote.type,
fileName = remote.originalFileName,
height = remote.exifInfo?.exifImageHeight?.toInt(),
width = remote.exifInfo?.exifImageWidth?.toInt(),
@ -47,7 +47,7 @@ class Asset {
stackCount = remote.stack?.assetCount ?? 0,
stackId = remote.stack?.id,
thumbhash = remote.thumbhash,
visibility = getVisibility(remote.visibility);
visibility = remote.visibility;
Asset({
this.id = Isar.autoIncrement,
@ -73,7 +73,7 @@ class Asset {
this.stackCount = 0,
this.isOffline = false,
this.thumbhash,
this.visibility = AssetVisibilityEnum.timeline,
this.visibility = AssetVisibility.timeline,
});
@ignore
@ -177,7 +177,7 @@ class Asset {
int stackCount;
@Enumerated(EnumType.ordinal)
AssetVisibilityEnum visibility;
AssetVisibility visibility;
/// Returns null if the asset has no sync access to the exif info
@ignore
@ -210,10 +210,10 @@ class Asset {
bool get isRemote => remoteId != null;
@ignore
bool get isImage => type == AssetType.image;
bool get isImage => type == AssetType.IMAGE;
@ignore
bool get isVideo => type == AssetType.video;
bool get isVideo => type == AssetType.VIDEO;
@ignore
bool get isMotionPhoto => livePhotoVideoId != null;
@ -459,7 +459,7 @@ class Asset {
String? stackPrimaryAssetId,
int? stackCount,
String? thumbhash,
AssetVisibilityEnum? visibility,
AssetVisibility? visibility,
}) =>
Asset(
id: id ?? this.id,
@ -553,27 +553,6 @@ class Asset {
"visibility": "$visibility",
}""";
}
static getVisibility(AssetVisibility visibility) {
switch (visibility) {
case AssetVisibility.timeline:
return AssetVisibilityEnum.timeline;
case AssetVisibility.archive:
return AssetVisibilityEnum.archive;
case AssetVisibility.hidden:
return AssetVisibilityEnum.hidden;
case AssetVisibility.locked:
return AssetVisibilityEnum.locked;
}
}
}
enum AssetType {
// do not change this order!
other,
image,
video,
audio,
}
/// Describes where the information of this asset came from:

View File

@ -3,6 +3,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
import 'package:openapi/api.dart';
@TableIndex(
name: 'UQ_remote_asset_owner_checksum',

View File

@ -165,7 +165,7 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
for (final asset in data) {
final companion = RemoteAssetEntityCompanion(
name: Value(asset.originalFileName),
type: Value(asset.type.toAssetType()),
type: Value(asset.type),
createdAt: Value.absentIfNull(asset.fileCreatedAt),
updatedAt: Value.absentIfNull(asset.fileModifiedAt),
durationInSeconds: const Value(0),

View File

@ -1,6 +1,7 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
mixin AssetEntityMixin on Table {
TextColumn get name => text()();
IntColumn get type => intEnum<AssetType>()();

View File

@ -18,7 +18,7 @@ abstract interface class IAlbumApiRepository {
String? thumbnailAssetId,
String? description,
bool? activityEnabled,
SortOrder? sortOrder,
AssetOrder? sortOrder,
});
Future<void> delete(String albumId);

View File

@ -1,5 +1,6 @@
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:openapi/api.dart';
abstract interface class IAssetApiRepository {
// Future<Asset> get(String id);
@ -19,6 +20,6 @@ abstract interface class IAssetApiRepository {
Future<void> updateVisibility(
List<String> list,
AssetVisibilityEnum visibility,
AssetVisibility visibility,
);
}

View File

@ -274,7 +274,7 @@ class SearchFilter {
display.isNotInAlbum == false &&
display.isArchive == false &&
display.isFavorite == false &&
mediaType == AssetType.other;
mediaType == AssetType.OTHER;
}
SearchFilter copyWith({

View File

@ -29,7 +29,7 @@ class AlbumsPage extends HookConsumerWidget {
final albums =
ref.watch(albumProvider).where((album) => album.isRemote).toList();
final albumSortOption = ref.watch(albumSortByOptionsProvider);
final albumSortIsReverse = ref.watch(albumSortOrderProvider);
final albumSortIsReverse = ref.watch(albumAssetOrderProvider);
final sorted = albumSortOption.sortFn(albums, albumSortIsReverse);
final isGrid = useState(false);
final searchController = useTextEditingController();
@ -322,7 +322,7 @@ class SortButton extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final albumSortOption = ref.watch(albumSortByOptionsProvider);
final albumSortIsReverse = ref.watch(albumSortOrderProvider);
final albumSortIsReverse = ref.watch(albumAssetOrderProvider);
return MenuAnchor(
style: MenuStyle(
@ -360,7 +360,7 @@ class SortButton extends ConsumerWidget {
// Switch direction
if (selected) {
ref
.read(albumSortOrderProvider.notifier)
.read(albumAssetOrderProvider.notifier)
.changeSortDirection(!albumSortIsReverse);
} else {
ref

View File

@ -15,6 +15,7 @@ import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/utils/bytes_units.dart';
import 'package:immich_mobile/widgets/asset_grid/thumbnail_image.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
import 'package:openapi/api.dart';
RecursiveFolder? _findFolderInStructure(
RootFolder rootFolder,
@ -49,7 +50,7 @@ class FolderPage extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final folderState = ref.watch(folderStructureProvider);
final currentFolder = useState<RecursiveFolder?>(folder);
final sortOrder = useState<SortOrder>(SortOrder.asc);
final sortOrder = useState<AssetOrder>(AssetOrder.asc);
useEffect(
() {
@ -78,9 +79,9 @@ class FolderPage extends HookConsumerWidget {
[folderState],
);
void onToggleSortOrder() {
void onToggleAssetOrder() {
final newOrder =
sortOrder.value == SortOrder.asc ? SortOrder.desc : SortOrder.asc;
sortOrder.value == AssetOrder.asc ? AssetOrder.desc : AssetOrder.asc;
ref.read(folderStructureProvider.notifier).fetchFolders(newOrder);
@ -95,7 +96,7 @@ class FolderPage extends HookConsumerWidget {
actions: [
IconButton(
icon: const Icon(Icons.swap_vert),
onPressed: onToggleSortOrder,
onPressed: onToggleAssetOrder,
),
],
),
@ -134,13 +135,13 @@ class FolderPage extends HookConsumerWidget {
class FolderContent extends HookConsumerWidget {
final RootFolder? folder;
final RootFolder root;
final SortOrder sortOrder;
final AssetOrder sortOrder;
const FolderContent({
super.key,
this.folder,
required this.root,
this.sortOrder = SortOrder.asc,
this.sortOrder = AssetOrder.asc,
});
@override

View File

@ -128,7 +128,7 @@ class PlaceTile extends StatelessWidget {
isArchive: false,
isFavorite: false,
),
mediaType: AssetType.other,
mediaType: AssetType.OTHER,
),
),
);

View File

@ -5,7 +5,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
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/extensions/build_context_extensions.dart';
import 'package:immich_mobile/interfaces/person_api.interface.dart';
@ -23,6 +22,7 @@ import 'package:immich_mobile/widgets/search/search_filter/media_type_picker.dar
import 'package:immich_mobile/widgets/search/search_filter/people_picker.dart';
import 'package:immich_mobile/widgets/search/search_filter/search_filter_chip.dart';
import 'package:immich_mobile/widgets/search/search_filter/search_filter_utils.dart';
import 'package:openapi/api.dart';
@RoutePage()
class SearchPage extends HookConsumerWidget {
@ -47,7 +47,7 @@ class SearchPage extends HookConsumerWidget {
isArchive: false,
isFavorite: false,
),
mediaType: prefilter?.mediaType ?? AssetType.other,
mediaType: prefilter?.mediaType ?? AssetType.OTHER,
language:
"${context.locale.languageCode}-${context.locale.countryCode}",
),
@ -371,9 +371,9 @@ class SearchPage extends HookConsumerWidget {
);
mediaTypeCurrentFilterWidget.value = Text(
assetType == AssetType.image
assetType == AssetType.IMAGE
? 'image'.tr()
: assetType == AssetType.video
: assetType == AssetType.VIDEO
? 'video'.tr()
: 'all'.tr(),
style: context.textTheme.labelLarge,
@ -382,7 +382,7 @@ class SearchPage extends HookConsumerWidget {
handleClear() {
filter.value = filter.value.copyWith(
mediaType: AssetType.other,
mediaType: AssetType.OTHER,
);
mediaTypeCurrentFilterWidget.value = null;

View File

@ -1,12 +1,12 @@
import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
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/models/albums/album_search.model.dart';
import 'package:immich_mobile/services/album.service.dart';
import 'package:openapi/api.dart';
final isRefreshingRemoteAlbumProvider = StateProvider<bool>((ref) => false);
@ -113,11 +113,11 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
return albumService.setActivityStatus(album, enabled);
}
Future<Album?> toggleSortOrder(Album album) {
Future<Album?> toggleAssetOrder(Album album) {
final order =
album.sortOrder == SortOrder.asc ? SortOrder.desc : SortOrder.asc;
album.sortOrder == AssetOrder.asc ? AssetOrder.desc : AssetOrder.asc;
return albumService.updateSortOrder(album, order);
return albumService.updateAssetOrder(album, order);
}
@override

View File

@ -106,7 +106,7 @@ class AlbumSortByOptions extends _$AlbumSortByOptions {
AlbumSortMode build() {
final sortOpt = ref
.watch(appSettingsServiceProvider)
.getSetting(AppSettingsEnum.selectedAlbumSortOrder);
.getSetting(AppSettingsEnum.selectedAlbumAssetOrder);
return AlbumSortMode.values.firstWhere(
(e) => e.storeIndex == sortOpt,
orElse: () => AlbumSortMode.title,
@ -116,14 +116,14 @@ class AlbumSortByOptions extends _$AlbumSortByOptions {
void changeSortMode(AlbumSortMode sortOption) {
state = sortOption;
ref.watch(appSettingsServiceProvider).setSetting(
AppSettingsEnum.selectedAlbumSortOrder,
AppSettingsEnum.selectedAlbumAssetOrder,
sortOption.storeIndex,
);
}
}
@riverpod
class AlbumSortOrder extends _$AlbumSortOrder {
class AlbumAssetOrder extends _$AlbumAssetOrder {
@override
bool build() {
return ref

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.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';
@ -13,6 +12,7 @@ import 'package:immich_mobile/services/etag.service.dart';
import 'package:immich_mobile/services/exif.service.dart';
import 'package:immich_mobile/services/sync.service.dart';
import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
final assetProvider = StateNotifierProvider<AssetNotifier, bool>((ref) {
return AssetNotifier(
@ -174,7 +174,7 @@ class AssetNotifier extends StateNotifier<bool> {
Future<void> setLockedView(
List<Asset> selection,
AssetVisibilityEnum visibility,
AssetVisibility visibility,
) {
return _assetService.setVisibility(selection, visibility);
}

View File

@ -1,9 +1,9 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/models/folder/root_folder.model.dart';
import 'package:immich_mobile/services/folder.service.dart';
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
class FolderStructureNotifier extends StateNotifier<AsyncValue<RootFolder>> {
final FolderService _folderService;
@ -11,7 +11,7 @@ class FolderStructureNotifier extends StateNotifier<AsyncValue<RootFolder>> {
FolderStructureNotifier(this._folderService) : super(const AsyncLoading());
Future<void> fetchFolders(SortOrder order) async {
Future<void> fetchFolders(AssetOrder order) async {
try {
final folders = await _folderService.getFolderStructure(order);
state = AsyncData(folders);
@ -38,7 +38,7 @@ class FolderRenderListNotifier extends StateNotifier<AsyncValue<RenderList>> {
FolderRenderListNotifier(this._folderService, this._folder)
: super(const AsyncLoading());
Future<void> fetchAssets(SortOrder order) async {
Future<void> fetchAssets(AssetOrder order) async {
try {
final assets = await _folderService.getFolderAssets(_folder, order);
final renderList =

View File

@ -9,6 +9,7 @@ import 'package:flutter/painting.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:logging/logging.dart';
import 'package:photo_manager/photo_manager.dart' show ThumbnailSize;
import 'package:openapi/api.dart';
/// The local image provider for an asset
class ImmichLocalImageProvider extends ImageProvider<ImmichLocalImageProvider> {
@ -60,7 +61,7 @@ class ImmichLocalImageProvider extends ImageProvider<ImmichLocalImageProvider> {
}
switch (asset.type) {
case AssetType.image:
case AssetType.IMAGE:
final File? file = await local.originFile;
if (file == null) {
throw StateError("Opening file for asset ${asset.fileName} failed");
@ -68,7 +69,7 @@ class ImmichLocalImageProvider extends ImageProvider<ImmichLocalImageProvider> {
final buffer = await ui.ImmutableBuffer.fromFilePath(file.path);
yield await decode(buffer);
break;
case AssetType.video:
case AssetType.VIDEO:
final size = ThumbnailSize(width.ceil(), height.ceil());
final thumbBytes = await local.thumbnailDataWithSize(size);
if (thumbBytes == null) {

View File

@ -1,5 +1,4 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
@ -59,11 +58,11 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
String? thumbnailAssetId,
String? description,
bool? activityEnabled,
SortOrder? sortOrder,
AssetOrder? sortOrder,
}) async {
AssetOrder? order;
if (sortOrder != null) {
order = sortOrder == SortOrder.asc ? AssetOrder.asc : AssetOrder.desc;
order = sortOrder == AssetOrder.asc ? AssetOrder.asc : AssetOrder.desc;
}
final response = await checkNull(
@ -163,7 +162,7 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
startDate: dto.startDate,
endDate: dto.endDate,
activityEnabled: dto.isActivityEnabled,
sortOrder: dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc,
sortOrder: dto.order == AssetOrder.asc ? AssetOrder.asc : AssetOrder.desc,
);
album.remoteAssetCount = dto.assetCount;
album.owner.value =

View File

@ -1,5 +1,4 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
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';
@ -8,6 +7,7 @@ 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:openapi/api.dart';
import 'package:isar/isar.dart';
final assetRepositoryProvider =
@ -231,7 +231,7 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository {
.where()
.ownerIdEqualToAnyChecksum(fastHash(userId))
.filter()
.visibilityEqualTo(AssetVisibilityEnum.timeline)
.visibilityEqualTo(AssetVisibility.timeline)
.sortByFileCreatedAtDesc()
.findAll();
}
@ -242,7 +242,7 @@ class AssetRepository extends DatabaseRepository implements IAssetRepository {
.where()
.ownerIdEqualToAnyChecksum(fastHash(userId))
.filter()
.visibilityEqualTo(AssetVisibilityEnum.timeline)
.visibilityEqualTo(AssetVisibility.timeline)
.livePhotoVideoIdIsNotNull()
.findAll();
}

View File

@ -1,5 +1,4 @@
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';
@ -53,23 +52,10 @@ class AssetApiRepository extends ApiRepository implements IAssetApiRepository {
@override
Future<void> updateVisibility(
List<String> ids,
AssetVisibilityEnum visibility,
AssetVisibility visibility,
) async {
return _api.updateAssets(
AssetBulkUpdateDto(ids: ids, visibility: _mapVisibility(visibility)),
AssetBulkUpdateDto(ids: ids, visibility: visibility),
);
}
_mapVisibility(AssetVisibilityEnum visibility) {
switch (visibility) {
case AssetVisibilityEnum.timeline:
return AssetVisibility.timeline;
case AssetVisibilityEnum.hidden:
return AssetVisibility.hidden;
case AssetVisibilityEnum.locked:
return AssetVisibility.locked;
case AssetVisibilityEnum.archive:
return AssetVisibility.archive;
}
}
}

View File

@ -6,6 +6,7 @@ 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;
import 'package:openapi/api.dart';
final assetMediaRepositoryProvider = Provider((ref) => AssetMediaRepository());

View File

@ -1,5 +1,4 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
@ -8,6 +7,7 @@ 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:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
import 'package:openapi/api.dart';
import 'package:isar/isar.dart';
final timelineRepositoryProvider =
@ -46,7 +46,7 @@ class TimelineRepository extends DatabaseRepository
.ownerIdEqualToAnyChecksum(fastHash(userId))
.filter()
.isTrashedEqualTo(false)
.visibilityEqualTo(AssetVisibilityEnum.archive)
.visibilityEqualTo(AssetVisibility.archive)
.sortByFileCreatedAtDesc();
return _watchRenderList(query, GroupAssetsBy.none);
@ -60,7 +60,7 @@ class TimelineRepository extends DatabaseRepository
.filter()
.isFavoriteEqualTo(true)
.not()
.visibilityEqualTo(AssetVisibilityEnum.locked)
.visibilityEqualTo(AssetVisibility.locked)
.isTrashedEqualTo(false)
.sortByFileCreatedAtDesc();
@ -76,11 +76,11 @@ class TimelineRepository extends DatabaseRepository
.filter()
.isTrashedEqualTo(false)
.not()
.visibilityEqualTo(AssetVisibilityEnum.locked);
.visibilityEqualTo(AssetVisibility.locked);
final withSortedOption = switch (album.sortOrder) {
SortOrder.asc => query.sortByFileCreatedAt(),
SortOrder.desc => query.sortByFileCreatedAtDesc(),
final withSortedOption = switch (album.assetOrder) {
AssetOrder.asc => query.sortByFileCreatedAt(),
AssetOrder.desc => query.sortByFileCreatedAtDesc(),
};
return _watchRenderList(withSortedOption, groupAssetByOption);
@ -104,8 +104,8 @@ class TimelineRepository extends DatabaseRepository
.ownerIdEqualToAnyChecksum(fastHash(userId))
.filter()
.isTrashedEqualTo(false)
.visibilityEqualTo(AssetVisibilityEnum.timeline)
.typeEqualTo(AssetType.video)
.visibilityEqualTo(AssetVisibility.timeline)
.typeEqualTo(AssetType.VIDEO)
.sortByFileCreatedAtDesc();
return _watchRenderList(query, GroupAssetsBy.none);
@ -122,7 +122,7 @@ class TimelineRepository extends DatabaseRepository
.filter()
.isTrashedEqualTo(false)
.stackPrimaryAssetIdIsNull()
.visibilityEqualTo(AssetVisibilityEnum.timeline)
.visibilityEqualTo(AssetVisibility.timeline)
.sortByFileCreatedAtDesc();
return _watchRenderList(query, groupAssetByOption);
@ -139,7 +139,7 @@ class TimelineRepository extends DatabaseRepository
.anyOf(isarUserIds, (qb, id) => qb.ownerIdEqualToAnyChecksum(id))
.filter()
.isTrashedEqualTo(false)
.visibilityEqualTo(AssetVisibilityEnum.timeline)
.visibilityEqualTo(AssetVisibility.timeline)
.stackPrimaryAssetIdIsNull()
.sortByFileCreatedAtDesc();
return _watchRenderList(query, groupAssetByOption);
@ -160,7 +160,7 @@ class TimelineRepository extends DatabaseRepository
.remoteIdIsNotNull()
.filter()
.ownerIdEqualTo(fastHash(userId))
.visibilityEqualTo(AssetVisibilityEnum.timeline)
.visibilityEqualTo(AssetVisibility.timeline)
.isTrashedEqualTo(false)
.stackPrimaryAssetIdIsNull()
.sortByFileCreatedAtDesc();
@ -177,7 +177,7 @@ class TimelineRepository extends DatabaseRepository
.where()
.ownerIdEqualToAnyChecksum(fastHash(userId))
.filter()
.visibilityEqualTo(AssetVisibilityEnum.locked)
.visibilityEqualTo(AssetVisibility.locked)
.isTrashedEqualTo(false)
.sortByFileCreatedAtDesc();

View File

@ -5,7 +5,6 @@ import 'dart:io';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.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';
@ -30,6 +29,7 @@ import 'package:immich_mobile/services/entity.service.dart';
import 'package:immich_mobile/services/sync.service.dart';
import 'package:immich_mobile/utils/hash.dart';
import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
final albumServiceProvider = Provider(
(ref) => AlbumService(
@ -483,7 +483,7 @@ class AlbumService {
return _albumRepository.search(searchTerm, filterMode);
}
Future<Album?> updateSortOrder(Album album, SortOrder order) async {
Future<Album?> updateAssetOrder(Album album, AssetOrder order) async {
try {
final updateAlbum =
await _albumApiRepository.update(album.remoteId!, sortOrder: order);

View File

@ -55,9 +55,9 @@ enum AppSettingsEnum<T> {
"albumThumbnailCacheSize",
200,
),
selectedAlbumSortOrder<int>(
StoreKey.selectedAlbumSortOrder,
"selectedAlbumSortOrder",
selectedAlbumAssetOrder<int>(
StoreKey.selectedAlbumAssetOrder,
"selectedAlbumAssetOrder",
0,
),
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, null, false),

View File

@ -3,7 +3,6 @@ import 'dart:async';
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/interfaces/user.interface.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
@ -241,8 +240,8 @@ class AssetService {
for (var element in assets) {
element.isArchived = isArchived;
element.visibility = isArchived
? AssetVisibilityEnum.archive
: AssetVisibilityEnum.timeline;
? AssetVisibility.archive
: AssetVisibility.timeline;
}
await _syncService.upsertAssetsWithExif(assets);
@ -480,7 +479,7 @@ class AssetService {
.where((asset) => asset.storage == AssetState.merged)
.map((asset) {
asset.remoteId = null;
asset.visibility = AssetVisibilityEnum.timeline;
asset.visibility = AssetVisibility.timeline;
return asset;
})
: assets.where((asset) => asset.isRemote).map((asset) {
@ -538,7 +537,7 @@ class AssetService {
Future<void> setVisibility(
List<Asset> assets,
AssetVisibilityEnum visibility,
AssetVisibility visibility,
) async {
await _assetApiRepository.updateVisibility(
assets.map((asset) => asset.remoteId!).toList(),

View File

@ -520,10 +520,11 @@ class BackupService {
}
String _getAssetType(AssetType assetType) => switch (assetType) {
AssetType.audio => "AUDIO",
AssetType.image => "IMAGE",
AssetType.video => "VIDEO",
AssetType.other => "OTHER",
AssetType.AUDIO => "AUDIO",
AssetType.IMAGE => "IMAGE",
AssetType.VIDEO => "VIDEO",
AssetType.OTHER => "OTHER",
_ => throw Exception('Unknown AssetType value: $this'),
};
}

View File

@ -21,6 +21,7 @@ import 'package:immich_mobile/repositories/file_media.repository.dart';
import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/utils/bootstrap.dart';
import 'package:immich_mobile/utils/diff.dart';
import 'package:openapi/api.dart';
/// Finds duplicates originating from missing EXIF information
class BackupVerificationService {
@ -175,7 +176,7 @@ class BackupVerificationService {
remote.fileCreatedAt,
local.fileCreatedAt,
))) {
if (remote.type == AssetType.video) {
if (remote.type == AssetType.VIDEO) {
// it's very unlikely that a video of same length, filesize, name
// and date is wrong match. Cannot easily compare videos anyway
return true;

View File

@ -5,6 +5,7 @@ import 'package:immich_mobile/models/folder/recursive_folder.model.dart';
import 'package:immich_mobile/models/folder/root_folder.model.dart';
import 'package:immich_mobile/repositories/folder_api.repository.dart';
import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
final folderServiceProvider = Provider(
(ref) => FolderService(ref.watch(folderApiRepositoryProvider)),
@ -16,7 +17,7 @@ class FolderService {
FolderService(this._folderApiRepository);
Future<RootFolder> getFolderStructure(SortOrder order) async {
Future<RootFolder> getFolderStructure(AssetOrder order) async {
final paths = await _folderApiRepository.getAllUniquePaths();
// Create folder structure
@ -54,7 +55,7 @@ class FolderService {
);
// Sort folders based on order parameter
folderMap[parentPath]!.sort(
(a, b) => order == SortOrder.desc
(a, b) => order == AssetOrder.desc
? b.name.compareTo(a.name)
: a.name.compareTo(b.name),
);
@ -71,7 +72,7 @@ class FolderService {
folder.subfolders.addAll(folderMap[fullPath]!);
// Sort subfolders based on order parameter
folder.subfolders.sort(
(a, b) => order == SortOrder.desc
(a, b) => order == AssetOrder.desc
? b.name.compareTo(a.name)
: a.name.compareTo(b.name),
);
@ -84,7 +85,7 @@ class FolderService {
List<RecursiveFolder> rootSubfolders = folderMap['_root_'] ?? [];
// Sort root subfolders based on order parameter
rootSubfolders.sort(
(a, b) => order == SortOrder.desc
(a, b) => order == AssetOrder.desc
? b.name.compareTo(a.name)
: a.name.compareTo(b.name),
);
@ -101,7 +102,7 @@ class FolderService {
Future<List<Asset>> getFolderAssets(
RootFolder folder,
SortOrder order,
AssetOrder order,
) async {
try {
if (folder is RecursiveFolder) {
@ -110,7 +111,7 @@ class FolderService {
fullPath = fullPath[0] == '/' ? fullPath.substring(1) : fullPath;
var result = await _folderApiRepository.getAssetsForPath(fullPath);
if (order == SortOrder.desc) {
if (order == AssetOrder.desc) {
result.sort((a, b) => b.fileCreatedAt.compareTo(a.fileCreatedAt));
} else {
result.sort((a, b) => a.fileCreatedAt.compareTo(b.fileCreatedAt));

View File

@ -50,9 +50,9 @@ class SearchService {
try {
SearchResponseDto? response;
AssetType? type;
if (filter.mediaType == AssetType.image) {
if (filter.mediaType == AssetType.IMAGE) {
type = AssetType.IMAGE;
} else if (filter.mediaType == AssetType.video) {
} else if (filter.mediaType == AssetType.VIDEO) {
type = AssetType.VIDEO;
}

View File

@ -2,7 +2,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
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/extensions/asset_extensions.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
@ -15,6 +14,7 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart';
import 'package:immich_mobile/widgets/common/location_picker.dart';
import 'package:immich_mobile/widgets/common/share_dialog.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
import 'package:openapi/api.dart';
void handleShareAssets(
WidgetRef ref,
@ -162,7 +162,7 @@ Future<void> handleEditLocation(
Future<void> handleSetAssetsVisibility(
WidgetRef ref,
BuildContext context,
AssetVisibilityEnum visibility,
AssetVisibility visibility,
List<Asset> selection, {
ToastGravity toastGravity = ToastGravity.BOTTOM,
}) async {
@ -172,7 +172,7 @@ Future<void> handleSetAssetsVisibility(
.setLockedView(selection, visibility);
final assetOrAssets = selection.length > 1 ? 'assets' : 'asset';
final toastMessage = visibility == AssetVisibilityEnum.locked
final toastMessage = visibility == AssetVisibility.locked
? 'Added ${selection.length} $assetOrAssets to locked folder'
: 'Removed ${selection.length} $assetOrAssets from locked folder';
if (context.mounted) {

View File

@ -23,7 +23,7 @@ class AddToAlbumSliverList extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final albumSortMode = ref.watch(albumSortByOptionsProvider);
final albumSortIsReverse = ref.watch(albumSortOrderProvider);
final albumSortIsReverse = ref.watch(albumAssetOrderProvider);
final sortedAlbums = albumSortMode.sortFn(albums, albumSortIsReverse);
final sortedSharedAlbums =
albumSortMode.sortFn(sharedAlbums, albumSortIsReverse);

View File

@ -148,9 +148,9 @@ class AlbumViewerAppbar extends HookConsumerWidget
// }
}
void onSortOrderToggled() async {
void onAssetOrderToggled() async {
final updatedAlbum =
await ref.read(albumProvider.notifier).toggleSortOrder(album);
await ref.read(albumProvider.notifier).toggleAssetOrder(album);
if (updatedAlbum == null) {
ImmichToast.show(
@ -182,7 +182,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
),
ListTile(
leading: const Icon(Icons.swap_vert_rounded),
onTap: onSortOrderToggled,
onTap: onAssetOrderToggled,
title: const Text(
"change_display_order",
style: TextStyle(fontWeight: FontWeight.w500),

View File

@ -7,7 +7,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/extensions/collection_extensions.dart';
@ -27,6 +26,7 @@ import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'
import 'package:immich_mobile/widgets/asset_grid/control_bottom_app_bar.dart';
import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
import 'package:openapi/api.dart';
class MultiselectGrid extends HookConsumerWidget {
const MultiselectGrid({
@ -407,8 +407,8 @@ class MultiselectGrid extends HookConsumerWidget {
if (remoteAssets.isNotEmpty) {
final isInLockedView = ref.read(inLockedViewProvider);
final visibility = isInLockedView
? AssetVisibilityEnum.timeline
: AssetVisibilityEnum.locked;
? AssetVisibility.timeline
: AssetVisibility.locked;
await handleSetAssetsVisibility(
ref,

View File

@ -1,13 +1,13 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.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/models/search/search_curated_content.model.dart';
import 'package:immich_mobile/models/search/search_filter.model.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/utils/image_url_builder.dart';
import 'package:immich_mobile/widgets/search/thumbnail_with_info.dart';
import 'package:openapi/api.dart';
class ExploreGrid extends StatelessWidget {
final List<SearchCuratedContent> curatedContent;
@ -73,7 +73,7 @@ class ExploreGrid extends StatelessWidget {
isArchive: false,
isFavorite: false,
),
mediaType: AssetType.other,
mediaType: AssetType.OTHER,
),
),
);

View File

@ -1,7 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:openapi/api.dart';
class MediaTypePicker extends HookWidget {
const MediaTypePicker({super.key, required this.onSelect, this.filter});
@ -11,7 +11,7 @@ class MediaTypePicker extends HookWidget {
@override
Widget build(BuildContext context) {
final selectedMediaType = useState(filter ?? AssetType.other);
final selectedMediaType = useState(filter ?? AssetType.OTHER);
return ListView(
shrinkWrap: true,
@ -19,7 +19,7 @@ class MediaTypePicker extends HookWidget {
RadioListTile(
key: const Key("all"),
title: const Text("all").tr(),
value: AssetType.other,
value: AssetType.OTHER,
onChanged: (value) {
selectedMediaType.value = value!;
onSelect(value);
@ -29,7 +29,7 @@ class MediaTypePicker extends HookWidget {
RadioListTile(
key: const Key("image"),
title: const Text("image").tr(),
value: AssetType.image,
value: AssetType.IMAGE,
onChanged: (value) {
selectedMediaType.value = value!;
onSelect(value);
@ -39,7 +39,7 @@ class MediaTypePicker extends HookWidget {
RadioListTile(
key: const Key("video"),
title: const Text("video").tr(),
value: AssetType.video,
value: AssetType.VIDEO,
onChanged: (value) {
selectedMediaType.value = value!;
onSelect(value);

View File

@ -1,5 +1,6 @@
import 'package:immich_mobile/domain/models/exif.model.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:openapi/api.dart';
final class AssetStub {
const AssetStub._();
@ -13,7 +14,7 @@ final class AssetStub {
fileModifiedAt: DateTime(2020),
updatedAt: DateTime.now(),
durationInSeconds: 0,
type: AssetType.image,
type: AssetType.IMAGE,
fileName: "image1.jpg",
isFavorite: true,
isArchived: false,
@ -30,7 +31,7 @@ final class AssetStub {
fileModifiedAt: DateTime(2010),
updatedAt: DateTime.now(),
durationInSeconds: 60,
type: AssetType.video,
type: AssetType.VIDEO,
fileName: "image2.jpg",
isFavorite: false,
isArchived: false,
@ -46,7 +47,7 @@ final class AssetStub {
fileModifiedAt: DateTime(2025),
updatedAt: DateTime.now(),
durationInSeconds: 60,
type: AssetType.image,
type: AssetType.IMAGE,
fileName: "image3.jpg",
isFavorite: true,
isArchived: false,

View File

@ -233,7 +233,7 @@ void main() {
).thenAnswer((_) async => {});
when(
() => settingsMock.setSetting<int>(
AppSettingsEnum.selectedAlbumSortOrder,
AppSettingsEnum.selectedAlbumAssetOrder,
any(),
),
).thenAnswer((_) async => {});
@ -242,7 +242,7 @@ void main() {
test('Returns the default sort mode when none set', () {
// Returns the default value when nothing is set
when(
() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder),
() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumAssetOrder),
).thenReturn(0);
expect(container.read(albumSortByOptionsProvider), AlbumSortMode.created);
@ -251,7 +251,7 @@ void main() {
test('Returns the correct sort mode with index from Store', () {
// Returns the default value when nothing is set
when(
() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder),
() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumAssetOrder),
).thenReturn(3);
expect(
@ -267,7 +267,7 @@ void main() {
verify(
() => settingsMock.setSetting(
AppSettingsEnum.selectedAlbumSortOrder,
AppSettingsEnum.selectedAlbumAssetOrder,
AlbumSortMode.mostOldest.storeIndex,
),
);
@ -275,7 +275,7 @@ void main() {
test('Notifies listeners on state change', () {
when(
() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder),
() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumAssetOrder),
).thenReturn(0);
final listener = ListenerMock<AlbumSortMode>();
@ -306,7 +306,7 @@ void main() {
});
/// Verify the sort order provider
group('AlbumSortOrder', () {
group('AlbumAssetOrder', () {
late AppSettingsService settingsMock;
late ProviderContainer container;
@ -327,7 +327,7 @@ void main() {
).thenAnswer((_) async => {});
when(
() => settingsMock.setSetting<int>(
AppSettingsEnum.selectedAlbumSortOrder,
AppSettingsEnum.selectedAlbumAssetOrder,
any(),
),
).thenAnswer((_) async => {});
@ -338,11 +338,11 @@ void main() {
() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse),
).thenReturn(false);
expect(container.read(albumSortOrderProvider), isFalse);
expect(container.read(albumAssetOrderProvider), isFalse);
});
test('Properly saves the correct order', () {
container.read(albumSortOrderProvider.notifier).changeSortDirection(true);
container.read(albumAssetOrderProvider.notifier).changeSortDirection(true);
verify(
() => settingsMock.setSetting(
@ -365,11 +365,11 @@ void main() {
);
// false -> true
container.read(albumSortOrderProvider.notifier).changeSortDirection(true);
container.read(albumAssetOrderProvider.notifier).changeSortDirection(true);
// true -> false
container
.read(albumSortOrderProvider.notifier)
.read(albumAssetOrderProvider.notifier)
.changeSortDirection(false);
verifyInOrder([

View File

@ -4,6 +4,7 @@ import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/extensions/asset_extensions.dart';
import 'package:timezone/data/latest.dart';
import 'package:timezone/timezone.dart';
import 'package:openapi/api.dart';
ExifInfo makeExif({
DateTime? dateTimeOriginal,
@ -29,7 +30,7 @@ Asset makeAsset({
fileModifiedAt: DateTime.now(),
updatedAt: DateTime.now(),
durationInSeconds: 0,
type: AssetType.image,
type: AssetType.IMAGE,
fileName: id,
isFavorite: false,
isArchived: false,

View File

@ -1,6 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:openapi/api.dart';
void main() {
final List<Asset> testAssets = [];
@ -20,7 +21,7 @@ void main() {
fileModifiedAt: date,
updatedAt: date,
durationInSeconds: 0,
type: AssetType.image,
type: AssetType.IMAGE,
fileName: '',
isFavorite: false,
isArchived: false,

View File

@ -14,6 +14,7 @@ import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
import 'package:immich_mobile/services/sync.service.dart';
import 'package:mocktail/mocktail.dart';
import 'package:openapi/api.dart';
import '../../domain/service.mock.dart';
import '../../fixtures/asset.stub.dart';
@ -41,7 +42,7 @@ void main() {
fileModifiedAt: date,
updatedAt: date,
durationInSeconds: 0,
type: AssetType.image,
type: AssetType.IMAGE,
fileName: localId ?? remoteId ?? "",
isFavorite: false,
isArchived: false,