fix: TimelineAsset visibility (#18395)

* fix: TimelineAsset visibility

* fix enum values
This commit is contained in:
Alex 2025-05-20 10:53:34 -05:00 committed by GitHub
parent 895e0eacfe
commit bdf19ce331
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 27 additions and 120 deletions

View File

@ -554,15 +554,15 @@ class Asset {
}""";
}
static getVisibility(AssetResponseDtoVisibilityEnum visibility) {
static getVisibility(AssetVisibility visibility) {
switch (visibility) {
case AssetResponseDtoVisibilityEnum.timeline:
case AssetVisibility.timeline:
return AssetVisibilityEnum.timeline;
case AssetResponseDtoVisibilityEnum.archive:
case AssetVisibility.archive:
return AssetVisibilityEnum.archive;
case AssetResponseDtoVisibilityEnum.hidden:
case AssetVisibility.hidden:
return AssetVisibilityEnum.hidden;
case AssetResponseDtoVisibilityEnum.locked:
case AssetVisibility.locked:
return AssetVisibilityEnum.locked;
}
}

View File

@ -29,7 +29,6 @@ dynamic upgradeDto(dynamic value, String targetType) {
case 'UserResponseDto':
if (value is Map) {
addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String());
addDefault(value, 'visibility', AssetVisibility.timeline);
}
break;
case 'AssetResponseDto':

View File

@ -133,7 +133,7 @@ class AssetResponseDto {
DateTime updatedAt;
AssetResponseDtoVisibilityEnum visibility;
AssetVisibility visibility;
@override
bool operator ==(Object other) => identical(this, other) || other is AssetResponseDto &&
@ -318,7 +318,7 @@ class AssetResponseDto {
type: AssetTypeEnum.fromJson(json[r'type'])!,
unassignedFaces: AssetFaceWithoutPersonResponseDto.listFromJson(json[r'unassignedFaces']),
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
visibility: AssetResponseDtoVisibilityEnum.fromJson(json[r'visibility'])!,
visibility: AssetVisibility.fromJson(json[r'visibility'])!,
);
}
return null;
@ -389,83 +389,3 @@ class AssetResponseDto {
};
}
class AssetResponseDtoVisibilityEnum {
/// Instantiate a new enum with the provided [value].
const AssetResponseDtoVisibilityEnum._(this.value);
/// The underlying value of this enum member.
final String value;
@override
String toString() => value;
String toJson() => value;
static const archive = AssetResponseDtoVisibilityEnum._(r'archive');
static const timeline = AssetResponseDtoVisibilityEnum._(r'timeline');
static const hidden = AssetResponseDtoVisibilityEnum._(r'hidden');
static const locked = AssetResponseDtoVisibilityEnum._(r'locked');
/// List of all possible values in this [enum][AssetResponseDtoVisibilityEnum].
static const values = <AssetResponseDtoVisibilityEnum>[
archive,
timeline,
hidden,
locked,
];
static AssetResponseDtoVisibilityEnum? fromJson(dynamic value) => AssetResponseDtoVisibilityEnumTypeTransformer().decode(value);
static List<AssetResponseDtoVisibilityEnum> listFromJson(dynamic json, {bool growable = false,}) {
final result = <AssetResponseDtoVisibilityEnum>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = AssetResponseDtoVisibilityEnum.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
}
/// Transformation class that can [encode] an instance of [AssetResponseDtoVisibilityEnum] to String,
/// and [decode] dynamic data back to [AssetResponseDtoVisibilityEnum].
class AssetResponseDtoVisibilityEnumTypeTransformer {
factory AssetResponseDtoVisibilityEnumTypeTransformer() => _instance ??= const AssetResponseDtoVisibilityEnumTypeTransformer._();
const AssetResponseDtoVisibilityEnumTypeTransformer._();
String encode(AssetResponseDtoVisibilityEnum data) => data.value;
/// Decodes a [dynamic value][data] to a AssetResponseDtoVisibilityEnum.
///
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
///
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
/// and users are still using an old app with the old code.
AssetResponseDtoVisibilityEnum? decode(dynamic data, {bool allowNull = true}) {
if (data != null) {
switch (data) {
case r'archive': return AssetResponseDtoVisibilityEnum.archive;
case r'timeline': return AssetResponseDtoVisibilityEnum.timeline;
case r'hidden': return AssetResponseDtoVisibilityEnum.hidden;
case r'locked': return AssetResponseDtoVisibilityEnum.locked;
default:
if (!allowNull) {
throw ArgumentError('Unknown enum value to decode: $data');
}
}
}
return null;
}
/// Singleton [AssetResponseDtoVisibilityEnumTypeTransformer] instance.
static AssetResponseDtoVisibilityEnumTypeTransformer? _instance;
}

View File

@ -9289,13 +9289,11 @@
"type": "string"
},
"visibility": {
"enum": [
"archive",
"timeline",
"hidden",
"locked"
],
"type": "string"
"allOf": [
{
"$ref": "#/components/schemas/AssetVisibility"
}
]
}
},
"required": [

View File

@ -329,7 +329,7 @@ export type AssetResponseDto = {
"type": AssetTypeEnum;
unassignedFaces?: AssetFaceWithoutPersonResponseDto[];
updatedAt: string;
visibility: Visibility;
visibility: AssetVisibility;
};
export type AlbumResponseDto = {
albumName: string;
@ -3675,12 +3675,6 @@ export enum AssetTypeEnum {
Audio = "AUDIO",
Other = "OTHER"
}
export enum Visibility {
Archive = "archive",
Timeline = "timeline",
Hidden = "hidden",
Locked = "locked"
}
export enum AssetOrder {
Asc = "asc",
Desc = "desc"

View File

@ -44,6 +44,7 @@ export class AssetResponseDto extends SanitizedAssetResponseDto {
isArchived!: boolean;
isTrashed!: boolean;
isOffline!: boolean;
@ApiProperty({ enum: AssetVisibility, enumName: 'AssetVisibility' })
visibility!: AssetVisibility;
exifInfo?: ExifResponseDto;
tags?: TagResponseDto[];

View File

@ -6,7 +6,7 @@
import type { TimelineAsset } from '$lib/stores/assets-store.svelte';
import { handleError } from '$lib/utils/handle-error';
import { AssetVisibility, updateAssets } from '@immich/sdk';
import { mdiEyeOffOutline, mdiFolderMoveOutline } from '@mdi/js';
import { mdiLockOpenVariantOutline, mdiLockOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
import type { OnAction, PreAction } from './action';
@ -57,5 +57,5 @@
<MenuOption
onClick={() => toggleLockedVisibility()}
text={isLocked ? $t('move_off_locked_folder') : $t('add_to_locked_folder')}
icon={isLocked ? mdiFolderMoveOutline : mdiEyeOffOutline}
icon={isLocked ? mdiLockOpenVariantOutline : mdiLockOutline}
/>

View File

@ -29,7 +29,7 @@
import {
AssetJobName,
AssetTypeEnum,
Visibility,
AssetVisibility,
type AlbumResponseDto,
type AssetResponseDto,
type PersonResponseDto,
@ -94,7 +94,7 @@
const sharedLink = getSharedLink();
let isOwner = $derived($user && asset.ownerId === $user?.id);
let showDownloadButton = $derived(sharedLink ? sharedLink.allowDownload : !asset.isOffline);
let isLocked = $derived(asset.visibility === Visibility.Locked);
let isLocked = $derived(asset.visibility === AssetVisibility.Locked);
// $: showEditorButton =
// isOwner &&

View File

@ -7,7 +7,7 @@
import { handleError } from '$lib/utils/handle-error';
import { AssetVisibility, updateAssets } from '@immich/sdk';
import { Button } from '@immich/ui';
import { mdiEyeOffOutline, mdiFolderMoveOutline } from '@mdi/js';
import { mdiLockOpenVariantOutline, mdiLockOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
interface Props {
@ -56,11 +56,11 @@
<MenuOption
onClick={setLockedVisibility}
text={unlock ? $t('move_off_locked_folder') : $t('add_to_locked_folder')}
icon={unlock ? mdiFolderMoveOutline : mdiEyeOffOutline}
icon={unlock ? mdiLockOpenVariantOutline : mdiLockOutline}
/>
{:else}
<Button
leadingIcon={unlock ? mdiFolderMoveOutline : mdiEyeOffOutline}
leadingIcon={unlock ? mdiLockOpenVariantOutline : mdiLockOutline}
disabled={loading}
size="medium"
color="secondary"

View File

@ -2,7 +2,7 @@ import type { TimelineAsset } from '$lib/stores/assets-store.svelte';
import { locale } from '$lib/stores/preferences.store';
import { getAssetRatio } from '$lib/utils/asset-utils';
import { AssetTypeEnum, AssetVisibility, type AssetResponseDto } from '@immich/sdk';
import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
import { memoize } from 'lodash-es';
import { DateTime, type LocaleOptions } from 'luxon';
@ -72,6 +72,7 @@ export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset):
const city = assetResponse.exifInfo?.city;
const country = assetResponse.exifInfo?.country;
const people = assetResponse.people?.map((person) => person.name) || [];
return {
id: assetResponse.id,
ownerId: assetResponse.ownerId,
@ -79,7 +80,7 @@ export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset):
thumbhash: assetResponse.thumbhash,
localDateTime: assetResponse.localDateTime,
isFavorite: assetResponse.isFavorite,
visibility: assetResponse.isArchived ? AssetVisibility.Archive : AssetVisibility.Timeline,
visibility: assetResponse.visibility,
isTrashed: assetResponse.isTrashed,
isVideo: assetResponse.type == AssetTypeEnum.Video,
isImage: assetResponse.type == AssetTypeEnum.Image,

View File

@ -65,7 +65,7 @@
onFilled={handleUnlockSession}
/>
<Button type="button" color="secondary" onclick={() => goto(AppRoute.PHOTOS)}>Back</Button>
<Button type="button" color="secondary" onclick={() => goto(AppRoute.PHOTOS)}>{$t('cancel')}</Button>
</div>
</div>
{:else}

View File

@ -1,12 +1,6 @@
import type { TimelineAsset } from '$lib/stores/assets-store.svelte';
import { faker } from '@faker-js/faker';
import {
AssetTypeEnum,
AssetVisibility,
Visibility,
type AssetResponseDto,
type TimeBucketAssetResponseDto,
} from '@immich/sdk';
import { AssetTypeEnum, AssetVisibility, type AssetResponseDto, type TimeBucketAssetResponseDto } from '@immich/sdk';
import { Sync } from 'factory.ts';
export const assetFactory = Sync.makeFactory<AssetResponseDto>({
@ -31,7 +25,7 @@ export const assetFactory = Sync.makeFactory<AssetResponseDto>({
checksum: Sync.each(() => faker.string.alphanumeric(28)),
isOffline: Sync.each(() => faker.datatype.boolean()),
hasMetadata: Sync.each(() => faker.datatype.boolean()),
visibility: Visibility.Timeline,
visibility: AssetVisibility.Timeline,
});
export const timelineAssetFactory = Sync.makeFactory<TimelineAsset>({