diff --git a/mobile/lib/entities/asset.entity.dart b/mobile/lib/entities/asset.entity.dart
index 9119d96a63..d8d2bd23c3 100644
--- a/mobile/lib/entities/asset.entity.dart
+++ b/mobile/lib/entities/asset.entity.dart
@@ -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;
     }
   }
diff --git a/mobile/lib/utils/openapi_patching.dart b/mobile/lib/utils/openapi_patching.dart
index 1ffe05c781..7c7d9bab88 100644
--- a/mobile/lib/utils/openapi_patching.dart
+++ b/mobile/lib/utils/openapi_patching.dart
@@ -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':
diff --git a/mobile/openapi/lib/model/asset_response_dto.dart b/mobile/openapi/lib/model/asset_response_dto.dart
index 74af8bd1eb..3d85b779cc 100644
--- a/mobile/openapi/lib/model/asset_response_dto.dart
+++ b/mobile/openapi/lib/model/asset_response_dto.dart
@@ -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;
-}
-
-
diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json
index 8d21c3ef90..2a8555f82c 100644
--- a/open-api/immich-openapi-specs.json
+++ b/open-api/immich-openapi-specs.json
@@ -9289,13 +9289,11 @@
             "type": "string"
           },
           "visibility": {
-            "enum": [
-              "archive",
-              "timeline",
-              "hidden",
-              "locked"
-            ],
-            "type": "string"
+            "allOf": [
+              {
+                "$ref": "#/components/schemas/AssetVisibility"
+              }
+            ]
           }
         },
         "required": [
diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts
index 5358cdfec9..c27c9bc194 100644
--- a/open-api/typescript-sdk/src/fetch-client.ts
+++ b/open-api/typescript-sdk/src/fetch-client.ts
@@ -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"
diff --git a/server/src/dtos/asset-response.dto.ts b/server/src/dtos/asset-response.dto.ts
index 4c1f2571e8..9bbfb450b2 100644
--- a/server/src/dtos/asset-response.dto.ts
+++ b/server/src/dtos/asset-response.dto.ts
@@ -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[];
diff --git a/web/src/lib/components/asset-viewer/actions/set-visibility-action.svelte b/web/src/lib/components/asset-viewer/actions/set-visibility-action.svelte
index 91db84b172..dff470f456 100644
--- a/web/src/lib/components/asset-viewer/actions/set-visibility-action.svelte
+++ b/web/src/lib/components/asset-viewer/actions/set-visibility-action.svelte
@@ -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}
 />
diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte
index 70600e6208..19705f05b6 100644
--- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte
+++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte
@@ -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 &&
diff --git a/web/src/lib/components/photos-page/actions/set-visibility-action.svelte b/web/src/lib/components/photos-page/actions/set-visibility-action.svelte
index c11ba114ce..407a92fadc 100644
--- a/web/src/lib/components/photos-page/actions/set-visibility-action.svelte
+++ b/web/src/lib/components/photos-page/actions/set-visibility-action.svelte
@@ -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"
diff --git a/web/src/lib/utils/timeline-util.ts b/web/src/lib/utils/timeline-util.ts
index 66436940d5..fca68a6aec 100644
--- a/web/src/lib/utils/timeline-util.ts
+++ b/web/src/lib/utils/timeline-util.ts
@@ -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,
diff --git a/web/src/routes/auth/pin-prompt/+page.svelte b/web/src/routes/auth/pin-prompt/+page.svelte
index 6c541aaef0..f3edc87e33 100644
--- a/web/src/routes/auth/pin-prompt/+page.svelte
+++ b/web/src/routes/auth/pin-prompt/+page.svelte
@@ -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}
diff --git a/web/src/test-data/factories/asset-factory.ts b/web/src/test-data/factories/asset-factory.ts
index e36bec6c4e..f68c3a1a1a 100644
--- a/web/src/test-data/factories/asset-factory.ts
+++ b/web/src/test-data/factories/asset-factory.ts
@@ -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>({