diff --git a/docs/docs/administration/server-commands.md b/docs/docs/administration/server-commands.md
index 2594da44b2..355ee10e39 100644
--- a/docs/docs/administration/server-commands.md
+++ b/docs/docs/administration/server-commands.md
@@ -77,7 +77,6 @@ immich-admin list-users
     deletedAt: null,
     updatedAt: 2023-09-21T15:42:28.129Z,
     oauthId: '',
-    memoriesEnabled: true
   }
 ]
 ```
diff --git a/e2e/src/api/specs/user-admin.e2e-spec.ts b/e2e/src/api/specs/user-admin.e2e-spec.ts
index ac2b3e693a..a041d98419 100644
--- a/e2e/src/api/specs/user-admin.e2e-spec.ts
+++ b/e2e/src/api/specs/user-admin.e2e-spec.ts
@@ -1,4 +1,11 @@
-import { LoginResponseDto, deleteUserAdmin, getMyUser, getUserAdmin, login } from '@immich/sdk';
+import {
+  LoginResponseDto,
+  deleteUserAdmin,
+  getMyUser,
+  getUserAdmin,
+  getUserPreferencesAdmin,
+  login,
+} from '@immich/sdk';
 import { Socket } from 'socket.io-client';
 import { createUserDto, uuidDto } from 'src/fixtures';
 import { errorDto } from 'src/responses';
@@ -103,15 +110,7 @@ describe('/admin/users', () => {
       expect(body).toEqual(errorDto.forbidden);
     });
 
-    for (const key of [
-      'password',
-      'email',
-      'name',
-      'quotaSizeInBytes',
-      'shouldChangePassword',
-      'memoriesEnabled',
-      'notify',
-    ]) {
+    for (const key of ['password', 'email', 'name', 'quotaSizeInBytes', 'shouldChangePassword', 'notify']) {
       it(`should not allow null ${key}`, async () => {
         const { status, body } = await request(app)
           .post(`/admin/users`)
@@ -139,23 +138,6 @@ describe('/admin/users', () => {
       });
       expect(status).toBe(201);
     });
-
-    it('should create a user without memories enabled', async () => {
-      const { status, body } = await request(app)
-        .post(`/admin/users`)
-        .send({
-          email: 'no-memories@immich.cloud',
-          password: 'Password123',
-          name: 'No Memories',
-          memoriesEnabled: false,
-        })
-        .set('Authorization', `Bearer ${admin.accessToken}`);
-      expect(body).toMatchObject({
-        email: 'no-memories@immich.cloud',
-        memoriesEnabled: false,
-      });
-      expect(status).toBe(201);
-    });
   });
 
   describe('PUT /admin/users/:id', () => {
@@ -173,7 +155,7 @@ describe('/admin/users', () => {
       expect(body).toEqual(errorDto.forbidden);
     });
 
-    for (const key of ['password', 'email', 'name', 'shouldChangePassword', 'memoriesEnabled']) {
+    for (const key of ['password', 'email', 'name', 'shouldChangePassword']) {
       it(`should not allow null ${key}`, async () => {
         const { status, body } = await request(app)
           .put(`/admin/users/${uuidDto.notFound}`)
@@ -221,22 +203,6 @@ describe('/admin/users', () => {
       expect(before.updatedAt).not.toEqual(body.updatedAt);
     });
 
-    it('should update memories enabled', async () => {
-      const before = await getUserAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
-      const { status, body } = await request(app)
-        .put(`/admin/users/${admin.userId}`)
-        .send({ memoriesEnabled: false })
-        .set('Authorization', `Bearer ${admin.accessToken}`);
-
-      expect(status).toBe(200);
-      expect(body).toMatchObject({
-        ...before,
-        updatedAt: expect.anything(),
-        memoriesEnabled: false,
-      });
-      expect(before.updatedAt).not.toEqual(body.updatedAt);
-    });
-
     it('should update password', async () => {
       const { status, body } = await request(app)
         .put(`/admin/users/${nonAdmin.userId}`)
@@ -254,6 +220,43 @@ describe('/admin/users', () => {
     });
   });
 
+  describe('PUT /admin/users/:id/preferences', () => {
+    it('should require authentication', async () => {
+      const { status, body } = await request(app).put(`/admin/users/${userToDelete.userId}/preferences`);
+      expect(status).toBe(401);
+      expect(body).toEqual(errorDto.unauthorized);
+    });
+
+    it('should update memories enabled', async () => {
+      const before = await getUserPreferencesAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
+      expect(before).toMatchObject({ memories: { enabled: true } });
+
+      const { status, body } = await request(app)
+        .put(`/admin/users/${admin.userId}/preferences`)
+        .send({ memories: { enabled: false } })
+        .set('Authorization', `Bearer ${admin.accessToken}`);
+
+      expect(status).toBe(200);
+      expect(body).toMatchObject({ memories: { enabled: false } });
+
+      const after = await getUserPreferencesAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
+      expect(after).toMatchObject({ memories: { enabled: false } });
+    });
+
+    it('should update the avatar color', async () => {
+      const { status, body } = await request(app)
+        .put(`/admin/users/${admin.userId}/preferences`)
+        .send({ avatar: { color: 'orange' } })
+        .set('Authorization', `Bearer ${admin.accessToken}`);
+
+      expect(status).toBe(200);
+      expect(body).toEqual({ avatar: { color: 'orange' }, memories: { enabled: false } });
+
+      const after = await getUserPreferencesAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
+      expect(after).toEqual({ avatar: { color: 'orange' }, memories: { enabled: false } });
+    });
+  });
+
   describe('DELETE /admin/users/:id', () => {
     it('should require authentication', async () => {
       const { status, body } = await request(app).delete(`/admin/users/${userToDelete.userId}`);
diff --git a/e2e/src/api/specs/user.e2e-spec.ts b/e2e/src/api/specs/user.e2e-spec.ts
index 0cc08479d3..ccf7d6dd3a 100644
--- a/e2e/src/api/specs/user.e2e-spec.ts
+++ b/e2e/src/api/specs/user.e2e-spec.ts
@@ -1,4 +1,4 @@
-import { LoginResponseDto, SharedLinkType, deleteUserAdmin, getMyUser, login } from '@immich/sdk';
+import { LoginResponseDto, SharedLinkType, deleteUserAdmin, getMyPreferences, getMyUser, login } from '@immich/sdk';
 import { createUserDto } from 'src/fixtures';
 import { errorDto } from 'src/responses';
 import { app, asBearerAuth, utils } from 'src/utils';
@@ -69,7 +69,6 @@ describe('/users', () => {
       expect(body).toMatchObject({
         id: admin.userId,
         email: 'admin@immich.cloud',
-        memoriesEnabled: true,
         quotaUsageInBytes: 0,
       });
     });
@@ -82,7 +81,7 @@ describe('/users', () => {
       expect(body).toEqual(errorDto.unauthorized);
     });
 
-    for (const key of ['email', 'name', 'memoriesEnabled', 'avatarColor']) {
+    for (const key of ['email', 'name']) {
       it(`should not allow null ${key}`, async () => {
         const dto = { [key]: null };
         const { status, body } = await request(app)
@@ -110,24 +109,6 @@ describe('/users', () => {
       });
     });
 
-    it('should update memories enabled', async () => {
-      const before = await getMyUser({ headers: asBearerAuth(admin.accessToken) });
-      const { status, body } = await request(app)
-        .put(`/users/me`)
-        .send({ memoriesEnabled: false })
-        .set('Authorization', `Bearer ${admin.accessToken}`);
-
-      expect(status).toBe(200);
-      expect(body).toMatchObject({
-        ...before,
-        updatedAt: expect.anything(),
-        memoriesEnabled: false,
-      });
-
-      const after = await getMyUser({ headers: asBearerAuth(admin.accessToken) });
-      expect(after.memoriesEnabled).toBe(false);
-    });
-
     /** @deprecated */
     it('should allow a user to change their password (deprecated)', async () => {
       const user = await getMyUser({ headers: asBearerAuth(nonAdmin.accessToken) });
@@ -176,6 +157,24 @@ describe('/users', () => {
     });
   });
 
+  describe('PUT /users/me/preferences', () => {
+    it('should update memories enabled', async () => {
+      const before = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) });
+      expect(before).toMatchObject({ memories: { enabled: true } });
+
+      const { status, body } = await request(app)
+        .put(`/users/me/preferences`)
+        .send({ memories: { enabled: false } })
+        .set('Authorization', `Bearer ${admin.accessToken}`);
+
+      expect(status).toBe(200);
+      expect(body).toMatchObject({ memories: { enabled: false } });
+
+      const after = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) });
+      expect(after).toMatchObject({ memories: { enabled: false } });
+    });
+  });
+
   describe('GET /users/:id', () => {
     it('should require authentication', async () => {
       const { status } = await request(app).get(`/users/${admin.userId}`);
@@ -194,7 +193,6 @@ describe('/users', () => {
 
       expect(body).not.toMatchObject({
         shouldChangePassword: expect.anything(),
-        memoriesEnabled: expect.anything(),
         storageLabel: expect.anything(),
       });
     });
diff --git a/e2e/src/fixtures.ts b/e2e/src/fixtures.ts
index 031985c5fb..9e311c896d 100644
--- a/e2e/src/fixtures.ts
+++ b/e2e/src/fixtures.ts
@@ -1,5 +1,3 @@
-import { UserAvatarColor } from '@immich/sdk';
-
 export const uuidDto = {
   invalid: 'invalid-uuid',
   // valid uuid v4
@@ -70,8 +68,6 @@ export const userDto = {
     updatedAt: new Date('2021-01-01'),
     tags: [],
     assets: [],
-    memoriesEnabled: true,
-    avatarColor: UserAvatarColor.Primary,
     quotaSizeInBytes: null,
     quotaUsageInBytes: 0,
   },
@@ -88,8 +84,6 @@ export const userDto = {
     updatedAt: new Date('2021-01-01'),
     tags: [],
     assets: [],
-    memoriesEnabled: true,
-    avatarColor: UserAvatarColor.Primary,
     quotaSizeInBytes: null,
     quotaUsageInBytes: 0,
   },
diff --git a/e2e/src/responses.ts b/e2e/src/responses.ts
index afe3334a7f..b7dcfca1ee 100644
--- a/e2e/src/responses.ts
+++ b/e2e/src/responses.ts
@@ -68,7 +68,6 @@ export const signupResponseDto = {
     updatedAt: expect.any(String),
     deletedAt: null,
     oauthId: '',
-    memoriesEnabled: true,
     quotaUsageInBytes: 0,
     quotaSizeInBytes: null,
     status: 'active',
diff --git a/mobile/lib/entities/user.entity.dart b/mobile/lib/entities/user.entity.dart
index b6adcf5d87..55a19fe496 100644
--- a/mobile/lib/entities/user.entity.dart
+++ b/mobile/lib/entities/user.entity.dart
@@ -27,8 +27,10 @@ class User {
 
   Id get isarId => fastHash(id);
 
-  User.fromUserDto(UserAdminResponseDto dto)
-      : id = dto.id,
+  User.fromUserDto(
+    UserAdminResponseDto dto,
+    UserPreferencesResponseDto? preferences,
+  )   : id = dto.id,
         updatedAt = dto.updatedAt,
         email = dto.email,
         name = dto.name,
@@ -36,7 +38,7 @@ class User {
         isPartnerSharedWith = false,
         profileImagePath = dto.profileImagePath,
         isAdmin = dto.isAdmin,
-        memoryEnabled = dto.memoriesEnabled ?? false,
+        memoryEnabled = preferences?.memories.enabled ?? false,
         avatarColor = dto.avatarColor.toAvatarColor(),
         inTimeline = false,
         quotaUsageInBytes = dto.quotaUsageInBytes ?? 0,
diff --git a/mobile/lib/providers/authentication.provider.dart b/mobile/lib/providers/authentication.provider.dart
index 073ee09db1..b5fb25bf20 100644
--- a/mobile/lib/providers/authentication.provider.dart
+++ b/mobile/lib/providers/authentication.provider.dart
@@ -177,8 +177,10 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
       retResult = false;
     } else {
       UserAdminResponseDto? userResponseDto;
+      UserPreferencesResponseDto? userPreferences;
       try {
         userResponseDto = await _apiService.userApi.getMyUser();
+        userPreferences = await _apiService.userApi.getMyPreferences();
       } on ApiException catch (error, stackTrace) {
         _log.severe(
           "Error getting user information from the server [API EXCEPTION]",
@@ -201,13 +203,13 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
         Store.put(StoreKey.deviceIdHash, fastHash(deviceId));
         Store.put(
           StoreKey.currentUser,
-          User.fromUserDto(userResponseDto),
+          User.fromUserDto(userResponseDto, userPreferences),
         );
         Store.put(StoreKey.serverUrl, serverUrl);
         Store.put(StoreKey.accessToken, accessToken);
 
         shouldChangePassword = userResponseDto.shouldChangePassword;
-        user = User.fromUserDto(userResponseDto);
+        user = User.fromUserDto(userResponseDto, userPreferences);
 
         retResult = true;
       } else {
diff --git a/mobile/lib/providers/user.provider.dart b/mobile/lib/providers/user.provider.dart
index bf052ebbba..2767615526 100644
--- a/mobile/lib/providers/user.provider.dart
+++ b/mobile/lib/providers/user.provider.dart
@@ -21,10 +21,11 @@ class CurrentUserProvider extends StateNotifier<User?> {
   refresh() async {
     try {
       final user = await _apiService.userApi.getMyUser();
+      final userPreferences = await _apiService.userApi.getMyPreferences();
       if (user != null) {
         Store.put(
           StoreKey.currentUser,
-          User.fromUserDto(user),
+          User.fromUserDto(user, userPreferences),
         );
       }
     } catch (_) {}
diff --git a/mobile/lib/routing/tab_navigation_observer.dart b/mobile/lib/routing/tab_navigation_observer.dart
index 8825e2ef02..6c0f36050b 100644
--- a/mobile/lib/routing/tab_navigation_observer.dart
+++ b/mobile/lib/routing/tab_navigation_observer.dart
@@ -58,6 +58,8 @@ class TabNavigationObserver extends AutoRouterObserver {
       try {
         final userResponseDto =
             await ref.read(apiServiceProvider).userApi.getMyUser();
+        final userPreferences =
+            await ref.read(apiServiceProvider).userApi.getMyPreferences();
 
         if (userResponseDto == null) {
           return;
@@ -65,7 +67,7 @@ class TabNavigationObserver extends AutoRouterObserver {
 
         Store.put(
           StoreKey.currentUser,
-          User.fromUserDto(userResponseDto),
+          User.fromUserDto(userResponseDto, userPreferences),
         );
         ref.read(serverInfoProvider.notifier).getServerVersion();
       } catch (e) {
diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md
index 273585c368..cdc75d4f28 100644
--- a/mobile/openapi/README.md
+++ b/mobile/openapi/README.md
@@ -215,15 +215,19 @@ Class | Method | HTTP request | Description
 *UserApi* | [**createUserAdmin**](doc//UserApi.md#createuseradmin) | **POST** /admin/users | 
 *UserApi* | [**deleteProfileImage**](doc//UserApi.md#deleteprofileimage) | **DELETE** /users/profile-image | 
 *UserApi* | [**deleteUserAdmin**](doc//UserApi.md#deleteuseradmin) | **DELETE** /admin/users/{id} | 
+*UserApi* | [**getMyPreferences**](doc//UserApi.md#getmypreferences) | **GET** /users/me/preferences | 
 *UserApi* | [**getMyUser**](doc//UserApi.md#getmyuser) | **GET** /users/me | 
 *UserApi* | [**getProfileImage**](doc//UserApi.md#getprofileimage) | **GET** /users/{id}/profile-image | 
 *UserApi* | [**getUser**](doc//UserApi.md#getuser) | **GET** /users/{id} | 
 *UserApi* | [**getUserAdmin**](doc//UserApi.md#getuseradmin) | **GET** /admin/users/{id} | 
+*UserApi* | [**getUserPreferencesAdmin**](doc//UserApi.md#getuserpreferencesadmin) | **GET** /admin/users/{id}/preferences | 
 *UserApi* | [**restoreUserAdmin**](doc//UserApi.md#restoreuseradmin) | **POST** /admin/users/{id}/restore | 
 *UserApi* | [**searchUsers**](doc//UserApi.md#searchusers) | **GET** /users | 
 *UserApi* | [**searchUsersAdmin**](doc//UserApi.md#searchusersadmin) | **GET** /admin/users | 
+*UserApi* | [**updateMyPreferences**](doc//UserApi.md#updatemypreferences) | **PUT** /users/me/preferences | 
 *UserApi* | [**updateMyUser**](doc//UserApi.md#updatemyuser) | **PUT** /users/me | 
 *UserApi* | [**updateUserAdmin**](doc//UserApi.md#updateuseradmin) | **PUT** /admin/users/{id} | 
+*UserApi* | [**updateUserPreferencesAdmin**](doc//UserApi.md#updateuserpreferencesadmin) | **PUT** /admin/users/{id}/preferences | 
 
 
 ## Documentation For Models
@@ -270,6 +274,8 @@ Class | Method | HTTP request | Description
  - [AssetTypeEnum](doc//AssetTypeEnum.md)
  - [AudioCodec](doc//AudioCodec.md)
  - [AuditDeletesResponseDto](doc//AuditDeletesResponseDto.md)
+ - [AvatarResponse](doc//AvatarResponse.md)
+ - [AvatarUpdate](doc//AvatarUpdate.md)
  - [BulkIdResponseDto](doc//BulkIdResponseDto.md)
  - [BulkIdsDto](doc//BulkIdsDto.md)
  - [CLIPConfig](doc//CLIPConfig.md)
@@ -313,8 +319,10 @@ Class | Method | HTTP request | Description
  - [MapTheme](doc//MapTheme.md)
  - [MemoryCreateDto](doc//MemoryCreateDto.md)
  - [MemoryLaneResponseDto](doc//MemoryLaneResponseDto.md)
+ - [MemoryResponse](doc//MemoryResponse.md)
  - [MemoryResponseDto](doc//MemoryResponseDto.md)
  - [MemoryType](doc//MemoryType.md)
+ - [MemoryUpdate](doc//MemoryUpdate.md)
  - [MemoryUpdateDto](doc//MemoryUpdateDto.md)
  - [MergePersonDto](doc//MergePersonDto.md)
  - [MetadataSearchDto](doc//MetadataSearchDto.md)
@@ -409,6 +417,8 @@ Class | Method | HTTP request | Description
  - [UserAdminResponseDto](doc//UserAdminResponseDto.md)
  - [UserAdminUpdateDto](doc//UserAdminUpdateDto.md)
  - [UserAvatarColor](doc//UserAvatarColor.md)
+ - [UserPreferencesResponseDto](doc//UserPreferencesResponseDto.md)
+ - [UserPreferencesUpdateDto](doc//UserPreferencesUpdateDto.md)
  - [UserResponseDto](doc//UserResponseDto.md)
  - [UserStatus](doc//UserStatus.md)
  - [UserUpdateMeDto](doc//UserUpdateMeDto.md)
diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart
index d7223a1ecf..94303a768f 100644
--- a/mobile/openapi/lib/api.dart
+++ b/mobile/openapi/lib/api.dart
@@ -99,6 +99,8 @@ part 'model/asset_stats_response_dto.dart';
 part 'model/asset_type_enum.dart';
 part 'model/audio_codec.dart';
 part 'model/audit_deletes_response_dto.dart';
+part 'model/avatar_response.dart';
+part 'model/avatar_update.dart';
 part 'model/bulk_id_response_dto.dart';
 part 'model/bulk_ids_dto.dart';
 part 'model/clip_config.dart';
@@ -142,8 +144,10 @@ part 'model/map_marker_response_dto.dart';
 part 'model/map_theme.dart';
 part 'model/memory_create_dto.dart';
 part 'model/memory_lane_response_dto.dart';
+part 'model/memory_response.dart';
 part 'model/memory_response_dto.dart';
 part 'model/memory_type.dart';
+part 'model/memory_update.dart';
 part 'model/memory_update_dto.dart';
 part 'model/merge_person_dto.dart';
 part 'model/metadata_search_dto.dart';
@@ -238,6 +242,8 @@ part 'model/user_admin_delete_dto.dart';
 part 'model/user_admin_response_dto.dart';
 part 'model/user_admin_update_dto.dart';
 part 'model/user_avatar_color.dart';
+part 'model/user_preferences_response_dto.dart';
+part 'model/user_preferences_update_dto.dart';
 part 'model/user_response_dto.dart';
 part 'model/user_status.dart';
 part 'model/user_update_me_dto.dart';
diff --git a/mobile/openapi/lib/api/user_api.dart b/mobile/openapi/lib/api/user_api.dart
index 3c1a3ff4e7..246ea422c5 100644
--- a/mobile/openapi/lib/api/user_api.dart
+++ b/mobile/openapi/lib/api/user_api.dart
@@ -205,6 +205,47 @@ class UserApi {
     return null;
   }
 
+  /// Performs an HTTP 'GET /users/me/preferences' operation and returns the [Response].
+  Future<Response> getMyPreferencesWithHttpInfo() async {
+    // ignore: prefer_const_declarations
+    final path = r'/users/me/preferences';
+
+    // ignore: prefer_final_locals
+    Object? postBody;
+
+    final queryParams = <QueryParam>[];
+    final headerParams = <String, String>{};
+    final formParams = <String, String>{};
+
+    const contentTypes = <String>[];
+
+
+    return apiClient.invokeAPI(
+      path,
+      'GET',
+      queryParams,
+      postBody,
+      headerParams,
+      formParams,
+      contentTypes.isEmpty ? null : contentTypes.first,
+    );
+  }
+
+  Future<UserPreferencesResponseDto?> getMyPreferences() async {
+    final response = await getMyPreferencesWithHttpInfo();
+    if (response.statusCode >= HttpStatus.badRequest) {
+      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
+    }
+    // When a remote server returns no body with a status of 204, we shall not decode it.
+    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
+    // FormatException when trying to decode an empty string.
+    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
+      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserPreferencesResponseDto',) as UserPreferencesResponseDto;
+    
+    }
+    return null;
+  }
+
   /// Performs an HTTP 'GET /users/me' operation and returns the [Response].
   Future<Response> getMyUserWithHttpInfo() async {
     // ignore: prefer_const_declarations
@@ -390,6 +431,54 @@ class UserApi {
     return null;
   }
 
+  /// Performs an HTTP 'GET /admin/users/{id}/preferences' operation and returns the [Response].
+  /// Parameters:
+  ///
+  /// * [String] id (required):
+  Future<Response> getUserPreferencesAdminWithHttpInfo(String id,) async {
+    // ignore: prefer_const_declarations
+    final path = r'/admin/users/{id}/preferences'
+      .replaceAll('{id}', id);
+
+    // ignore: prefer_final_locals
+    Object? postBody;
+
+    final queryParams = <QueryParam>[];
+    final headerParams = <String, String>{};
+    final formParams = <String, String>{};
+
+    const contentTypes = <String>[];
+
+
+    return apiClient.invokeAPI(
+      path,
+      'GET',
+      queryParams,
+      postBody,
+      headerParams,
+      formParams,
+      contentTypes.isEmpty ? null : contentTypes.first,
+    );
+  }
+
+  /// Parameters:
+  ///
+  /// * [String] id (required):
+  Future<UserPreferencesResponseDto?> getUserPreferencesAdmin(String id,) async {
+    final response = await getUserPreferencesAdminWithHttpInfo(id,);
+    if (response.statusCode >= HttpStatus.badRequest) {
+      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
+    }
+    // When a remote server returns no body with a status of 204, we shall not decode it.
+    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
+    // FormatException when trying to decode an empty string.
+    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
+      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserPreferencesResponseDto',) as UserPreferencesResponseDto;
+    
+    }
+    return null;
+  }
+
   /// Performs an HTTP 'POST /admin/users/{id}/restore' operation and returns the [Response].
   /// Parameters:
   ///
@@ -536,6 +625,53 @@ class UserApi {
     return null;
   }
 
+  /// Performs an HTTP 'PUT /users/me/preferences' operation and returns the [Response].
+  /// Parameters:
+  ///
+  /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required):
+  Future<Response> updateMyPreferencesWithHttpInfo(UserPreferencesUpdateDto userPreferencesUpdateDto,) async {
+    // ignore: prefer_const_declarations
+    final path = r'/users/me/preferences';
+
+    // ignore: prefer_final_locals
+    Object? postBody = userPreferencesUpdateDto;
+
+    final queryParams = <QueryParam>[];
+    final headerParams = <String, String>{};
+    final formParams = <String, String>{};
+
+    const contentTypes = <String>['application/json'];
+
+
+    return apiClient.invokeAPI(
+      path,
+      'PUT',
+      queryParams,
+      postBody,
+      headerParams,
+      formParams,
+      contentTypes.isEmpty ? null : contentTypes.first,
+    );
+  }
+
+  /// Parameters:
+  ///
+  /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required):
+  Future<UserPreferencesResponseDto?> updateMyPreferences(UserPreferencesUpdateDto userPreferencesUpdateDto,) async {
+    final response = await updateMyPreferencesWithHttpInfo(userPreferencesUpdateDto,);
+    if (response.statusCode >= HttpStatus.badRequest) {
+      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
+    }
+    // When a remote server returns no body with a status of 204, we shall not decode it.
+    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
+    // FormatException when trying to decode an empty string.
+    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
+      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserPreferencesResponseDto',) as UserPreferencesResponseDto;
+    
+    }
+    return null;
+  }
+
   /// Performs an HTTP 'PUT /users/me' operation and returns the [Response].
   /// Parameters:
   ///
@@ -634,4 +770,56 @@ class UserApi {
     }
     return null;
   }
+
+  /// Performs an HTTP 'PUT /admin/users/{id}/preferences' operation and returns the [Response].
+  /// Parameters:
+  ///
+  /// * [String] id (required):
+  ///
+  /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required):
+  Future<Response> updateUserPreferencesAdminWithHttpInfo(String id, UserPreferencesUpdateDto userPreferencesUpdateDto,) async {
+    // ignore: prefer_const_declarations
+    final path = r'/admin/users/{id}/preferences'
+      .replaceAll('{id}', id);
+
+    // ignore: prefer_final_locals
+    Object? postBody = userPreferencesUpdateDto;
+
+    final queryParams = <QueryParam>[];
+    final headerParams = <String, String>{};
+    final formParams = <String, String>{};
+
+    const contentTypes = <String>['application/json'];
+
+
+    return apiClient.invokeAPI(
+      path,
+      'PUT',
+      queryParams,
+      postBody,
+      headerParams,
+      formParams,
+      contentTypes.isEmpty ? null : contentTypes.first,
+    );
+  }
+
+  /// Parameters:
+  ///
+  /// * [String] id (required):
+  ///
+  /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required):
+  Future<UserPreferencesResponseDto?> updateUserPreferencesAdmin(String id, UserPreferencesUpdateDto userPreferencesUpdateDto,) async {
+    final response = await updateUserPreferencesAdminWithHttpInfo(id, userPreferencesUpdateDto,);
+    if (response.statusCode >= HttpStatus.badRequest) {
+      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
+    }
+    // When a remote server returns no body with a status of 204, we shall not decode it.
+    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
+    // FormatException when trying to decode an empty string.
+    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
+      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserPreferencesResponseDto',) as UserPreferencesResponseDto;
+    
+    }
+    return null;
+  }
 }
diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart
index bd3433872a..bf306ac10a 100644
--- a/mobile/openapi/lib/api_client.dart
+++ b/mobile/openapi/lib/api_client.dart
@@ -266,6 +266,10 @@ class ApiClient {
           return AudioCodecTypeTransformer().decode(value);
         case 'AuditDeletesResponseDto':
           return AuditDeletesResponseDto.fromJson(value);
+        case 'AvatarResponse':
+          return AvatarResponse.fromJson(value);
+        case 'AvatarUpdate':
+          return AvatarUpdate.fromJson(value);
         case 'BulkIdResponseDto':
           return BulkIdResponseDto.fromJson(value);
         case 'BulkIdsDto':
@@ -352,10 +356,14 @@ class ApiClient {
           return MemoryCreateDto.fromJson(value);
         case 'MemoryLaneResponseDto':
           return MemoryLaneResponseDto.fromJson(value);
+        case 'MemoryResponse':
+          return MemoryResponse.fromJson(value);
         case 'MemoryResponseDto':
           return MemoryResponseDto.fromJson(value);
         case 'MemoryType':
           return MemoryTypeTypeTransformer().decode(value);
+        case 'MemoryUpdate':
+          return MemoryUpdate.fromJson(value);
         case 'MemoryUpdateDto':
           return MemoryUpdateDto.fromJson(value);
         case 'MergePersonDto':
@@ -544,6 +552,10 @@ class ApiClient {
           return UserAdminUpdateDto.fromJson(value);
         case 'UserAvatarColor':
           return UserAvatarColorTypeTransformer().decode(value);
+        case 'UserPreferencesResponseDto':
+          return UserPreferencesResponseDto.fromJson(value);
+        case 'UserPreferencesUpdateDto':
+          return UserPreferencesUpdateDto.fromJson(value);
         case 'UserResponseDto':
           return UserResponseDto.fromJson(value);
         case 'UserStatus':
diff --git a/mobile/openapi/lib/model/avatar_response.dart b/mobile/openapi/lib/model/avatar_response.dart
new file mode 100644
index 0000000000..edd242df4e
--- /dev/null
+++ b/mobile/openapi/lib/model/avatar_response.dart
@@ -0,0 +1,98 @@
+//
+// AUTO-GENERATED FILE, DO NOT MODIFY!
+//
+// @dart=2.18
+
+// ignore_for_file: unused_element, unused_import
+// ignore_for_file: always_put_required_named_parameters_first
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: lines_longer_than_80_chars
+
+part of openapi.api;
+
+class AvatarResponse {
+  /// Returns a new [AvatarResponse] instance.
+  AvatarResponse({
+    required this.color,
+  });
+
+  UserAvatarColor color;
+
+  @override
+  bool operator ==(Object other) => identical(this, other) || other is AvatarResponse &&
+    other.color == color;
+
+  @override
+  int get hashCode =>
+    // ignore: unnecessary_parenthesis
+    (color.hashCode);
+
+  @override
+  String toString() => 'AvatarResponse[color=$color]';
+
+  Map<String, dynamic> toJson() {
+    final json = <String, dynamic>{};
+      json[r'color'] = this.color;
+    return json;
+  }
+
+  /// Returns a new [AvatarResponse] instance and imports its values from
+  /// [value] if it's a [Map], null otherwise.
+  // ignore: prefer_constructors_over_static_methods
+  static AvatarResponse? fromJson(dynamic value) {
+    if (value is Map) {
+      final json = value.cast<String, dynamic>();
+
+      return AvatarResponse(
+        color: UserAvatarColor.fromJson(json[r'color'])!,
+      );
+    }
+    return null;
+  }
+
+  static List<AvatarResponse> listFromJson(dynamic json, {bool growable = false,}) {
+    final result = <AvatarResponse>[];
+    if (json is List && json.isNotEmpty) {
+      for (final row in json) {
+        final value = AvatarResponse.fromJson(row);
+        if (value != null) {
+          result.add(value);
+        }
+      }
+    }
+    return result.toList(growable: growable);
+  }
+
+  static Map<String, AvatarResponse> mapFromJson(dynamic json) {
+    final map = <String, AvatarResponse>{};
+    if (json is Map && json.isNotEmpty) {
+      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
+      for (final entry in json.entries) {
+        final value = AvatarResponse.fromJson(entry.value);
+        if (value != null) {
+          map[entry.key] = value;
+        }
+      }
+    }
+    return map;
+  }
+
+  // maps a json object with a list of AvatarResponse-objects as value to a dart map
+  static Map<String, List<AvatarResponse>> mapListFromJson(dynamic json, {bool growable = false,}) {
+    final map = <String, List<AvatarResponse>>{};
+    if (json is Map && json.isNotEmpty) {
+      // ignore: parameter_assignments
+      json = json.cast<String, dynamic>();
+      for (final entry in json.entries) {
+        map[entry.key] = AvatarResponse.listFromJson(entry.value, growable: growable,);
+      }
+    }
+    return map;
+  }
+
+  /// The list of required keys that must be present in a JSON.
+  static const requiredKeys = <String>{
+    'color',
+  };
+}
+
diff --git a/mobile/openapi/lib/model/avatar_update.dart b/mobile/openapi/lib/model/avatar_update.dart
new file mode 100644
index 0000000000..b92eb8dcbd
--- /dev/null
+++ b/mobile/openapi/lib/model/avatar_update.dart
@@ -0,0 +1,107 @@
+//
+// AUTO-GENERATED FILE, DO NOT MODIFY!
+//
+// @dart=2.18
+
+// ignore_for_file: unused_element, unused_import
+// ignore_for_file: always_put_required_named_parameters_first
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: lines_longer_than_80_chars
+
+part of openapi.api;
+
+class AvatarUpdate {
+  /// Returns a new [AvatarUpdate] instance.
+  AvatarUpdate({
+    this.color,
+  });
+
+  ///
+  /// Please note: This property should have been non-nullable! Since the specification file
+  /// does not include a default value (using the "default:" property), however, the generated
+  /// source code must fall back to having a nullable type.
+  /// Consider adding a "default:" property in the specification file to hide this note.
+  ///
+  UserAvatarColor? color;
+
+  @override
+  bool operator ==(Object other) => identical(this, other) || other is AvatarUpdate &&
+    other.color == color;
+
+  @override
+  int get hashCode =>
+    // ignore: unnecessary_parenthesis
+    (color == null ? 0 : color!.hashCode);
+
+  @override
+  String toString() => 'AvatarUpdate[color=$color]';
+
+  Map<String, dynamic> toJson() {
+    final json = <String, dynamic>{};
+    if (this.color != null) {
+      json[r'color'] = this.color;
+    } else {
+    //  json[r'color'] = null;
+    }
+    return json;
+  }
+
+  /// Returns a new [AvatarUpdate] instance and imports its values from
+  /// [value] if it's a [Map], null otherwise.
+  // ignore: prefer_constructors_over_static_methods
+  static AvatarUpdate? fromJson(dynamic value) {
+    if (value is Map) {
+      final json = value.cast<String, dynamic>();
+
+      return AvatarUpdate(
+        color: UserAvatarColor.fromJson(json[r'color']),
+      );
+    }
+    return null;
+  }
+
+  static List<AvatarUpdate> listFromJson(dynamic json, {bool growable = false,}) {
+    final result = <AvatarUpdate>[];
+    if (json is List && json.isNotEmpty) {
+      for (final row in json) {
+        final value = AvatarUpdate.fromJson(row);
+        if (value != null) {
+          result.add(value);
+        }
+      }
+    }
+    return result.toList(growable: growable);
+  }
+
+  static Map<String, AvatarUpdate> mapFromJson(dynamic json) {
+    final map = <String, AvatarUpdate>{};
+    if (json is Map && json.isNotEmpty) {
+      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
+      for (final entry in json.entries) {
+        final value = AvatarUpdate.fromJson(entry.value);
+        if (value != null) {
+          map[entry.key] = value;
+        }
+      }
+    }
+    return map;
+  }
+
+  // maps a json object with a list of AvatarUpdate-objects as value to a dart map
+  static Map<String, List<AvatarUpdate>> mapListFromJson(dynamic json, {bool growable = false,}) {
+    final map = <String, List<AvatarUpdate>>{};
+    if (json is Map && json.isNotEmpty) {
+      // ignore: parameter_assignments
+      json = json.cast<String, dynamic>();
+      for (final entry in json.entries) {
+        map[entry.key] = AvatarUpdate.listFromJson(entry.value, growable: growable,);
+      }
+    }
+    return map;
+  }
+
+  /// The list of required keys that must be present in a JSON.
+  static const requiredKeys = <String>{
+  };
+}
+
diff --git a/mobile/openapi/lib/model/memory_response.dart b/mobile/openapi/lib/model/memory_response.dart
new file mode 100644
index 0000000000..fb34bc1518
--- /dev/null
+++ b/mobile/openapi/lib/model/memory_response.dart
@@ -0,0 +1,98 @@
+//
+// AUTO-GENERATED FILE, DO NOT MODIFY!
+//
+// @dart=2.18
+
+// ignore_for_file: unused_element, unused_import
+// ignore_for_file: always_put_required_named_parameters_first
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: lines_longer_than_80_chars
+
+part of openapi.api;
+
+class MemoryResponse {
+  /// Returns a new [MemoryResponse] instance.
+  MemoryResponse({
+    required this.enabled,
+  });
+
+  bool enabled;
+
+  @override
+  bool operator ==(Object other) => identical(this, other) || other is MemoryResponse &&
+    other.enabled == enabled;
+
+  @override
+  int get hashCode =>
+    // ignore: unnecessary_parenthesis
+    (enabled.hashCode);
+
+  @override
+  String toString() => 'MemoryResponse[enabled=$enabled]';
+
+  Map<String, dynamic> toJson() {
+    final json = <String, dynamic>{};
+      json[r'enabled'] = this.enabled;
+    return json;
+  }
+
+  /// Returns a new [MemoryResponse] instance and imports its values from
+  /// [value] if it's a [Map], null otherwise.
+  // ignore: prefer_constructors_over_static_methods
+  static MemoryResponse? fromJson(dynamic value) {
+    if (value is Map) {
+      final json = value.cast<String, dynamic>();
+
+      return MemoryResponse(
+        enabled: mapValueOfType<bool>(json, r'enabled')!,
+      );
+    }
+    return null;
+  }
+
+  static List<MemoryResponse> listFromJson(dynamic json, {bool growable = false,}) {
+    final result = <MemoryResponse>[];
+    if (json is List && json.isNotEmpty) {
+      for (final row in json) {
+        final value = MemoryResponse.fromJson(row);
+        if (value != null) {
+          result.add(value);
+        }
+      }
+    }
+    return result.toList(growable: growable);
+  }
+
+  static Map<String, MemoryResponse> mapFromJson(dynamic json) {
+    final map = <String, MemoryResponse>{};
+    if (json is Map && json.isNotEmpty) {
+      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
+      for (final entry in json.entries) {
+        final value = MemoryResponse.fromJson(entry.value);
+        if (value != null) {
+          map[entry.key] = value;
+        }
+      }
+    }
+    return map;
+  }
+
+  // maps a json object with a list of MemoryResponse-objects as value to a dart map
+  static Map<String, List<MemoryResponse>> mapListFromJson(dynamic json, {bool growable = false,}) {
+    final map = <String, List<MemoryResponse>>{};
+    if (json is Map && json.isNotEmpty) {
+      // ignore: parameter_assignments
+      json = json.cast<String, dynamic>();
+      for (final entry in json.entries) {
+        map[entry.key] = MemoryResponse.listFromJson(entry.value, growable: growable,);
+      }
+    }
+    return map;
+  }
+
+  /// The list of required keys that must be present in a JSON.
+  static const requiredKeys = <String>{
+    'enabled',
+  };
+}
+
diff --git a/mobile/openapi/lib/model/memory_update.dart b/mobile/openapi/lib/model/memory_update.dart
new file mode 100644
index 0000000000..f2529186c0
--- /dev/null
+++ b/mobile/openapi/lib/model/memory_update.dart
@@ -0,0 +1,107 @@
+//
+// AUTO-GENERATED FILE, DO NOT MODIFY!
+//
+// @dart=2.18
+
+// ignore_for_file: unused_element, unused_import
+// ignore_for_file: always_put_required_named_parameters_first
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: lines_longer_than_80_chars
+
+part of openapi.api;
+
+class MemoryUpdate {
+  /// Returns a new [MemoryUpdate] instance.
+  MemoryUpdate({
+    this.enabled,
+  });
+
+  ///
+  /// Please note: This property should have been non-nullable! Since the specification file
+  /// does not include a default value (using the "default:" property), however, the generated
+  /// source code must fall back to having a nullable type.
+  /// Consider adding a "default:" property in the specification file to hide this note.
+  ///
+  bool? enabled;
+
+  @override
+  bool operator ==(Object other) => identical(this, other) || other is MemoryUpdate &&
+    other.enabled == enabled;
+
+  @override
+  int get hashCode =>
+    // ignore: unnecessary_parenthesis
+    (enabled == null ? 0 : enabled!.hashCode);
+
+  @override
+  String toString() => 'MemoryUpdate[enabled=$enabled]';
+
+  Map<String, dynamic> toJson() {
+    final json = <String, dynamic>{};
+    if (this.enabled != null) {
+      json[r'enabled'] = this.enabled;
+    } else {
+    //  json[r'enabled'] = null;
+    }
+    return json;
+  }
+
+  /// Returns a new [MemoryUpdate] instance and imports its values from
+  /// [value] if it's a [Map], null otherwise.
+  // ignore: prefer_constructors_over_static_methods
+  static MemoryUpdate? fromJson(dynamic value) {
+    if (value is Map) {
+      final json = value.cast<String, dynamic>();
+
+      return MemoryUpdate(
+        enabled: mapValueOfType<bool>(json, r'enabled'),
+      );
+    }
+    return null;
+  }
+
+  static List<MemoryUpdate> listFromJson(dynamic json, {bool growable = false,}) {
+    final result = <MemoryUpdate>[];
+    if (json is List && json.isNotEmpty) {
+      for (final row in json) {
+        final value = MemoryUpdate.fromJson(row);
+        if (value != null) {
+          result.add(value);
+        }
+      }
+    }
+    return result.toList(growable: growable);
+  }
+
+  static Map<String, MemoryUpdate> mapFromJson(dynamic json) {
+    final map = <String, MemoryUpdate>{};
+    if (json is Map && json.isNotEmpty) {
+      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
+      for (final entry in json.entries) {
+        final value = MemoryUpdate.fromJson(entry.value);
+        if (value != null) {
+          map[entry.key] = value;
+        }
+      }
+    }
+    return map;
+  }
+
+  // maps a json object with a list of MemoryUpdate-objects as value to a dart map
+  static Map<String, List<MemoryUpdate>> mapListFromJson(dynamic json, {bool growable = false,}) {
+    final map = <String, List<MemoryUpdate>>{};
+    if (json is Map && json.isNotEmpty) {
+      // ignore: parameter_assignments
+      json = json.cast<String, dynamic>();
+      for (final entry in json.entries) {
+        map[entry.key] = MemoryUpdate.listFromJson(entry.value, growable: growable,);
+      }
+    }
+    return map;
+  }
+
+  /// The list of required keys that must be present in a JSON.
+  static const requiredKeys = <String>{
+  };
+}
+
diff --git a/mobile/openapi/lib/model/user_admin_create_dto.dart b/mobile/openapi/lib/model/user_admin_create_dto.dart
index daf8854e01..db514a1d57 100644
--- a/mobile/openapi/lib/model/user_admin_create_dto.dart
+++ b/mobile/openapi/lib/model/user_admin_create_dto.dart
@@ -14,7 +14,6 @@ class UserAdminCreateDto {
   /// Returns a new [UserAdminCreateDto] instance.
   UserAdminCreateDto({
     required this.email,
-    this.memoriesEnabled,
     required this.name,
     this.notify,
     required this.password,
@@ -25,14 +24,6 @@ class UserAdminCreateDto {
 
   String email;
 
-  ///
-  /// Please note: This property should have been non-nullable! Since the specification file
-  /// does not include a default value (using the "default:" property), however, the generated
-  /// source code must fall back to having a nullable type.
-  /// Consider adding a "default:" property in the specification file to hide this note.
-  ///
-  bool? memoriesEnabled;
-
   String name;
 
   ///
@@ -61,7 +52,6 @@ class UserAdminCreateDto {
   @override
   bool operator ==(Object other) => identical(this, other) || other is UserAdminCreateDto &&
     other.email == email &&
-    other.memoriesEnabled == memoriesEnabled &&
     other.name == name &&
     other.notify == notify &&
     other.password == password &&
@@ -73,7 +63,6 @@ class UserAdminCreateDto {
   int get hashCode =>
     // ignore: unnecessary_parenthesis
     (email.hashCode) +
-    (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
     (name.hashCode) +
     (notify == null ? 0 : notify!.hashCode) +
     (password.hashCode) +
@@ -82,16 +71,11 @@ class UserAdminCreateDto {
     (storageLabel == null ? 0 : storageLabel!.hashCode);
 
   @override
-  String toString() => 'UserAdminCreateDto[email=$email, memoriesEnabled=$memoriesEnabled, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
+  String toString() => 'UserAdminCreateDto[email=$email, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
       json[r'email'] = this.email;
-    if (this.memoriesEnabled != null) {
-      json[r'memoriesEnabled'] = this.memoriesEnabled;
-    } else {
-    //  json[r'memoriesEnabled'] = null;
-    }
       json[r'name'] = this.name;
     if (this.notify != null) {
       json[r'notify'] = this.notify;
@@ -126,7 +110,6 @@ class UserAdminCreateDto {
 
       return UserAdminCreateDto(
         email: mapValueOfType<String>(json, r'email')!,
-        memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
         name: mapValueOfType<String>(json, r'name')!,
         notify: mapValueOfType<bool>(json, r'notify'),
         password: mapValueOfType<String>(json, r'password')!,
diff --git a/mobile/openapi/lib/model/user_admin_response_dto.dart b/mobile/openapi/lib/model/user_admin_response_dto.dart
index 3fc8c2e274..8060fa7cfc 100644
--- a/mobile/openapi/lib/model/user_admin_response_dto.dart
+++ b/mobile/openapi/lib/model/user_admin_response_dto.dart
@@ -19,7 +19,6 @@ class UserAdminResponseDto {
     required this.email,
     required this.id,
     required this.isAdmin,
-    this.memoriesEnabled,
     required this.name,
     required this.oauthId,
     required this.profileImagePath,
@@ -43,14 +42,6 @@ class UserAdminResponseDto {
 
   bool isAdmin;
 
-  ///
-  /// Please note: This property should have been non-nullable! Since the specification file
-  /// does not include a default value (using the "default:" property), however, the generated
-  /// source code must fall back to having a nullable type.
-  /// Consider adding a "default:" property in the specification file to hide this note.
-  ///
-  bool? memoriesEnabled;
-
   String name;
 
   String oauthId;
@@ -77,7 +68,6 @@ class UserAdminResponseDto {
     other.email == email &&
     other.id == id &&
     other.isAdmin == isAdmin &&
-    other.memoriesEnabled == memoriesEnabled &&
     other.name == name &&
     other.oauthId == oauthId &&
     other.profileImagePath == profileImagePath &&
@@ -97,7 +87,6 @@ class UserAdminResponseDto {
     (email.hashCode) +
     (id.hashCode) +
     (isAdmin.hashCode) +
-    (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
     (name.hashCode) +
     (oauthId.hashCode) +
     (profileImagePath.hashCode) +
@@ -109,7 +98,7 @@ class UserAdminResponseDto {
     (updatedAt.hashCode);
 
   @override
-  String toString() => 'UserAdminResponseDto[avatarColor=$avatarColor, createdAt=$createdAt, deletedAt=$deletedAt, email=$email, id=$id, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, oauthId=$oauthId, profileImagePath=$profileImagePath, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, shouldChangePassword=$shouldChangePassword, status=$status, storageLabel=$storageLabel, updatedAt=$updatedAt]';
+  String toString() => 'UserAdminResponseDto[avatarColor=$avatarColor, createdAt=$createdAt, deletedAt=$deletedAt, email=$email, id=$id, isAdmin=$isAdmin, name=$name, oauthId=$oauthId, profileImagePath=$profileImagePath, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, shouldChangePassword=$shouldChangePassword, status=$status, storageLabel=$storageLabel, updatedAt=$updatedAt]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
@@ -123,11 +112,6 @@ class UserAdminResponseDto {
       json[r'email'] = this.email;
       json[r'id'] = this.id;
       json[r'isAdmin'] = this.isAdmin;
-    if (this.memoriesEnabled != null) {
-      json[r'memoriesEnabled'] = this.memoriesEnabled;
-    } else {
-    //  json[r'memoriesEnabled'] = null;
-    }
       json[r'name'] = this.name;
       json[r'oauthId'] = this.oauthId;
       json[r'profileImagePath'] = this.profileImagePath;
@@ -166,7 +150,6 @@ class UserAdminResponseDto {
         email: mapValueOfType<String>(json, r'email')!,
         id: mapValueOfType<String>(json, r'id')!,
         isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
-        memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
         name: mapValueOfType<String>(json, r'name')!,
         oauthId: mapValueOfType<String>(json, r'oauthId')!,
         profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
diff --git a/mobile/openapi/lib/model/user_admin_update_dto.dart b/mobile/openapi/lib/model/user_admin_update_dto.dart
index ecd145248f..dd0db767fe 100644
--- a/mobile/openapi/lib/model/user_admin_update_dto.dart
+++ b/mobile/openapi/lib/model/user_admin_update_dto.dart
@@ -13,9 +13,7 @@ part of openapi.api;
 class UserAdminUpdateDto {
   /// Returns a new [UserAdminUpdateDto] instance.
   UserAdminUpdateDto({
-    this.avatarColor,
     this.email,
-    this.memoriesEnabled,
     this.name,
     this.password,
     this.quotaSizeInBytes,
@@ -23,14 +21,6 @@ class UserAdminUpdateDto {
     this.storageLabel,
   });
 
-  ///
-  /// Please note: This property should have been non-nullable! Since the specification file
-  /// does not include a default value (using the "default:" property), however, the generated
-  /// source code must fall back to having a nullable type.
-  /// Consider adding a "default:" property in the specification file to hide this note.
-  ///
-  UserAvatarColor? avatarColor;
-
   ///
   /// Please note: This property should have been non-nullable! Since the specification file
   /// does not include a default value (using the "default:" property), however, the generated
@@ -39,14 +29,6 @@ class UserAdminUpdateDto {
   ///
   String? email;
 
-  ///
-  /// Please note: This property should have been non-nullable! Since the specification file
-  /// does not include a default value (using the "default:" property), however, the generated
-  /// source code must fall back to having a nullable type.
-  /// Consider adding a "default:" property in the specification file to hide this note.
-  ///
-  bool? memoriesEnabled;
-
   ///
   /// Please note: This property should have been non-nullable! Since the specification file
   /// does not include a default value (using the "default:" property), however, the generated
@@ -78,9 +60,7 @@ class UserAdminUpdateDto {
 
   @override
   bool operator ==(Object other) => identical(this, other) || other is UserAdminUpdateDto &&
-    other.avatarColor == avatarColor &&
     other.email == email &&
-    other.memoriesEnabled == memoriesEnabled &&
     other.name == name &&
     other.password == password &&
     other.quotaSizeInBytes == quotaSizeInBytes &&
@@ -90,9 +70,7 @@ class UserAdminUpdateDto {
   @override
   int get hashCode =>
     // ignore: unnecessary_parenthesis
-    (avatarColor == null ? 0 : avatarColor!.hashCode) +
     (email == null ? 0 : email!.hashCode) +
-    (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
     (name == null ? 0 : name!.hashCode) +
     (password == null ? 0 : password!.hashCode) +
     (quotaSizeInBytes == null ? 0 : quotaSizeInBytes!.hashCode) +
@@ -100,25 +78,15 @@ class UserAdminUpdateDto {
     (storageLabel == null ? 0 : storageLabel!.hashCode);
 
   @override
-  String toString() => 'UserAdminUpdateDto[avatarColor=$avatarColor, email=$email, memoriesEnabled=$memoriesEnabled, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
+  String toString() => 'UserAdminUpdateDto[email=$email, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
-    if (this.avatarColor != null) {
-      json[r'avatarColor'] = this.avatarColor;
-    } else {
-    //  json[r'avatarColor'] = null;
-    }
     if (this.email != null) {
       json[r'email'] = this.email;
     } else {
     //  json[r'email'] = null;
     }
-    if (this.memoriesEnabled != null) {
-      json[r'memoriesEnabled'] = this.memoriesEnabled;
-    } else {
-    //  json[r'memoriesEnabled'] = null;
-    }
     if (this.name != null) {
       json[r'name'] = this.name;
     } else {
@@ -155,9 +123,7 @@ class UserAdminUpdateDto {
       final json = value.cast<String, dynamic>();
 
       return UserAdminUpdateDto(
-        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']),
         email: mapValueOfType<String>(json, r'email'),
-        memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
         name: mapValueOfType<String>(json, r'name'),
         password: mapValueOfType<String>(json, r'password'),
         quotaSizeInBytes: mapValueOfType<int>(json, r'quotaSizeInBytes'),
diff --git a/mobile/openapi/lib/model/user_preferences_response_dto.dart b/mobile/openapi/lib/model/user_preferences_response_dto.dart
new file mode 100644
index 0000000000..673f5bfaf8
--- /dev/null
+++ b/mobile/openapi/lib/model/user_preferences_response_dto.dart
@@ -0,0 +1,106 @@
+//
+// AUTO-GENERATED FILE, DO NOT MODIFY!
+//
+// @dart=2.18
+
+// ignore_for_file: unused_element, unused_import
+// ignore_for_file: always_put_required_named_parameters_first
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: lines_longer_than_80_chars
+
+part of openapi.api;
+
+class UserPreferencesResponseDto {
+  /// Returns a new [UserPreferencesResponseDto] instance.
+  UserPreferencesResponseDto({
+    required this.avatar,
+    required this.memories,
+  });
+
+  AvatarResponse avatar;
+
+  MemoryResponse memories;
+
+  @override
+  bool operator ==(Object other) => identical(this, other) || other is UserPreferencesResponseDto &&
+    other.avatar == avatar &&
+    other.memories == memories;
+
+  @override
+  int get hashCode =>
+    // ignore: unnecessary_parenthesis
+    (avatar.hashCode) +
+    (memories.hashCode);
+
+  @override
+  String toString() => 'UserPreferencesResponseDto[avatar=$avatar, memories=$memories]';
+
+  Map<String, dynamic> toJson() {
+    final json = <String, dynamic>{};
+      json[r'avatar'] = this.avatar;
+      json[r'memories'] = this.memories;
+    return json;
+  }
+
+  /// Returns a new [UserPreferencesResponseDto] instance and imports its values from
+  /// [value] if it's a [Map], null otherwise.
+  // ignore: prefer_constructors_over_static_methods
+  static UserPreferencesResponseDto? fromJson(dynamic value) {
+    if (value is Map) {
+      final json = value.cast<String, dynamic>();
+
+      return UserPreferencesResponseDto(
+        avatar: AvatarResponse.fromJson(json[r'avatar'])!,
+        memories: MemoryResponse.fromJson(json[r'memories'])!,
+      );
+    }
+    return null;
+  }
+
+  static List<UserPreferencesResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
+    final result = <UserPreferencesResponseDto>[];
+    if (json is List && json.isNotEmpty) {
+      for (final row in json) {
+        final value = UserPreferencesResponseDto.fromJson(row);
+        if (value != null) {
+          result.add(value);
+        }
+      }
+    }
+    return result.toList(growable: growable);
+  }
+
+  static Map<String, UserPreferencesResponseDto> mapFromJson(dynamic json) {
+    final map = <String, UserPreferencesResponseDto>{};
+    if (json is Map && json.isNotEmpty) {
+      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
+      for (final entry in json.entries) {
+        final value = UserPreferencesResponseDto.fromJson(entry.value);
+        if (value != null) {
+          map[entry.key] = value;
+        }
+      }
+    }
+    return map;
+  }
+
+  // maps a json object with a list of UserPreferencesResponseDto-objects as value to a dart map
+  static Map<String, List<UserPreferencesResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
+    final map = <String, List<UserPreferencesResponseDto>>{};
+    if (json is Map && json.isNotEmpty) {
+      // ignore: parameter_assignments
+      json = json.cast<String, dynamic>();
+      for (final entry in json.entries) {
+        map[entry.key] = UserPreferencesResponseDto.listFromJson(entry.value, growable: growable,);
+      }
+    }
+    return map;
+  }
+
+  /// The list of required keys that must be present in a JSON.
+  static const requiredKeys = <String>{
+    'avatar',
+    'memories',
+  };
+}
+
diff --git a/mobile/openapi/lib/model/user_preferences_update_dto.dart b/mobile/openapi/lib/model/user_preferences_update_dto.dart
new file mode 100644
index 0000000000..887293931c
--- /dev/null
+++ b/mobile/openapi/lib/model/user_preferences_update_dto.dart
@@ -0,0 +1,124 @@
+//
+// AUTO-GENERATED FILE, DO NOT MODIFY!
+//
+// @dart=2.18
+
+// ignore_for_file: unused_element, unused_import
+// ignore_for_file: always_put_required_named_parameters_first
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: lines_longer_than_80_chars
+
+part of openapi.api;
+
+class UserPreferencesUpdateDto {
+  /// Returns a new [UserPreferencesUpdateDto] instance.
+  UserPreferencesUpdateDto({
+    this.avatar,
+    this.memories,
+  });
+
+  ///
+  /// Please note: This property should have been non-nullable! Since the specification file
+  /// does not include a default value (using the "default:" property), however, the generated
+  /// source code must fall back to having a nullable type.
+  /// Consider adding a "default:" property in the specification file to hide this note.
+  ///
+  AvatarUpdate? avatar;
+
+  ///
+  /// Please note: This property should have been non-nullable! Since the specification file
+  /// does not include a default value (using the "default:" property), however, the generated
+  /// source code must fall back to having a nullable type.
+  /// Consider adding a "default:" property in the specification file to hide this note.
+  ///
+  MemoryUpdate? memories;
+
+  @override
+  bool operator ==(Object other) => identical(this, other) || other is UserPreferencesUpdateDto &&
+    other.avatar == avatar &&
+    other.memories == memories;
+
+  @override
+  int get hashCode =>
+    // ignore: unnecessary_parenthesis
+    (avatar == null ? 0 : avatar!.hashCode) +
+    (memories == null ? 0 : memories!.hashCode);
+
+  @override
+  String toString() => 'UserPreferencesUpdateDto[avatar=$avatar, memories=$memories]';
+
+  Map<String, dynamic> toJson() {
+    final json = <String, dynamic>{};
+    if (this.avatar != null) {
+      json[r'avatar'] = this.avatar;
+    } else {
+    //  json[r'avatar'] = null;
+    }
+    if (this.memories != null) {
+      json[r'memories'] = this.memories;
+    } else {
+    //  json[r'memories'] = null;
+    }
+    return json;
+  }
+
+  /// Returns a new [UserPreferencesUpdateDto] instance and imports its values from
+  /// [value] if it's a [Map], null otherwise.
+  // ignore: prefer_constructors_over_static_methods
+  static UserPreferencesUpdateDto? fromJson(dynamic value) {
+    if (value is Map) {
+      final json = value.cast<String, dynamic>();
+
+      return UserPreferencesUpdateDto(
+        avatar: AvatarUpdate.fromJson(json[r'avatar']),
+        memories: MemoryUpdate.fromJson(json[r'memories']),
+      );
+    }
+    return null;
+  }
+
+  static List<UserPreferencesUpdateDto> listFromJson(dynamic json, {bool growable = false,}) {
+    final result = <UserPreferencesUpdateDto>[];
+    if (json is List && json.isNotEmpty) {
+      for (final row in json) {
+        final value = UserPreferencesUpdateDto.fromJson(row);
+        if (value != null) {
+          result.add(value);
+        }
+      }
+    }
+    return result.toList(growable: growable);
+  }
+
+  static Map<String, UserPreferencesUpdateDto> mapFromJson(dynamic json) {
+    final map = <String, UserPreferencesUpdateDto>{};
+    if (json is Map && json.isNotEmpty) {
+      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
+      for (final entry in json.entries) {
+        final value = UserPreferencesUpdateDto.fromJson(entry.value);
+        if (value != null) {
+          map[entry.key] = value;
+        }
+      }
+    }
+    return map;
+  }
+
+  // maps a json object with a list of UserPreferencesUpdateDto-objects as value to a dart map
+  static Map<String, List<UserPreferencesUpdateDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
+    final map = <String, List<UserPreferencesUpdateDto>>{};
+    if (json is Map && json.isNotEmpty) {
+      // ignore: parameter_assignments
+      json = json.cast<String, dynamic>();
+      for (final entry in json.entries) {
+        map[entry.key] = UserPreferencesUpdateDto.listFromJson(entry.value, growable: growable,);
+      }
+    }
+    return map;
+  }
+
+  /// The list of required keys that must be present in a JSON.
+  static const requiredKeys = <String>{
+  };
+}
+
diff --git a/mobile/openapi/lib/model/user_update_me_dto.dart b/mobile/openapi/lib/model/user_update_me_dto.dart
index 1b54d4a383..2d665fc784 100644
--- a/mobile/openapi/lib/model/user_update_me_dto.dart
+++ b/mobile/openapi/lib/model/user_update_me_dto.dart
@@ -13,21 +13,11 @@ part of openapi.api;
 class UserUpdateMeDto {
   /// Returns a new [UserUpdateMeDto] instance.
   UserUpdateMeDto({
-    this.avatarColor,
     this.email,
-    this.memoriesEnabled,
     this.name,
     this.password,
   });
 
-  ///
-  /// Please note: This property should have been non-nullable! Since the specification file
-  /// does not include a default value (using the "default:" property), however, the generated
-  /// source code must fall back to having a nullable type.
-  /// Consider adding a "default:" property in the specification file to hide this note.
-  ///
-  UserAvatarColor? avatarColor;
-
   ///
   /// Please note: This property should have been non-nullable! Since the specification file
   /// does not include a default value (using the "default:" property), however, the generated
@@ -36,14 +26,6 @@ class UserUpdateMeDto {
   ///
   String? email;
 
-  ///
-  /// Please note: This property should have been non-nullable! Since the specification file
-  /// does not include a default value (using the "default:" property), however, the generated
-  /// source code must fall back to having a nullable type.
-  /// Consider adding a "default:" property in the specification file to hide this note.
-  ///
-  bool? memoriesEnabled;
-
   ///
   /// Please note: This property should have been non-nullable! Since the specification file
   /// does not include a default value (using the "default:" property), however, the generated
@@ -62,41 +44,27 @@ class UserUpdateMeDto {
 
   @override
   bool operator ==(Object other) => identical(this, other) || other is UserUpdateMeDto &&
-    other.avatarColor == avatarColor &&
     other.email == email &&
-    other.memoriesEnabled == memoriesEnabled &&
     other.name == name &&
     other.password == password;
 
   @override
   int get hashCode =>
     // ignore: unnecessary_parenthesis
-    (avatarColor == null ? 0 : avatarColor!.hashCode) +
     (email == null ? 0 : email!.hashCode) +
-    (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
     (name == null ? 0 : name!.hashCode) +
     (password == null ? 0 : password!.hashCode);
 
   @override
-  String toString() => 'UserUpdateMeDto[avatarColor=$avatarColor, email=$email, memoriesEnabled=$memoriesEnabled, name=$name, password=$password]';
+  String toString() => 'UserUpdateMeDto[email=$email, name=$name, password=$password]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
-    if (this.avatarColor != null) {
-      json[r'avatarColor'] = this.avatarColor;
-    } else {
-    //  json[r'avatarColor'] = null;
-    }
     if (this.email != null) {
       json[r'email'] = this.email;
     } else {
     //  json[r'email'] = null;
     }
-    if (this.memoriesEnabled != null) {
-      json[r'memoriesEnabled'] = this.memoriesEnabled;
-    } else {
-    //  json[r'memoriesEnabled'] = null;
-    }
     if (this.name != null) {
       json[r'name'] = this.name;
     } else {
@@ -118,9 +86,7 @@ class UserUpdateMeDto {
       final json = value.cast<String, dynamic>();
 
       return UserUpdateMeDto(
-        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']),
         email: mapValueOfType<String>(json, r'email'),
-        memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
         name: mapValueOfType<String>(json, r'name'),
         password: mapValueOfType<String>(json, r'password'),
       );
diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json
index 558823e62b..d875994865 100644
--- a/open-api/immich-openapi-specs.json
+++ b/open-api/immich-openapi-specs.json
@@ -432,6 +432,98 @@
         ]
       }
     },
+    "/admin/users/{id}/preferences": {
+      "get": {
+        "operationId": "getUserPreferencesAdmin",
+        "parameters": [
+          {
+            "name": "id",
+            "required": true,
+            "in": "path",
+            "schema": {
+              "format": "uuid",
+              "type": "string"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/UserPreferencesResponseDto"
+                }
+              }
+            },
+            "description": ""
+          }
+        },
+        "security": [
+          {
+            "bearer": []
+          },
+          {
+            "cookie": []
+          },
+          {
+            "api_key": []
+          }
+        ],
+        "tags": [
+          "User"
+        ]
+      },
+      "put": {
+        "operationId": "updateUserPreferencesAdmin",
+        "parameters": [
+          {
+            "name": "id",
+            "required": true,
+            "in": "path",
+            "schema": {
+              "format": "uuid",
+              "type": "string"
+            }
+          }
+        ],
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/UserPreferencesUpdateDto"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "200": {
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/UserPreferencesResponseDto"
+                }
+              }
+            },
+            "description": ""
+          }
+        },
+        "security": [
+          {
+            "bearer": []
+          },
+          {
+            "cookie": []
+          },
+          {
+            "api_key": []
+          }
+        ],
+        "tags": [
+          "User"
+        ]
+      }
+    },
     "/admin/users/{id}/restore": {
       "post": {
         "operationId": "restoreUserAdmin",
@@ -6403,6 +6495,78 @@
         ]
       }
     },
+    "/users/me/preferences": {
+      "get": {
+        "operationId": "getMyPreferences",
+        "parameters": [],
+        "responses": {
+          "200": {
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/UserPreferencesResponseDto"
+                }
+              }
+            },
+            "description": ""
+          }
+        },
+        "security": [
+          {
+            "bearer": []
+          },
+          {
+            "cookie": []
+          },
+          {
+            "api_key": []
+          }
+        ],
+        "tags": [
+          "User"
+        ]
+      },
+      "put": {
+        "operationId": "updateMyPreferences",
+        "parameters": [],
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/UserPreferencesUpdateDto"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "200": {
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/UserPreferencesResponseDto"
+                }
+              }
+            },
+            "description": ""
+          }
+        },
+        "security": [
+          {
+            "bearer": []
+          },
+          {
+            "cookie": []
+          },
+          {
+            "api_key": []
+          }
+        ],
+        "tags": [
+          "User"
+        ]
+      }
+    },
     "/users/profile-image": {
       "delete": {
         "operationId": "deleteProfileImage",
@@ -7621,6 +7785,25 @@
         ],
         "type": "object"
       },
+      "AvatarResponse": {
+        "properties": {
+          "color": {
+            "$ref": "#/components/schemas/UserAvatarColor"
+          }
+        },
+        "required": [
+          "color"
+        ],
+        "type": "object"
+      },
+      "AvatarUpdate": {
+        "properties": {
+          "color": {
+            "$ref": "#/components/schemas/UserAvatarColor"
+          }
+        },
+        "type": "object"
+      },
       "BulkIdResponseDto": {
         "properties": {
           "error": {
@@ -8584,6 +8767,17 @@
         ],
         "type": "object"
       },
+      "MemoryResponse": {
+        "properties": {
+          "enabled": {
+            "type": "boolean"
+          }
+        },
+        "required": [
+          "enabled"
+        ],
+        "type": "object"
+      },
       "MemoryResponseDto": {
         "properties": {
           "assets": {
@@ -8650,6 +8844,14 @@
         ],
         "type": "string"
       },
+      "MemoryUpdate": {
+        "properties": {
+          "enabled": {
+            "type": "boolean"
+          }
+        },
+        "type": "object"
+      },
       "MemoryUpdateDto": {
         "properties": {
           "isSaved": {
@@ -10878,9 +11080,6 @@
           "email": {
             "type": "string"
           },
-          "memoriesEnabled": {
-            "type": "boolean"
-          },
           "name": {
             "type": "string"
           },
@@ -10942,9 +11141,6 @@
           "isAdmin": {
             "type": "boolean"
           },
-          "memoriesEnabled": {
-            "type": "boolean"
-          },
           "name": {
             "type": "string"
           },
@@ -11000,15 +11196,9 @@
       },
       "UserAdminUpdateDto": {
         "properties": {
-          "avatarColor": {
-            "$ref": "#/components/schemas/UserAvatarColor"
-          },
           "email": {
             "type": "string"
           },
-          "memoriesEnabled": {
-            "type": "boolean"
-          },
           "name": {
             "type": "string"
           },
@@ -11046,6 +11236,32 @@
         ],
         "type": "string"
       },
+      "UserPreferencesResponseDto": {
+        "properties": {
+          "avatar": {
+            "$ref": "#/components/schemas/AvatarResponse"
+          },
+          "memories": {
+            "$ref": "#/components/schemas/MemoryResponse"
+          }
+        },
+        "required": [
+          "avatar",
+          "memories"
+        ],
+        "type": "object"
+      },
+      "UserPreferencesUpdateDto": {
+        "properties": {
+          "avatar": {
+            "$ref": "#/components/schemas/AvatarUpdate"
+          },
+          "memories": {
+            "$ref": "#/components/schemas/MemoryUpdate"
+          }
+        },
+        "type": "object"
+      },
       "UserResponseDto": {
         "properties": {
           "avatarColor": {
@@ -11083,15 +11299,9 @@
       },
       "UserUpdateMeDto": {
         "properties": {
-          "avatarColor": {
-            "$ref": "#/components/schemas/UserAvatarColor"
-          },
           "email": {
             "type": "string"
           },
-          "memoriesEnabled": {
-            "type": "boolean"
-          },
           "name": {
             "type": "string"
           },
diff --git a/open-api/typescript-sdk/README.md b/open-api/typescript-sdk/README.md
index 53a83a4237..046cea7695 100644
--- a/open-api/typescript-sdk/README.md
+++ b/open-api/typescript-sdk/README.md
@@ -13,22 +13,13 @@ npm i --save @immich/sdk
 For a more detailed example, check out the [`@immich/cli`](https://github.com/immich-app/immich/tree/main/cli).
 
 ```typescript
-<<<<<<< HEAD
-import { getAllAlbums, getAllAssets, getMyUser, init } from "@immich/sdk";
-=======
-import { getAllAlbums, getMyUserInfo, init } from "@immich/sdk";
->>>>>>> e7c8501930a988dfb6c23ce1c48b0beb076a58c2
+import { getAllAlbums, getMyUser, init } from "@immich/sdk";
 
 const API_KEY = "<API_KEY>"; // process.env.IMMICH_API_KEY
 
 init({ baseUrl: "https://demo.immich.app/api", apiKey: API_KEY });
 
-<<<<<<< HEAD
 const user = await getMyUser();
-const assets = await getAllAssets({ take: 1000 });
-=======
-const user = await getMyUserInfo();
->>>>>>> e7c8501930a988dfb6c23ce1c48b0beb076a58c2
 const albums = await getAllAlbums({});
 
 console.log({ user, albums });
diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts
index 2c07072f68..8030c92d44 100644
--- a/open-api/typescript-sdk/src/fetch-client.ts
+++ b/open-api/typescript-sdk/src/fetch-client.ts
@@ -45,7 +45,6 @@ export type UserAdminResponseDto = {
     email: string;
     id: string;
     isAdmin: boolean;
-    memoriesEnabled?: boolean;
     name: string;
     oauthId: string;
     profileImagePath: string;
@@ -58,7 +57,6 @@ export type UserAdminResponseDto = {
 };
 export type UserAdminCreateDto = {
     email: string;
-    memoriesEnabled?: boolean;
     name: string;
     notify?: boolean;
     password: string;
@@ -70,15 +68,33 @@ export type UserAdminDeleteDto = {
     force?: boolean;
 };
 export type UserAdminUpdateDto = {
-    avatarColor?: UserAvatarColor;
     email?: string;
-    memoriesEnabled?: boolean;
     name?: string;
     password?: string;
     quotaSizeInBytes?: number | null;
     shouldChangePassword?: boolean;
     storageLabel?: string | null;
 };
+export type AvatarResponse = {
+    color: UserAvatarColor;
+};
+export type MemoryResponse = {
+    enabled: boolean;
+};
+export type UserPreferencesResponseDto = {
+    avatar: AvatarResponse;
+    memories: MemoryResponse;
+};
+export type AvatarUpdate = {
+    color?: UserAvatarColor;
+};
+export type MemoryUpdate = {
+    enabled?: boolean;
+};
+export type UserPreferencesUpdateDto = {
+    avatar?: AvatarUpdate;
+    memories?: MemoryUpdate;
+};
 export type AlbumUserResponseDto = {
     role: AlbumUserRole;
     user: UserResponseDto;
@@ -1073,9 +1089,7 @@ export type TimeBucketResponseDto = {
     timeBucket: string;
 };
 export type UserUpdateMeDto = {
-    avatarColor?: UserAvatarColor;
     email?: string;
-    memoriesEnabled?: boolean;
     name?: string;
     password?: string;
 };
@@ -1200,6 +1214,29 @@ export function updateUserAdmin({ id, userAdminUpdateDto }: {
         body: userAdminUpdateDto
     })));
 }
+export function getUserPreferencesAdmin({ id }: {
+    id: string;
+}, opts?: Oazapfts.RequestOpts) {
+    return oazapfts.ok(oazapfts.fetchJson<{
+        status: 200;
+        data: UserPreferencesResponseDto;
+    }>(`/admin/users/${encodeURIComponent(id)}/preferences`, {
+        ...opts
+    }));
+}
+export function updateUserPreferencesAdmin({ id, userPreferencesUpdateDto }: {
+    id: string;
+    userPreferencesUpdateDto: UserPreferencesUpdateDto;
+}, opts?: Oazapfts.RequestOpts) {
+    return oazapfts.ok(oazapfts.fetchJson<{
+        status: 200;
+        data: UserPreferencesResponseDto;
+    }>(`/admin/users/${encodeURIComponent(id)}/preferences`, oazapfts.json({
+        ...opts,
+        method: "PUT",
+        body: userPreferencesUpdateDto
+    })));
+}
 export function restoreUserAdmin({ id }: {
     id: string;
 }, opts?: Oazapfts.RequestOpts) {
@@ -2780,6 +2817,26 @@ export function updateMyUser({ userUpdateMeDto }: {
         body: userUpdateMeDto
     })));
 }
+export function getMyPreferences(opts?: Oazapfts.RequestOpts) {
+    return oazapfts.ok(oazapfts.fetchJson<{
+        status: 200;
+        data: UserPreferencesResponseDto;
+    }>("/users/me/preferences", {
+        ...opts
+    }));
+}
+export function updateMyPreferences({ userPreferencesUpdateDto }: {
+    userPreferencesUpdateDto: UserPreferencesUpdateDto;
+}, opts?: Oazapfts.RequestOpts) {
+    return oazapfts.ok(oazapfts.fetchJson<{
+        status: 200;
+        data: UserPreferencesResponseDto;
+    }>("/users/me/preferences", oazapfts.json({
+        ...opts,
+        method: "PUT",
+        body: userPreferencesUpdateDto
+    })));
+}
 export function deleteProfileImage(opts?: Oazapfts.RequestOpts) {
     return oazapfts.ok(oazapfts.fetchText("/users/profile-image", {
         ...opts,
diff --git a/server/src/controllers/user-admin.controller.ts b/server/src/controllers/user-admin.controller.ts
index 4d0b781e81..83b5156eda 100644
--- a/server/src/controllers/user-admin.controller.ts
+++ b/server/src/controllers/user-admin.controller.ts
@@ -1,6 +1,7 @@
 import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common';
 import { ApiTags } from '@nestjs/swagger';
 import { AuthDto } from 'src/dtos/auth.dto';
+import { UserPreferencesResponseDto, UserPreferencesUpdateDto } from 'src/dtos/user-preferences.dto';
 import {
   UserAdminCreateDto,
   UserAdminDeleteDto,
@@ -55,6 +56,22 @@ export class UserAdminController {
     return this.service.delete(auth, id, dto);
   }
 
+  @Get(':id/preferences')
+  @Authenticated()
+  getUserPreferencesAdmin(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<UserPreferencesResponseDto> {
+    return this.service.getPreferences(auth, id);
+  }
+
+  @Put(':id/preferences')
+  @Authenticated()
+  updateUserPreferencesAdmin(
+    @Auth() auth: AuthDto,
+    @Param() { id }: UUIDParamDto,
+    @Body() dto: UserPreferencesUpdateDto,
+  ): Promise<UserPreferencesResponseDto> {
+    return this.service.updatePreferences(auth, id, dto);
+  }
+
   @Post(':id/restore')
   @Authenticated({ admin: true })
   restoreUserAdmin(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<UserAdminResponseDto> {
diff --git a/server/src/controllers/user.controller.ts b/server/src/controllers/user.controller.ts
index f66807b92c..66a92e1a3f 100644
--- a/server/src/controllers/user.controller.ts
+++ b/server/src/controllers/user.controller.ts
@@ -17,6 +17,7 @@ import {
 import { ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger';
 import { NextFunction, Response } from 'express';
 import { AuthDto } from 'src/dtos/auth.dto';
+import { UserPreferencesResponseDto, UserPreferencesUpdateDto } from 'src/dtos/user-preferences.dto';
 import { CreateProfileImageDto, CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto';
 import { UserAdminResponseDto, UserResponseDto, UserUpdateMeDto } from 'src/dtos/user.dto';
 import { ILoggerRepository } from 'src/interfaces/logger.interface';
@@ -52,6 +53,21 @@ export class UserController {
     return this.service.updateMe(auth, dto);
   }
 
+  @Get('me/preferences')
+  @Authenticated()
+  getMyPreferences(@Auth() auth: AuthDto): UserPreferencesResponseDto {
+    return this.service.getMyPreferences(auth);
+  }
+
+  @Put('me/preferences')
+  @Authenticated()
+  updateMyPreferences(
+    @Auth() auth: AuthDto,
+    @Body() dto: UserPreferencesUpdateDto,
+  ): Promise<UserPreferencesResponseDto> {
+    return this.service.updateMyPreferences(auth, dto);
+  }
+
   @Get(':id')
   @Authenticated()
   getUser(@Param() { id }: UUIDParamDto): Promise<UserResponseDto> {
diff --git a/server/src/dtos/user-preferences.dto.ts b/server/src/dtos/user-preferences.dto.ts
new file mode 100644
index 0000000000..2dd9492d07
--- /dev/null
+++ b/server/src/dtos/user-preferences.dto.ts
@@ -0,0 +1,47 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { Type } from 'class-transformer';
+import { IsEnum, ValidateNested } from 'class-validator';
+import { UserAvatarColor, UserPreferences } from 'src/entities/user-metadata.entity';
+import { Optional, ValidateBoolean } from 'src/validation';
+
+class AvatarUpdate {
+  @Optional()
+  @IsEnum(UserAvatarColor)
+  @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
+  color?: UserAvatarColor;
+}
+
+class MemoryUpdate {
+  @ValidateBoolean({ optional: true })
+  enabled?: boolean;
+}
+
+export class UserPreferencesUpdateDto {
+  @Optional()
+  @ValidateNested()
+  @Type(() => AvatarUpdate)
+  avatar?: AvatarUpdate;
+
+  @Optional()
+  @ValidateNested()
+  @Type(() => MemoryUpdate)
+  memories?: MemoryUpdate;
+}
+
+class AvatarResponse {
+  @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
+  color!: UserAvatarColor;
+}
+
+class MemoryResponse {
+  enabled!: boolean;
+}
+
+export class UserPreferencesResponseDto implements UserPreferences {
+  memories!: MemoryResponse;
+  avatar!: AvatarResponse;
+}
+
+export const mapPreferences = (preferences: UserPreferences): UserPreferencesResponseDto => {
+  return preferences;
+};
diff --git a/server/src/dtos/user.dto.ts b/server/src/dtos/user.dto.ts
index 8290df6adb..63bac60d06 100644
--- a/server/src/dtos/user.dto.ts
+++ b/server/src/dtos/user.dto.ts
@@ -1,6 +1,6 @@
 import { ApiProperty } from '@nestjs/swagger';
 import { Transform } from 'class-transformer';
-import { IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumber, IsPositive, IsString } from 'class-validator';
+import { IsBoolean, IsEmail, IsNotEmpty, IsNumber, IsPositive, IsString } from 'class-validator';
 import { UserAvatarColor } from 'src/entities/user-metadata.entity';
 import { UserEntity, UserStatus } from 'src/entities/user.entity';
 import { getPreferences } from 'src/utils/preferences';
@@ -22,14 +22,6 @@ export class UserUpdateMeDto {
   @IsString()
   @IsNotEmpty()
   name?: string;
-
-  @ValidateBoolean({ optional: true })
-  memoriesEnabled?: boolean;
-
-  @Optional()
-  @IsEnum(UserAvatarColor)
-  @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
-  avatarColor?: UserAvatarColor;
 }
 
 export class UserResponseDto {
@@ -37,7 +29,6 @@ export class UserResponseDto {
   name!: string;
   email!: string;
   profileImagePath!: string;
-  @IsEnum(UserAvatarColor)
   @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
   avatarColor!: UserAvatarColor;
 }
@@ -75,9 +66,6 @@ export class UserAdminCreateDto {
   @Transform(toSanitized)
   storageLabel?: string | null;
 
-  @ValidateBoolean({ optional: true })
-  memoriesEnabled?: boolean;
-
   @Optional({ nullable: true })
   @IsNumber()
   @IsPositive()
@@ -116,14 +104,6 @@ export class UserAdminUpdateDto {
   @ValidateBoolean({ optional: true })
   shouldChangePassword?: boolean;
 
-  @ValidateBoolean({ optional: true })
-  memoriesEnabled?: boolean;
-
-  @Optional()
-  @IsEnum(UserAvatarColor)
-  @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
-  avatarColor?: UserAvatarColor;
-
   @Optional({ nullable: true })
   @IsNumber()
   @IsPositive()
@@ -144,7 +124,6 @@ export class UserAdminResponseDto extends UserResponseDto {
   deletedAt!: Date | null;
   updatedAt!: Date;
   oauthId!: string;
-  memoriesEnabled?: boolean;
   @ApiProperty({ type: 'integer', format: 'int64' })
   quotaSizeInBytes!: number | null;
   @ApiProperty({ type: 'integer', format: 'int64' })
@@ -163,7 +142,6 @@ export function mapUserAdmin(entity: UserEntity): UserAdminResponseDto {
     deletedAt: entity.deletedAt,
     updatedAt: entity.updatedAt,
     oauthId: entity.oauthId,
-    memoriesEnabled: getPreferences(entity).memories.enabled,
     quotaSizeInBytes: entity.quotaSizeInBytes,
     quotaUsageInBytes: entity.quotaUsageInBytes,
     status: entity.status,
diff --git a/server/src/services/user-admin.service.ts b/server/src/services/user-admin.service.ts
index 1b93f96e71..72330ac9b7 100644
--- a/server/src/services/user-admin.service.ts
+++ b/server/src/services/user-admin.service.ts
@@ -2,6 +2,7 @@ import { BadRequestException, ForbiddenException, Inject, Injectable } from '@ne
 import { SALT_ROUNDS } from 'src/constants';
 import { UserCore } from 'src/cores/user.core';
 import { AuthDto } from 'src/dtos/auth.dto';
+import { UserPreferencesResponseDto, UserPreferencesUpdateDto, mapPreferences } from 'src/dtos/user-preferences.dto';
 import {
   UserAdminCreateDto,
   UserAdminDeleteDto,
@@ -17,7 +18,7 @@ import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 import { IJobRepository, JobName } from 'src/interfaces/job.interface';
 import { ILoggerRepository } from 'src/interfaces/logger.interface';
 import { IUserRepository, UserFindOptions } from 'src/interfaces/user.interface';
-import { getPreferences, getPreferencesPartial } from 'src/utils/preferences';
+import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
 
 @Injectable()
 export class UserAdminService {
@@ -40,18 +41,8 @@ export class UserAdminService {
   }
 
   async create(dto: UserAdminCreateDto): Promise<UserAdminResponseDto> {
-    const { memoriesEnabled, notify, ...rest } = dto;
-    let user = await this.userCore.createUser(rest);
-
-    // TODO remove and replace with entire dto.preferences config
-    if (memoriesEnabled === false) {
-      await this.userRepository.upsertMetadata(user.id, {
-        key: UserMetadataKey.PREFERENCES,
-        value: { memories: { enabled: false } },
-      });
-
-      user = await this.findOrFail(user.id, {});
-    }
+    const { notify, ...rest } = dto;
+    const user = await this.userCore.createUser(rest);
 
     const tempPassword = user.shouldChangePassword ? rest.password : undefined;
     if (notify) {
@@ -72,25 +63,6 @@ export class UserAdminService {
       await this.userRepository.syncUsage(id);
     }
 
-    // TODO replace with entire preferences object
-    if (dto.memoriesEnabled !== undefined || dto.avatarColor) {
-      const newPreferences = getPreferences(user);
-      if (dto.memoriesEnabled !== undefined) {
-        newPreferences.memories.enabled = dto.memoriesEnabled;
-        delete dto.memoriesEnabled;
-      }
-
-      if (dto.avatarColor) {
-        newPreferences.avatar.color = dto.avatarColor;
-        delete dto.avatarColor;
-      }
-
-      await this.userRepository.upsertMetadata(id, {
-        key: UserMetadataKey.PREFERENCES,
-        value: getPreferencesPartial(user, newPreferences),
-      });
-    }
-
     if (dto.email) {
       const duplicate = await this.userRepository.getByEmail(dto.email);
       if (duplicate && duplicate.id !== id) {
@@ -144,6 +116,24 @@ export class UserAdminService {
     return mapUserAdmin(user);
   }
 
+  async getPreferences(auth: AuthDto, id: string): Promise<UserPreferencesResponseDto> {
+    const user = await this.findOrFail(id, { withDeleted: false });
+    const preferences = getPreferences(user);
+    return mapPreferences(preferences);
+  }
+
+  async updatePreferences(auth: AuthDto, id: string, dto: UserPreferencesUpdateDto) {
+    const user = await this.findOrFail(id, { withDeleted: false });
+    const preferences = mergePreferences(user, dto);
+
+    await this.userRepository.upsertMetadata(user.id, {
+      key: UserMetadataKey.PREFERENCES,
+      value: getPreferencesPartial(user, preferences),
+    });
+
+    return mapPreferences(preferences);
+  }
+
   private async findOrFail(id: string, options: UserFindOptions) {
     const user = await this.userRepository.get(id, options);
     if (!user) {
diff --git a/server/src/services/user.service.ts b/server/src/services/user.service.ts
index 1f36501051..3920dbeaac 100644
--- a/server/src/services/user.service.ts
+++ b/server/src/services/user.service.ts
@@ -4,6 +4,7 @@ import { SALT_ROUNDS } from 'src/constants';
 import { StorageCore, StorageFolder } from 'src/cores/storage.core';
 import { SystemConfigCore } from 'src/cores/system-config.core';
 import { AuthDto } from 'src/dtos/auth.dto';
+import { UserPreferencesResponseDto, UserPreferencesUpdateDto, mapPreferences } from 'src/dtos/user-preferences.dto';
 import { CreateProfileImageResponseDto, mapCreateProfileImageResponse } from 'src/dtos/user-profile.dto';
 import { UserAdminResponseDto, UserResponseDto, UserUpdateMeDto, mapUser, mapUserAdmin } from 'src/dtos/user.dto';
 import { UserMetadataKey } from 'src/entities/user-metadata.entity';
@@ -16,7 +17,7 @@ import { IStorageRepository } from 'src/interfaces/storage.interface';
 import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
 import { IUserRepository, UserFindOptions } from 'src/interfaces/user.interface';
 import { CacheControl, ImmichFileResponse } from 'src/utils/file';
-import { getPreferences, getPreferencesPartial } from 'src/utils/preferences';
+import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
 
 @Injectable()
 export class UserService {
@@ -45,25 +46,6 @@ export class UserService {
   }
 
   async updateMe({ user }: AuthDto, dto: UserUpdateMeDto): Promise<UserAdminResponseDto> {
-    // TODO replace with entire preferences object
-    if (dto.memoriesEnabled !== undefined || dto.avatarColor) {
-      const newPreferences = getPreferences(user);
-      if (dto.memoriesEnabled !== undefined) {
-        newPreferences.memories.enabled = dto.memoriesEnabled;
-        delete dto.memoriesEnabled;
-      }
-
-      if (dto.avatarColor) {
-        newPreferences.avatar.color = dto.avatarColor;
-        delete dto.avatarColor;
-      }
-
-      await this.userRepository.upsertMetadata(user.id, {
-        key: UserMetadataKey.PREFERENCES,
-        value: getPreferencesPartial(user, newPreferences),
-      });
-    }
-
     if (dto.email) {
       const duplicate = await this.userRepository.getByEmail(dto.email);
       if (duplicate && duplicate.id !== user.id) {
@@ -87,6 +69,22 @@ export class UserService {
     return mapUserAdmin(updatedUser);
   }
 
+  getMyPreferences({ user }: AuthDto): UserPreferencesResponseDto {
+    const preferences = getPreferences(user);
+    return mapPreferences(preferences);
+  }
+
+  async updateMyPreferences({ user }: AuthDto, dto: UserPreferencesUpdateDto) {
+    const preferences = mergePreferences(user, dto);
+
+    await this.userRepository.upsertMetadata(user.id, {
+      key: UserMetadataKey.PREFERENCES,
+      value: getPreferencesPartial(user, preferences),
+    });
+
+    return mapPreferences(preferences);
+  }
+
   async get(id: string): Promise<UserResponseDto> {
     const user = await this.findOrFail(id, { withDeleted: false });
     return mapUser(user);
diff --git a/server/src/utils/preferences.ts b/server/src/utils/preferences.ts
index ae10c24fc9..f3561fa7b6 100644
--- a/server/src/utils/preferences.ts
+++ b/server/src/utils/preferences.ts
@@ -1,4 +1,5 @@
 import _ from 'lodash';
+import { UserPreferencesUpdateDto } from 'src/dtos/user-preferences.dto';
 import { UserMetadataKey, UserPreferences, getDefaultPreferences } from 'src/entities/user-metadata.entity';
 import { UserEntity } from 'src/entities/user.entity';
 import { getKeysDeep } from 'src/utils/misc';
@@ -37,3 +38,12 @@ export const getPreferencesPartial = (user: { email: string }, newPreferences: U
 
   return partial;
 };
+
+export const mergePreferences = (user: UserEntity, dto: UserPreferencesUpdateDto) => {
+  const preferences = getPreferences(user);
+  for (const key of getKeysDeep(dto)) {
+    _.set(preferences, key, _.get(dto, key));
+  }
+
+  return preferences;
+};
diff --git a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte
index 8c73ed4d84..5d5a351de2 100644
--- a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte
+++ b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte
@@ -1,18 +1,18 @@
 <script lang="ts">
   import Button from '$lib/components/elements/buttons/button.svelte';
+  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
   import Icon from '$lib/components/elements/icon.svelte';
+  import FocusTrap from '$lib/components/shared-components/focus-trap.svelte';
   import { AppRoute } from '$lib/constants';
-  import { user } from '$lib/stores/user.store';
+  import { preferences, user } from '$lib/stores/user.store';
   import { handleError } from '$lib/utils/handle-error';
-  import { deleteProfileImage, updateMyUser, type UserAvatarColor } from '@immich/sdk';
+  import { deleteProfileImage, updateMyPreferences, type UserAvatarColor } from '@immich/sdk';
   import { mdiCog, mdiLogout, mdiPencil } from '@mdi/js';
   import { createEventDispatcher } from 'svelte';
   import { fade } from 'svelte/transition';
-  import { notificationController, NotificationType } from '../notification/notification';
+  import { NotificationType, notificationController } from '../notification/notification';
   import UserAvatar from '../user-avatar.svelte';
   import AvatarSelector from './avatar-selector.svelte';
-  import FocusTrap from '$lib/components/shared-components/focus-trap.svelte';
-  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 
   let isShowSelectAvatar = false;
 
@@ -27,14 +27,7 @@
         await deleteProfileImage();
       }
 
-      $user = await updateMyUser({
-        userUpdateMeDto: {
-          email: $user.email,
-          name: $user.name,
-          avatarColor: color,
-        },
-      });
-
+      $preferences = await updateMyPreferences({ userPreferencesUpdateDto: { avatar: { color } } });
       isShowSelectAvatar = false;
 
       notificationController.show({
diff --git a/web/src/lib/components/user-settings-page/memories-settings.svelte b/web/src/lib/components/user-settings-page/memories-settings.svelte
index dcd7033aa4..4d103ade13 100644
--- a/web/src/lib/components/user-settings-page/memories-settings.svelte
+++ b/web/src/lib/components/user-settings-page/memories-settings.svelte
@@ -3,20 +3,20 @@
     notificationController,
     NotificationType,
   } from '$lib/components/shared-components/notification/notification';
-  import { updateMyUser, type UserAdminResponseDto } from '@immich/sdk';
+  import { updateMyPreferences } from '@immich/sdk';
   import { fade } from 'svelte/transition';
   import { handleError } from '../../utils/handle-error';
 
   import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
+  import { preferences } from '$lib/stores/user.store';
   import Button from '../elements/buttons/button.svelte';
 
-  export let user: UserAdminResponseDto;
+  let memoriesEnabled = $preferences?.memories?.enabled ?? false;
 
   const handleSave = async () => {
     try {
-      const data = await updateMyUser({ userUpdateMeDto: { memoriesEnabled: user.memoriesEnabled } });
-
-      Object.assign(user, data);
+      const data = await updateMyPreferences({ userPreferencesUpdateDto: { memories: { enabled: memoriesEnabled } } });
+      $preferences.memories.enabled = data.memories.enabled;
 
       notificationController.show({ message: 'Saved settings', type: NotificationType.Info });
     } catch (error) {
@@ -34,7 +34,7 @@
             id="time-based-memories"
             title="Time-based memories"
             subtitle="Photos from previous years"
-            bind:checked={user.memoriesEnabled}
+            bind:checked={memoriesEnabled}
           />
         </div>
         <div class="flex justify-end">
diff --git a/web/src/lib/components/user-settings-page/user-settings-list.svelte b/web/src/lib/components/user-settings-page/user-settings-list.svelte
index d239886ed9..f88ee58872 100644
--- a/web/src/lib/components/user-settings-page/user-settings-list.svelte
+++ b/web/src/lib/components/user-settings-page/user-settings-list.svelte
@@ -42,7 +42,7 @@
   </SettingAccordion>
 
   <SettingAccordion key="memories" title="Memories" subtitle="Manage what you see in your memories">
-    <MemoriesSettings user={$user} />
+    <MemoriesSettings />
   </SettingAccordion>
 
   {#if $featureFlags.loaded && $featureFlags.oauth}
diff --git a/web/src/lib/stores/user.store.ts b/web/src/lib/stores/user.store.ts
index 506782f48a..8d422d3704 100644
--- a/web/src/lib/stores/user.store.ts
+++ b/web/src/lib/stores/user.store.ts
@@ -1,7 +1,8 @@
-import type { UserAdminResponseDto } from '@immich/sdk';
+import { type UserAdminResponseDto, type UserPreferencesResponseDto } from '@immich/sdk';
 import { writable } from 'svelte/store';
 
 export const user = writable<UserAdminResponseDto>();
+export const preferences = writable<UserPreferencesResponseDto>();
 
 /**
  * Reset the store to its initial undefined value. Make sure to
@@ -9,4 +10,5 @@ export const user = writable<UserAdminResponseDto>();
  */
 export const resetSavedUser = () => {
   user.set(undefined as unknown as UserAdminResponseDto);
+  preferences.set(undefined as unknown as UserPreferencesResponseDto);
 };
diff --git a/web/src/lib/utils/auth.ts b/web/src/lib/utils/auth.ts
index 91beb0293f..df5c9bc46a 100644
--- a/web/src/lib/utils/auth.ts
+++ b/web/src/lib/utils/auth.ts
@@ -1,7 +1,7 @@
 import { browser } from '$app/environment';
 import { serverInfo } from '$lib/stores/server-info.store';
-import { user } from '$lib/stores/user.store';
-import { getMyUser, getStorage } from '@immich/sdk';
+import { preferences as preferences$, user as user$ } from '$lib/stores/user.store';
+import { getMyPreferences, getMyUser, getStorage } from '@immich/sdk';
 import { redirect } from '@sveltejs/kit';
 import { get } from 'svelte/store';
 import { AppRoute } from '../constants';
@@ -13,12 +13,14 @@ export interface AuthOptions {
 
 export const loadUser = async () => {
   try {
-    let loaded = get(user);
-    if (!loaded && hasAuthCookie()) {
-      loaded = await getMyUser();
-      user.set(loaded);
+    let user = get(user$);
+    let preferences = get(preferences$);
+    if ((!user || !preferences) && hasAuthCookie()) {
+      [user, preferences] = await Promise.all([getMyUser(), getMyPreferences()]);
+      user$.set(user);
+      preferences$.set(preferences);
     }
-    return loaded;
+    return user;
   } catch {
     return null;
   }
@@ -57,7 +59,7 @@ export const authenticate = async (options?: AuthOptions) => {
 };
 
 export const requestServerInfo = async () => {
-  if (get(user)) {
+  if (get(user$)) {
     const data = await getStorage();
     serverInfo.set(data);
   }
diff --git a/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte
index f711b081d6..d510edd25d 100644
--- a/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte
+++ b/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte
@@ -22,7 +22,7 @@
   import { openFileUploadDialog } from '$lib/utils/file-uploader';
   import { assetViewingStore } from '$lib/stores/asset-viewing.store';
   import { mdiDotsVertical, mdiPlus } from '@mdi/js';
-  import { user } from '$lib/stores/user.store';
+  import { preferences, user } from '$lib/stores/user.store';
 
   let { isViewing: showAssetViewer } = assetViewingStore;
   let handleEscapeKey = false;
@@ -98,7 +98,7 @@
     on:escape={handleEscape}
     withStacked
   >
-    {#if $user.memoriesEnabled}
+    {#if $preferences.memories.enabled}
       <MemoryLane />
     {/if}
     <EmptyPlaceholder text="CLICK TO UPLOAD YOUR FIRST PHOTO" onClick={() => openFileUploadDialog()} slot="empty" />