Procházet zdrojové kódy

fix: replace first and last name with single field (#4915)

Brian Austin před 1 rokem
rodič
revize
7fca0d8da5
98 změnil soubory, kde provedl 454 přidání a 1034 odebrání
  1. 19 67
      cli/src/api/open-api/api.ts
  2. 1 2
      docs/docs/administration/server-commands.md
  3. 1 1
      mobile/assets/i18n/en-US.json
  4. 1 2
      mobile/lib/modules/activities/models/activity.model.dart
  5. 1 1
      mobile/lib/modules/activities/views/activities_page.dart
  6. 2 2
      mobile/lib/modules/album/views/album_options_part.dart
  7. 7 13
      mobile/lib/modules/login/models/authentication_state.model.dart
  8. 3 6
      mobile/lib/modules/login/providers/authentication.provider.dart
  9. 1 2
      mobile/lib/modules/login/ui/change_password_form.dart
  10. 1 1
      mobile/lib/modules/partner/ui/partner_list.dart
  11. 2 2
      mobile/lib/modules/partner/views/partner_detail_page.dart
  12. 2 2
      mobile/lib/modules/partner/views/partner_page.dart
  13. 2 5
      mobile/lib/shared/models/album.dart
  14. 6 12
      mobile/lib/shared/models/user.dart
  15. 108 286
      mobile/lib/shared/models/user.g.dart
  16. 1 1
      mobile/lib/shared/ui/app_bar_dialog/app_bar_profile_info.dart
  17. 2 3
      mobile/lib/shared/ui/user_avatar.dart
  18. 1 1
      mobile/lib/shared/ui/user_circle_avatar.dart
  19. 1 2
      mobile/openapi/doc/CreateUserDto.md
  20. 1 2
      mobile/openapi/doc/LoginResponseDto.md
  21. 1 2
      mobile/openapi/doc/PartnerResponseDto.md
  22. 1 2
      mobile/openapi/doc/SignUpDto.md
  23. 1 2
      mobile/openapi/doc/UpdateUserDto.md
  24. 1 2
      mobile/openapi/doc/UsageByUserDto.md
  25. 1 2
      mobile/openapi/doc/UserDto.md
  26. 1 2
      mobile/openapi/doc/UserResponseDto.md
  27. 9 17
      mobile/openapi/lib/model/create_user_dto.dart
  28. 8 16
      mobile/openapi/lib/model/login_response_dto.dart
  29. 9 17
      mobile/openapi/lib/model/partner_response_dto.dart
  30. 8 16
      mobile/openapi/lib/model/sign_up_dto.dart
  31. 12 29
      mobile/openapi/lib/model/update_user_dto.dart
  32. 8 16
      mobile/openapi/lib/model/usage_by_user_dto.dart
  33. 8 16
      mobile/openapi/lib/model/user_dto.dart
  34. 9 17
      mobile/openapi/lib/model/user_response_dto.dart
  35. 4 9
      mobile/openapi/test/create_user_dto_test.dart
  36. 2 7
      mobile/openapi/test/login_response_dto_test.dart
  37. 4 9
      mobile/openapi/test/partner_response_dto_test.dart
  38. 2 7
      mobile/openapi/test/sign_up_dto_test.dart
  39. 4 9
      mobile/openapi/test/update_user_dto_test.dart
  40. 2 7
      mobile/openapi/test/usage_by_user_dto_test.dart
  41. 2 7
      mobile/openapi/test/user_dto_test.dart
  42. 4 9
      mobile/openapi/test/user_response_dto_test.dart
  43. 1 2
      mobile/test/sync_service_test.dart
  44. 23 55
      server/immich-openapi-specs.json
  45. 0 5
      server/package-lock.json
  46. 2 2
      server/src/admin-cli/commands/reset-admin-password.command.ts
  47. 3 10
      server/src/domain/auth/auth.dto.ts
  48. 2 3
      server/src/domain/auth/auth.service.spec.ts
  49. 3 4
      server/src/domain/auth/auth.service.ts
  50. 2 4
      server/src/domain/partner/partner.service.spec.ts
  51. 1 2
      server/src/domain/repositories/user.repository.ts
  52. 1 3
      server/src/domain/server-info/server-info.dto.ts
  53. 6 12
      server/src/domain/server-info/server-info.service.spec.ts
  54. 1 2
      server/src/domain/server-info/server-info.service.ts
  55. 4 8
      server/src/domain/user/dto/create-user.dto.spec.ts
  56. 3 12
      server/src/domain/user/dto/create-user.dto.ts
  57. 1 6
      server/src/domain/user/dto/update-user.dto.ts
  58. 2 4
      server/src/domain/user/response-dto/user-response.dto.ts
  59. 3 6
      server/src/domain/user/user.service.spec.ts
  60. 1 4
      server/src/infra/entities/user.entity.ts
  61. 21 0
      server/src/infra/migrations/1699322864544-UserNameConsolidation.ts
  62. 1 2
      server/src/infra/repositories/user.repository.ts
  63. 3 6
      server/test/e2e/activity.e2e-spec.ts
  64. 2 4
      server/test/e2e/album.e2e-spec.ts
  65. 2 4
      server/test/e2e/asset.e2e-spec.ts
  66. 7 13
      server/test/e2e/auth.e2e-spec.ts
  67. 2 4
      server/test/e2e/library.e2e-spec.ts
  68. 2 4
      server/test/e2e/partner.e2e-spec.ts
  69. 2 3
      server/test/e2e/server-info.e2e-spec.ts
  70. 1 2
      server/test/e2e/shared-link.e2e-spec.ts
  71. 1 2
      server/test/e2e/system-config.e2e-spec.ts
  72. 8 16
      server/test/e2e/user.e2e-spec.ts
  73. 5 10
      server/test/fixtures/auth.stub.ts
  74. 7 14
      server/test/fixtures/user.stub.ts
  75. 19 67
      web/src/api/open-api/api.ts
  76. 1 1
      web/src/lib/components/admin-page/delete-confirm-dialoge.svelte
  77. 1 1
      web/src/lib/components/admin-page/restore-dialoge.svelte
  78. 1 1
      web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte
  79. 1 2
      web/src/lib/components/album-page/album-card.svelte
  80. 2 2
      web/src/lib/components/album-page/album-options.svelte
  81. 4 4
      web/src/lib/components/album-page/share-info-modal.svelte
  82. 2 3
      web/src/lib/components/album-page/user-selection-modal.svelte
  83. 2 7
      web/src/lib/components/asset-viewer/activity-viewer.svelte
  84. 1 2
      web/src/lib/components/asset-viewer/detail-panel.svelte
  85. 4 11
      web/src/lib/components/forms/admin-registration-form.svelte
  86. 4 11
      web/src/lib/components/forms/create-user-form.svelte
  87. 5 18
      web/src/lib/components/forms/edit-user-form.svelte
  88. 1 2
      web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte
  89. 1 1
      web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte
  90. 3 4
      web/src/lib/components/shared-components/user-avatar.svelte
  91. 1 2
      web/src/lib/components/user-settings-page/partner-selection-modal.svelte
  92. 5 6
      web/src/lib/components/user-settings-page/partner-settings.svelte
  93. 2 15
      web/src/lib/components/user-settings-page/user-profile-settings.svelte
  94. 1 2
      web/src/routes/(user)/partners/[userId]/+page.svelte
  95. 1 2
      web/src/routes/(user)/sharing/+page.svelte
  96. 3 5
      web/src/routes/admin/user-management/+page.svelte
  97. 1 2
      web/src/routes/auth/change-password/+page.svelte
  98. 1 2
      web/src/test-data/factories/user-factory.ts

+ 19 - 67
cli/src/api/open-api/api.ts

@@ -1343,22 +1343,16 @@ export interface CreateUserDto {
     'externalPath'?: string | null;
     /**
      * 
-     * @type {string}
+     * @type {boolean}
      * @memberof CreateUserDto
      */
-    'firstName': string;
+    'memoriesEnabled'?: boolean;
     /**
      * 
      * @type {string}
      * @memberof CreateUserDto
      */
-    'lastName': string;
-    /**
-     * 
-     * @type {boolean}
-     * @memberof CreateUserDto
-     */
-    'memoriesEnabled'?: boolean;
+    'name': string;
     /**
      * 
      * @type {string}
@@ -2137,12 +2131,6 @@ export interface LoginResponseDto {
      * @memberof LoginResponseDto
      */
     'accessToken': string;
-    /**
-     * 
-     * @type {string}
-     * @memberof LoginResponseDto
-     */
-    'firstName': string;
     /**
      * 
      * @type {boolean}
@@ -2154,7 +2142,7 @@ export interface LoginResponseDto {
      * @type {string}
      * @memberof LoginResponseDto
      */
-    'lastName': string;
+    'name': string;
     /**
      * 
      * @type {string}
@@ -2391,12 +2379,6 @@ export interface PartnerResponseDto {
      * @memberof PartnerResponseDto
      */
     'externalPath': string | null;
-    /**
-     * 
-     * @type {string}
-     * @memberof PartnerResponseDto
-     */
-    'firstName': string;
     /**
      * 
      * @type {string}
@@ -2417,16 +2399,16 @@ export interface PartnerResponseDto {
     'isAdmin': boolean;
     /**
      * 
-     * @type {string}
+     * @type {boolean}
      * @memberof PartnerResponseDto
      */
-    'lastName': string;
+    'memoriesEnabled'?: boolean;
     /**
      * 
-     * @type {boolean}
+     * @type {string}
      * @memberof PartnerResponseDto
      */
-    'memoriesEnabled'?: boolean;
+    'name': string;
     /**
      * 
      * @type {string}
@@ -3431,13 +3413,7 @@ export interface SignUpDto {
      * @type {string}
      * @memberof SignUpDto
      */
-    'firstName': string;
-    /**
-     * 
-     * @type {string}
-     * @memberof SignUpDto
-     */
-    'lastName': string;
+    'name': string;
     /**
      * 
      * @type {string}
@@ -4380,12 +4356,6 @@ export interface UpdateUserDto {
      * @memberof UpdateUserDto
      */
     'externalPath'?: string;
-    /**
-     * 
-     * @type {string}
-     * @memberof UpdateUserDto
-     */
-    'firstName'?: string;
     /**
      * 
      * @type {string}
@@ -4400,16 +4370,16 @@ export interface UpdateUserDto {
     'isAdmin'?: boolean;
     /**
      * 
-     * @type {string}
+     * @type {boolean}
      * @memberof UpdateUserDto
      */
-    'lastName'?: string;
+    'memoriesEnabled'?: boolean;
     /**
      * 
-     * @type {boolean}
+     * @type {string}
      * @memberof UpdateUserDto
      */
-    'memoriesEnabled'?: boolean;
+    'name'?: string;
     /**
      * 
      * @type {string}
@@ -4447,12 +4417,6 @@ export interface UsageByUserDto {
      * @memberof UsageByUserDto
      */
     'usage': number;
-    /**
-     * 
-     * @type {string}
-     * @memberof UsageByUserDto
-     */
-    'userFirstName': string;
     /**
      * 
      * @type {string}
@@ -4464,7 +4428,7 @@ export interface UsageByUserDto {
      * @type {string}
      * @memberof UsageByUserDto
      */
-    'userLastName': string;
+    'userName': string;
     /**
      * 
      * @type {number}
@@ -4484,12 +4448,6 @@ export interface UserDto {
      * @memberof UserDto
      */
     'email': string;
-    /**
-     * 
-     * @type {string}
-     * @memberof UserDto
-     */
-    'firstName': string;
     /**
      * 
      * @type {string}
@@ -4501,7 +4459,7 @@ export interface UserDto {
      * @type {string}
      * @memberof UserDto
      */
-    'lastName': string;
+    'name': string;
     /**
      * 
      * @type {string}
@@ -4539,12 +4497,6 @@ export interface UserResponseDto {
      * @memberof UserResponseDto
      */
     'externalPath': string | null;
-    /**
-     * 
-     * @type {string}
-     * @memberof UserResponseDto
-     */
-    'firstName': string;
     /**
      * 
      * @type {string}
@@ -4559,16 +4511,16 @@ export interface UserResponseDto {
     'isAdmin': boolean;
     /**
      * 
-     * @type {string}
+     * @type {boolean}
      * @memberof UserResponseDto
      */
-    'lastName': string;
+    'memoriesEnabled'?: boolean;
     /**
      * 
-     * @type {boolean}
+     * @type {string}
      * @memberof UserResponseDto
      */
-    'memoriesEnabled'?: boolean;
+    'name': string;
     /**
      * 
      * @type {string}

+ 1 - 2
docs/docs/administration/server-commands.md

@@ -51,8 +51,7 @@ immich-admin list-users
   {
     id: 'e65e6f88-2a30-4dbe-8dd9-1885f4889b53',
     email: 'immich@example.com.com',
-    firstName: 'Immich',
-    lastName: 'Admin',
+    name: 'Immich Admin',
     storageLabel: 'admin',
     externalPath: null,
     profileImagePath: 'upload/profile/e65e6f88-2a30-4dbe-8dd9-1885f4889b53/e65e6f88-2a30-4dbe-8dd9-1885f4889b53.jpg',

+ 1 - 1
mobile/assets/i18n/en-US.json

@@ -114,7 +114,7 @@
   "cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)",
   "cache_settings_title": "Caching Settings",
   "change_password_form_confirm_password": "Confirm Password",
-  "change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
+  "change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
   "change_password_form_new_password": "New Password",
   "change_password_form_password_mismatch": "Passwords do not match",
   "change_password_form_reenter_new_password": "Re-enter New Password",

+ 1 - 2
mobile/lib/modules/activities/models/activity.model.dart

@@ -48,8 +48,7 @@ class Activity {
             : ActivityType.like,
         user = User(
           email: dto.user.email,
-          firstName: dto.user.firstName,
-          lastName: dto.user.lastName,
+          name: dto.user.name,
           profileImagePath: dto.user.profileImagePath,
           id: dto.user.id,
           // Placeholder values

+ 1 - 1
mobile/lib/modules/activities/views/activities_page.dart

@@ -61,7 +61,7 @@ class ActivitiesPage extends HookConsumerWidget {
         mainAxisSize: leftAlign ? MainAxisSize.min : MainAxisSize.max,
         children: [
           Text(
-            "${activity.user.firstName} ${activity.user.lastName}",
+            activity.user.name,
             style: textStyle,
             overflow: TextOverflow.ellipsis,
           ),

+ 2 - 2
mobile/lib/modules/album/views/album_options_part.dart

@@ -124,7 +124,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
               )
             : const SizedBox(),
         title: Text(
-          album.owner.value?.firstName ?? "",
+          album.owner.value?.name ?? "",
           style: const TextStyle(
             fontWeight: FontWeight.bold,
           ),
@@ -155,7 +155,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
               radius: 22,
             ),
             title: Text(
-              user.firstName,
+              user.name,
               style: const TextStyle(
                 fontWeight: FontWeight.bold,
               ),

+ 7 - 13
mobile/lib/modules/login/models/authentication_state.model.dart

@@ -3,8 +3,7 @@ class AuthenticationState {
   final String userId;
   final String userEmail;
   final bool isAuthenticated;
-  final String firstName;
-  final String lastName;
+  final String name;
   final bool isAdmin;
   final bool shouldChangePassword;
   final String profileImagePath;
@@ -13,8 +12,7 @@ class AuthenticationState {
     required this.userId,
     required this.userEmail,
     required this.isAuthenticated,
-    required this.firstName,
-    required this.lastName,
+    required this.name,
     required this.isAdmin,
     required this.shouldChangePassword,
     required this.profileImagePath,
@@ -25,8 +23,7 @@ class AuthenticationState {
     String? userId,
     String? userEmail,
     bool? isAuthenticated,
-    String? firstName,
-    String? lastName,
+    String? name,
     bool? isAdmin,
     bool? shouldChangePassword,
     String? profileImagePath,
@@ -36,8 +33,7 @@ class AuthenticationState {
       userId: userId ?? this.userId,
       userEmail: userEmail ?? this.userEmail,
       isAuthenticated: isAuthenticated ?? this.isAuthenticated,
-      firstName: firstName ?? this.firstName,
-      lastName: lastName ?? this.lastName,
+      name: name ?? this.name,
       isAdmin: isAdmin ?? this.isAdmin,
       shouldChangePassword: shouldChangePassword ?? this.shouldChangePassword,
       profileImagePath: profileImagePath ?? this.profileImagePath,
@@ -46,7 +42,7 @@ class AuthenticationState {
 
   @override
   String toString() {
-    return 'AuthenticationState(deviceId: $deviceId, userId: $userId, userEmail: $userEmail, isAuthenticated: $isAuthenticated, firstName: $firstName, lastName: $lastName, isAdmin: $isAdmin, shouldChangePassword: $shouldChangePassword, profileImagePath: $profileImagePath)';
+    return 'AuthenticationState(deviceId: $deviceId, userId: $userId, userEmail: $userEmail, isAuthenticated: $isAuthenticated, name: $name, isAdmin: $isAdmin, shouldChangePassword: $shouldChangePassword, profileImagePath: $profileImagePath)';
   }
 
   @override
@@ -58,8 +54,7 @@ class AuthenticationState {
         other.userId == userId &&
         other.userEmail == userEmail &&
         other.isAuthenticated == isAuthenticated &&
-        other.firstName == firstName &&
-        other.lastName == lastName &&
+        other.name == name &&
         other.isAdmin == isAdmin &&
         other.shouldChangePassword == shouldChangePassword &&
         other.profileImagePath == profileImagePath;
@@ -71,8 +66,7 @@ class AuthenticationState {
         userId.hashCode ^
         userEmail.hashCode ^
         isAuthenticated.hashCode ^
-        firstName.hashCode ^
-        lastName.hashCode ^
+        name.hashCode ^
         isAdmin.hashCode ^
         shouldChangePassword.hashCode ^
         profileImagePath.hashCode;

+ 3 - 6
mobile/lib/modules/login/providers/authentication.provider.dart

@@ -26,8 +26,7 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
             deviceId: "",
             userId: "",
             userEmail: "",
-            firstName: '',
-            lastName: '',
+            name: '',
             profileImagePath: '',
             isAdmin: false,
             shouldChangePassword: false,
@@ -117,8 +116,7 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
         deviceId: "",
         userId: "",
         userEmail: "",
-        firstName: '',
-        lastName: '',
+        name: '',
         profileImagePath: '',
         isAdmin: false,
         shouldChangePassword: false,
@@ -208,8 +206,7 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
       isAuthenticated: true,
       userId: user.id,
       userEmail: user.email,
-      firstName: user.firstName,
-      lastName: user.lastName,
+      name: user.name,
       profileImagePath: user.profileImagePath,
       isAdmin: user.isAdmin,
       shouldChangePassword: shouldChangePassword,

+ 1 - 2
mobile/lib/modules/login/ui/change_password_form.dart

@@ -46,8 +46,7 @@ class ChangePasswordForm extends HookConsumerWidget {
                 child: Text(
                   'change_password_form_description'.tr(
                     namedArgs: {
-                      'firstName': authState.firstName,
-                      'lastName': authState.lastName,
+                      'name': authState.name,
                     },
                   ),
                   style: TextStyle(

+ 1 - 1
mobile/lib/modules/partner/ui/partner_list.dart

@@ -24,7 +24,7 @@ class PartnerList extends HookConsumerWidget {
       contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
       leading: userAvatar(context, p, radius: 30),
       title: Text(
-        "${p.firstName} ${p.lastName}'s photos",
+        "${p.name}'s photos",
         style: TextStyle(
           fontWeight: FontWeight.bold,
           fontSize: 14,

+ 2 - 2
mobile/lib/modules/partner/views/partner_detail_page.dart

@@ -25,7 +25,7 @@ class PartnerDetailPage extends HookConsumerWidget {
 
     return Scaffold(
       appBar: AppBar(
-        title: Text("${partner.firstName} ${partner.lastName}"),
+        title: Text(partner.name),
         elevation: 0,
         centerTitle: false,
       ),
@@ -34,7 +34,7 @@ class PartnerDetailPage extends HookConsumerWidget {
             ? Padding(
                 padding: const EdgeInsets.all(16),
                 child: Text(
-                    "It seems ${partner.firstName} does not have any photos...\n"
+                    "It seems ${partner.name} does not have any photos...\n"
                     "Or your server version does not match the app version."),
               )
             : ImmichAssetGrid(

+ 2 - 2
mobile/lib/modules/partner/views/partner_page.dart

@@ -41,7 +41,7 @@ class PartnerPage extends HookConsumerWidget {
                         padding: const EdgeInsets.only(right: 8),
                         child: userAvatar(context, u),
                       ),
-                      Text("${u.firstName} ${u.lastName}"),
+                      Text(u.name),
                     ],
                   ),
                 ),
@@ -71,7 +71,7 @@ class PartnerPage extends HookConsumerWidget {
           return ConfirmDialog(
             title: "partner_page_stop_sharing_title",
             content:
-                "partner_page_stop_sharing_content".tr(args: [u.firstName]),
+                "partner_page_stop_sharing_content".tr(args: [u.name]),
             onOk: () => ref.read(partnerServiceProvider).removePartner(u),
           );
         },

+ 2 - 5
mobile/lib/shared/models/album.dart

@@ -68,11 +68,8 @@ class Album {
     }
 
     final name = <String>[];
-    if (owner.value?.firstName != null) {
-      name.add(owner.value!.firstName);
-    }
-    if (owner.value?.lastName != null) {
-      name.add(owner.value!.lastName);
+    if (owner.value?.name != null) {
+      name.add(owner.value!.name);
     }
 
     return name.join(' ');

+ 6 - 12
mobile/lib/shared/models/user.dart

@@ -11,8 +11,7 @@ class User {
     required this.id,
     required this.updatedAt,
     required this.email,
-    required this.firstName,
-    required this.lastName,
+    required this.name,
     required this.isAdmin,
     this.isPartnerSharedBy = false,
     this.isPartnerSharedWith = false,
@@ -27,8 +26,7 @@ class User {
       : id = dto.id,
         updatedAt = dto.updatedAt,
         email = dto.email,
-        firstName = dto.firstName,
-        lastName = dto.lastName,
+        name = dto.name,
         isPartnerSharedBy = false,
         isPartnerSharedWith = false,
         profileImagePath = dto.profileImagePath,
@@ -39,8 +37,7 @@ class User {
       : id = dto.id,
         updatedAt = dto.updatedAt,
         email = dto.email,
-        firstName = dto.firstName,
-        lastName = dto.lastName,
+        name = dto.name,
         isPartnerSharedBy = false,
         isPartnerSharedWith = false,
         profileImagePath = dto.profileImagePath,
@@ -52,8 +49,7 @@ class User {
   String id;
   DateTime updatedAt;
   String email;
-  String firstName;
-  String lastName;
+  String name;
   bool isPartnerSharedBy;
   bool isPartnerSharedWith;
   bool isAdmin;
@@ -72,8 +68,7 @@ class User {
     return id == other.id &&
         updatedAt.isAtSameMomentAs(other.updatedAt) &&
         email == other.email &&
-        firstName == other.firstName &&
-        lastName == other.lastName &&
+        name == other.name &&
         isPartnerSharedBy == other.isPartnerSharedBy &&
         isPartnerSharedWith == other.isPartnerSharedWith &&
         profileImagePath == other.profileImagePath &&
@@ -88,8 +83,7 @@ class User {
       id.hashCode ^
       updatedAt.hashCode ^
       email.hashCode ^
-      firstName.hashCode ^
-      lastName.hashCode ^
+      name.hashCode ^
       isPartnerSharedBy.hashCode ^
       isPartnerSharedWith.hashCode ^
       profileImagePath.hashCode ^

+ 108 - 286
mobile/lib/shared/models/user.g.dart

@@ -22,53 +22,48 @@ const UserSchema = CollectionSchema(
       name: r'email',
       type: IsarType.string,
     ),
-    r'firstName': PropertySchema(
-      id: 1,
-      name: r'firstName',
-      type: IsarType.string,
-    ),
     r'id': PropertySchema(
-      id: 2,
+      id: 1,
       name: r'id',
       type: IsarType.string,
     ),
     r'inTimeline': PropertySchema(
-      id: 3,
+      id: 2,
       name: r'inTimeline',
       type: IsarType.bool,
     ),
     r'isAdmin': PropertySchema(
-      id: 4,
+      id: 3,
       name: r'isAdmin',
       type: IsarType.bool,
     ),
     r'isPartnerSharedBy': PropertySchema(
-      id: 5,
+      id: 4,
       name: r'isPartnerSharedBy',
       type: IsarType.bool,
     ),
     r'isPartnerSharedWith': PropertySchema(
-      id: 6,
+      id: 5,
       name: r'isPartnerSharedWith',
       type: IsarType.bool,
     ),
-    r'lastName': PropertySchema(
-      id: 7,
-      name: r'lastName',
-      type: IsarType.string,
-    ),
     r'memoryEnabled': PropertySchema(
-      id: 8,
+      id: 6,
       name: r'memoryEnabled',
       type: IsarType.bool,
     ),
+    r'name': PropertySchema(
+      id: 7,
+      name: r'name',
+      type: IsarType.string,
+    ),
     r'profileImagePath': PropertySchema(
-      id: 9,
+      id: 8,
       name: r'profileImagePath',
       type: IsarType.string,
     ),
     r'updatedAt': PropertySchema(
-      id: 10,
+      id: 9,
       name: r'updatedAt',
       type: IsarType.dateTime,
     )
@@ -123,9 +118,8 @@ int _userEstimateSize(
 ) {
   var bytesCount = offsets.last;
   bytesCount += 3 + object.email.length * 3;
-  bytesCount += 3 + object.firstName.length * 3;
   bytesCount += 3 + object.id.length * 3;
-  bytesCount += 3 + object.lastName.length * 3;
+  bytesCount += 3 + object.name.length * 3;
   bytesCount += 3 + object.profileImagePath.length * 3;
   return bytesCount;
 }
@@ -137,16 +131,15 @@ void _userSerialize(
   Map<Type, List<int>> allOffsets,
 ) {
   writer.writeString(offsets[0], object.email);
-  writer.writeString(offsets[1], object.firstName);
-  writer.writeString(offsets[2], object.id);
-  writer.writeBool(offsets[3], object.inTimeline);
-  writer.writeBool(offsets[4], object.isAdmin);
-  writer.writeBool(offsets[5], object.isPartnerSharedBy);
-  writer.writeBool(offsets[6], object.isPartnerSharedWith);
-  writer.writeString(offsets[7], object.lastName);
-  writer.writeBool(offsets[8], object.memoryEnabled);
-  writer.writeString(offsets[9], object.profileImagePath);
-  writer.writeDateTime(offsets[10], object.updatedAt);
+  writer.writeString(offsets[1], object.id);
+  writer.writeBool(offsets[2], object.inTimeline);
+  writer.writeBool(offsets[3], object.isAdmin);
+  writer.writeBool(offsets[4], object.isPartnerSharedBy);
+  writer.writeBool(offsets[5], object.isPartnerSharedWith);
+  writer.writeBool(offsets[6], object.memoryEnabled);
+  writer.writeString(offsets[7], object.name);
+  writer.writeString(offsets[8], object.profileImagePath);
+  writer.writeDateTime(offsets[9], object.updatedAt);
 }
 
 User _userDeserialize(
@@ -157,16 +150,15 @@ User _userDeserialize(
 ) {
   final object = User(
     email: reader.readString(offsets[0]),
-    firstName: reader.readString(offsets[1]),
-    id: reader.readString(offsets[2]),
-    inTimeline: reader.readBoolOrNull(offsets[3]),
-    isAdmin: reader.readBool(offsets[4]),
-    isPartnerSharedBy: reader.readBoolOrNull(offsets[5]) ?? false,
-    isPartnerSharedWith: reader.readBoolOrNull(offsets[6]) ?? false,
-    lastName: reader.readString(offsets[7]),
-    memoryEnabled: reader.readBoolOrNull(offsets[8]),
-    profileImagePath: reader.readStringOrNull(offsets[9]) ?? '',
-    updatedAt: reader.readDateTime(offsets[10]),
+    id: reader.readString(offsets[1]),
+    inTimeline: reader.readBoolOrNull(offsets[2]),
+    isAdmin: reader.readBool(offsets[3]),
+    isPartnerSharedBy: reader.readBoolOrNull(offsets[4]) ?? false,
+    isPartnerSharedWith: reader.readBoolOrNull(offsets[5]) ?? false,
+    memoryEnabled: reader.readBoolOrNull(offsets[6]),
+    name: reader.readString(offsets[7]),
+    profileImagePath: reader.readStringOrNull(offsets[8]) ?? '',
+    updatedAt: reader.readDateTime(offsets[9]),
   );
   return object;
 }
@@ -183,22 +175,20 @@ P _userDeserializeProp<P>(
     case 1:
       return (reader.readString(offset)) as P;
     case 2:
-      return (reader.readString(offset)) as P;
-    case 3:
       return (reader.readBoolOrNull(offset)) as P;
-    case 4:
+    case 3:
       return (reader.readBool(offset)) as P;
+    case 4:
+      return (reader.readBoolOrNull(offset) ?? false) as P;
     case 5:
       return (reader.readBoolOrNull(offset) ?? false) as P;
     case 6:
-      return (reader.readBoolOrNull(offset) ?? false) as P;
+      return (reader.readBoolOrNull(offset)) as P;
     case 7:
       return (reader.readString(offset)) as P;
     case 8:
-      return (reader.readBoolOrNull(offset)) as P;
-    case 9:
       return (reader.readStringOrNull(offset) ?? '') as P;
-    case 10:
+    case 9:
       return (reader.readDateTime(offset)) as P;
     default:
       throw IsarError('Unknown property with id $propertyId');
@@ -520,136 +510,6 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
     });
   }
 
-  QueryBuilder<User, User, QAfterFilterCondition> firstNameEqualTo(
-    String value, {
-    bool caseSensitive = true,
-  }) {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(FilterCondition.equalTo(
-        property: r'firstName',
-        value: value,
-        caseSensitive: caseSensitive,
-      ));
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> firstNameGreaterThan(
-    String value, {
-    bool include = false,
-    bool caseSensitive = true,
-  }) {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(FilterCondition.greaterThan(
-        include: include,
-        property: r'firstName',
-        value: value,
-        caseSensitive: caseSensitive,
-      ));
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> firstNameLessThan(
-    String value, {
-    bool include = false,
-    bool caseSensitive = true,
-  }) {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(FilterCondition.lessThan(
-        include: include,
-        property: r'firstName',
-        value: value,
-        caseSensitive: caseSensitive,
-      ));
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> firstNameBetween(
-    String lower,
-    String upper, {
-    bool includeLower = true,
-    bool includeUpper = true,
-    bool caseSensitive = true,
-  }) {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(FilterCondition.between(
-        property: r'firstName',
-        lower: lower,
-        includeLower: includeLower,
-        upper: upper,
-        includeUpper: includeUpper,
-        caseSensitive: caseSensitive,
-      ));
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> firstNameStartsWith(
-    String value, {
-    bool caseSensitive = true,
-  }) {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(FilterCondition.startsWith(
-        property: r'firstName',
-        value: value,
-        caseSensitive: caseSensitive,
-      ));
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> firstNameEndsWith(
-    String value, {
-    bool caseSensitive = true,
-  }) {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(FilterCondition.endsWith(
-        property: r'firstName',
-        value: value,
-        caseSensitive: caseSensitive,
-      ));
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> firstNameContains(
-      String value,
-      {bool caseSensitive = true}) {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(FilterCondition.contains(
-        property: r'firstName',
-        value: value,
-        caseSensitive: caseSensitive,
-      ));
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> firstNameMatches(
-      String pattern,
-      {bool caseSensitive = true}) {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(FilterCondition.matches(
-        property: r'firstName',
-        wildcard: pattern,
-        caseSensitive: caseSensitive,
-      ));
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> firstNameIsEmpty() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(FilterCondition.equalTo(
-        property: r'firstName',
-        value: '',
-      ));
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> firstNameIsNotEmpty() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(FilterCondition.greaterThan(
-        property: r'firstName',
-        value: '',
-      ));
-    });
-  }
-
   QueryBuilder<User, User, QAfterFilterCondition> idEqualTo(
     String value, {
     bool caseSensitive = true,
@@ -885,20 +745,46 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
     });
   }
 
-  QueryBuilder<User, User, QAfterFilterCondition> lastNameEqualTo(
+  QueryBuilder<User, User, QAfterFilterCondition> memoryEnabledIsNull() {
+    return QueryBuilder.apply(this, (query) {
+      return query.addFilterCondition(const FilterCondition.isNull(
+        property: r'memoryEnabled',
+      ));
+    });
+  }
+
+  QueryBuilder<User, User, QAfterFilterCondition> memoryEnabledIsNotNull() {
+    return QueryBuilder.apply(this, (query) {
+      return query.addFilterCondition(const FilterCondition.isNotNull(
+        property: r'memoryEnabled',
+      ));
+    });
+  }
+
+  QueryBuilder<User, User, QAfterFilterCondition> memoryEnabledEqualTo(
+      bool? value) {
+    return QueryBuilder.apply(this, (query) {
+      return query.addFilterCondition(FilterCondition.equalTo(
+        property: r'memoryEnabled',
+        value: value,
+      ));
+    });
+  }
+
+  QueryBuilder<User, User, QAfterFilterCondition> nameEqualTo(
     String value, {
     bool caseSensitive = true,
   }) {
     return QueryBuilder.apply(this, (query) {
       return query.addFilterCondition(FilterCondition.equalTo(
-        property: r'lastName',
+        property: r'name',
         value: value,
         caseSensitive: caseSensitive,
       ));
     });
   }
 
-  QueryBuilder<User, User, QAfterFilterCondition> lastNameGreaterThan(
+  QueryBuilder<User, User, QAfterFilterCondition> nameGreaterThan(
     String value, {
     bool include = false,
     bool caseSensitive = true,
@@ -906,14 +792,14 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
     return QueryBuilder.apply(this, (query) {
       return query.addFilterCondition(FilterCondition.greaterThan(
         include: include,
-        property: r'lastName',
+        property: r'name',
         value: value,
         caseSensitive: caseSensitive,
       ));
     });
   }
 
-  QueryBuilder<User, User, QAfterFilterCondition> lastNameLessThan(
+  QueryBuilder<User, User, QAfterFilterCondition> nameLessThan(
     String value, {
     bool include = false,
     bool caseSensitive = true,
@@ -921,14 +807,14 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
     return QueryBuilder.apply(this, (query) {
       return query.addFilterCondition(FilterCondition.lessThan(
         include: include,
-        property: r'lastName',
+        property: r'name',
         value: value,
         caseSensitive: caseSensitive,
       ));
     });
   }
 
-  QueryBuilder<User, User, QAfterFilterCondition> lastNameBetween(
+  QueryBuilder<User, User, QAfterFilterCondition> nameBetween(
     String lower,
     String upper, {
     bool includeLower = true,
@@ -937,7 +823,7 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
   }) {
     return QueryBuilder.apply(this, (query) {
       return query.addFilterCondition(FilterCondition.between(
-        property: r'lastName',
+        property: r'name',
         lower: lower,
         includeLower: includeLower,
         upper: upper,
@@ -947,99 +833,72 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
     });
   }
 
-  QueryBuilder<User, User, QAfterFilterCondition> lastNameStartsWith(
+  QueryBuilder<User, User, QAfterFilterCondition> nameStartsWith(
     String value, {
     bool caseSensitive = true,
   }) {
     return QueryBuilder.apply(this, (query) {
       return query.addFilterCondition(FilterCondition.startsWith(
-        property: r'lastName',
+        property: r'name',
         value: value,
         caseSensitive: caseSensitive,
       ));
     });
   }
 
-  QueryBuilder<User, User, QAfterFilterCondition> lastNameEndsWith(
+  QueryBuilder<User, User, QAfterFilterCondition> nameEndsWith(
     String value, {
     bool caseSensitive = true,
   }) {
     return QueryBuilder.apply(this, (query) {
       return query.addFilterCondition(FilterCondition.endsWith(
-        property: r'lastName',
+        property: r'name',
         value: value,
         caseSensitive: caseSensitive,
       ));
     });
   }
 
-  QueryBuilder<User, User, QAfterFilterCondition> lastNameContains(String value,
+  QueryBuilder<User, User, QAfterFilterCondition> nameContains(String value,
       {bool caseSensitive = true}) {
     return QueryBuilder.apply(this, (query) {
       return query.addFilterCondition(FilterCondition.contains(
-        property: r'lastName',
+        property: r'name',
         value: value,
         caseSensitive: caseSensitive,
       ));
     });
   }
 
-  QueryBuilder<User, User, QAfterFilterCondition> lastNameMatches(
-      String pattern,
+  QueryBuilder<User, User, QAfterFilterCondition> nameMatches(String pattern,
       {bool caseSensitive = true}) {
     return QueryBuilder.apply(this, (query) {
       return query.addFilterCondition(FilterCondition.matches(
-        property: r'lastName',
+        property: r'name',
         wildcard: pattern,
         caseSensitive: caseSensitive,
       ));
     });
   }
 
-  QueryBuilder<User, User, QAfterFilterCondition> lastNameIsEmpty() {
+  QueryBuilder<User, User, QAfterFilterCondition> nameIsEmpty() {
     return QueryBuilder.apply(this, (query) {
       return query.addFilterCondition(FilterCondition.equalTo(
-        property: r'lastName',
+        property: r'name',
         value: '',
       ));
     });
   }
 
-  QueryBuilder<User, User, QAfterFilterCondition> lastNameIsNotEmpty() {
+  QueryBuilder<User, User, QAfterFilterCondition> nameIsNotEmpty() {
     return QueryBuilder.apply(this, (query) {
       return query.addFilterCondition(FilterCondition.greaterThan(
-        property: r'lastName',
+        property: r'name',
         value: '',
       ));
     });
   }
 
-  QueryBuilder<User, User, QAfterFilterCondition> memoryEnabledIsNull() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(const FilterCondition.isNull(
-        property: r'memoryEnabled',
-      ));
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> memoryEnabledIsNotNull() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(const FilterCondition.isNotNull(
-        property: r'memoryEnabled',
-      ));
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> memoryEnabledEqualTo(
-      bool? value) {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(FilterCondition.equalTo(
-        property: r'memoryEnabled',
-        value: value,
-      ));
-    });
-  }
-
   QueryBuilder<User, User, QAfterFilterCondition> profileImagePathEqualTo(
     String value, {
     bool caseSensitive = true,
@@ -1352,18 +1211,6 @@ extension UserQuerySortBy on QueryBuilder<User, User, QSortBy> {
     });
   }
 
-  QueryBuilder<User, User, QAfterSortBy> sortByFirstName() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'firstName', Sort.asc);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterSortBy> sortByFirstNameDesc() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'firstName', Sort.desc);
-    });
-  }
-
   QueryBuilder<User, User, QAfterSortBy> sortById() {
     return QueryBuilder.apply(this, (query) {
       return query.addSortBy(r'id', Sort.asc);
@@ -1424,27 +1271,27 @@ extension UserQuerySortBy on QueryBuilder<User, User, QSortBy> {
     });
   }
 
-  QueryBuilder<User, User, QAfterSortBy> sortByLastName() {
+  QueryBuilder<User, User, QAfterSortBy> sortByMemoryEnabled() {
     return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'lastName', Sort.asc);
+      return query.addSortBy(r'memoryEnabled', Sort.asc);
     });
   }
 
-  QueryBuilder<User, User, QAfterSortBy> sortByLastNameDesc() {
+  QueryBuilder<User, User, QAfterSortBy> sortByMemoryEnabledDesc() {
     return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'lastName', Sort.desc);
+      return query.addSortBy(r'memoryEnabled', Sort.desc);
     });
   }
 
-  QueryBuilder<User, User, QAfterSortBy> sortByMemoryEnabled() {
+  QueryBuilder<User, User, QAfterSortBy> sortByName() {
     return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'memoryEnabled', Sort.asc);
+      return query.addSortBy(r'name', Sort.asc);
     });
   }
 
-  QueryBuilder<User, User, QAfterSortBy> sortByMemoryEnabledDesc() {
+  QueryBuilder<User, User, QAfterSortBy> sortByNameDesc() {
     return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'memoryEnabled', Sort.desc);
+      return query.addSortBy(r'name', Sort.desc);
     });
   }
 
@@ -1486,18 +1333,6 @@ extension UserQuerySortThenBy on QueryBuilder<User, User, QSortThenBy> {
     });
   }
 
-  QueryBuilder<User, User, QAfterSortBy> thenByFirstName() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'firstName', Sort.asc);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterSortBy> thenByFirstNameDesc() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'firstName', Sort.desc);
-    });
-  }
-
   QueryBuilder<User, User, QAfterSortBy> thenById() {
     return QueryBuilder.apply(this, (query) {
       return query.addSortBy(r'id', Sort.asc);
@@ -1570,27 +1405,27 @@ extension UserQuerySortThenBy on QueryBuilder<User, User, QSortThenBy> {
     });
   }
 
-  QueryBuilder<User, User, QAfterSortBy> thenByLastName() {
+  QueryBuilder<User, User, QAfterSortBy> thenByMemoryEnabled() {
     return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'lastName', Sort.asc);
+      return query.addSortBy(r'memoryEnabled', Sort.asc);
     });
   }
 
-  QueryBuilder<User, User, QAfterSortBy> thenByLastNameDesc() {
+  QueryBuilder<User, User, QAfterSortBy> thenByMemoryEnabledDesc() {
     return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'lastName', Sort.desc);
+      return query.addSortBy(r'memoryEnabled', Sort.desc);
     });
   }
 
-  QueryBuilder<User, User, QAfterSortBy> thenByMemoryEnabled() {
+  QueryBuilder<User, User, QAfterSortBy> thenByName() {
     return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'memoryEnabled', Sort.asc);
+      return query.addSortBy(r'name', Sort.asc);
     });
   }
 
-  QueryBuilder<User, User, QAfterSortBy> thenByMemoryEnabledDesc() {
+  QueryBuilder<User, User, QAfterSortBy> thenByNameDesc() {
     return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'memoryEnabled', Sort.desc);
+      return query.addSortBy(r'name', Sort.desc);
     });
   }
 
@@ -1627,13 +1462,6 @@ extension UserQueryWhereDistinct on QueryBuilder<User, User, QDistinct> {
     });
   }
 
-  QueryBuilder<User, User, QDistinct> distinctByFirstName(
-      {bool caseSensitive = true}) {
-    return QueryBuilder.apply(this, (query) {
-      return query.addDistinctBy(r'firstName', caseSensitive: caseSensitive);
-    });
-  }
-
   QueryBuilder<User, User, QDistinct> distinctById(
       {bool caseSensitive = true}) {
     return QueryBuilder.apply(this, (query) {
@@ -1665,16 +1493,16 @@ extension UserQueryWhereDistinct on QueryBuilder<User, User, QDistinct> {
     });
   }
 
-  QueryBuilder<User, User, QDistinct> distinctByLastName(
-      {bool caseSensitive = true}) {
+  QueryBuilder<User, User, QDistinct> distinctByMemoryEnabled() {
     return QueryBuilder.apply(this, (query) {
-      return query.addDistinctBy(r'lastName', caseSensitive: caseSensitive);
+      return query.addDistinctBy(r'memoryEnabled');
     });
   }
 
-  QueryBuilder<User, User, QDistinct> distinctByMemoryEnabled() {
+  QueryBuilder<User, User, QDistinct> distinctByName(
+      {bool caseSensitive = true}) {
     return QueryBuilder.apply(this, (query) {
-      return query.addDistinctBy(r'memoryEnabled');
+      return query.addDistinctBy(r'name', caseSensitive: caseSensitive);
     });
   }
 
@@ -1706,12 +1534,6 @@ extension UserQueryProperty on QueryBuilder<User, User, QQueryProperty> {
     });
   }
 
-  QueryBuilder<User, String, QQueryOperations> firstNameProperty() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addPropertyName(r'firstName');
-    });
-  }
-
   QueryBuilder<User, String, QQueryOperations> idProperty() {
     return QueryBuilder.apply(this, (query) {
       return query.addPropertyName(r'id');
@@ -1742,15 +1564,15 @@ extension UserQueryProperty on QueryBuilder<User, User, QQueryProperty> {
     });
   }
 
-  QueryBuilder<User, String, QQueryOperations> lastNameProperty() {
+  QueryBuilder<User, bool?, QQueryOperations> memoryEnabledProperty() {
     return QueryBuilder.apply(this, (query) {
-      return query.addPropertyName(r'lastName');
+      return query.addPropertyName(r'memoryEnabled');
     });
   }
 
-  QueryBuilder<User, bool?, QQueryOperations> memoryEnabledProperty() {
+  QueryBuilder<User, String, QQueryOperations> nameProperty() {
     return QueryBuilder.apply(this, (query) {
-      return query.addPropertyName(r'memoryEnabled');
+      return query.addPropertyName(r'name');
     });
   }
 

+ 1 - 1
mobile/lib/shared/ui/app_bar_dialog/app_bar_profile_info.dart

@@ -132,7 +132,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
             ),
           ),
           title: Text(
-            "${authState.firstName} ${authState.lastName}",
+            authState.name,
             style: TextStyle(
               color: context.primaryColor,
               fontWeight: FontWeight.bold,

+ 2 - 3
mobile/lib/shared/ui/user_avatar.dart

@@ -7,8 +7,7 @@ import 'package:immich_mobile/shared/models/user.dart';
 Widget userAvatar(BuildContext context, User u, {double? radius}) {
   final url =
       "${Store.get(StoreKey.serverEndpoint)}/user/profile-image/${u.id}";
-  final firstNameFirstLetter = u.firstName.isNotEmpty ? u.firstName[0] : "";
-  final lastNameFirstLetter = u.lastName.isNotEmpty ? u.lastName[0] : "";
+  final nameFirstLetter = u.name.isNotEmpty ? u.name[0] : "";
   return CircleAvatar(
     radius: radius,
     backgroundColor: context.primaryColor.withAlpha(50),
@@ -19,6 +18,6 @@ Widget userAvatar(BuildContext context, User u, {double? radius}) {
     ),
     // silence errors if user has no profile image, use initials as fallback
     onForegroundImageError: (exception, stackTrace) {},
-    child: Text((firstNameFirstLetter + lastNameFirstLetter).toUpperCase()),
+    child: Text(nameFirstLetter.toUpperCase()),
   );
 }

+ 1 - 1
mobile/lib/shared/ui/user_circle_avatar.dart

@@ -43,7 +43,7 @@ class UserCircleAvatar extends ConsumerWidget {
         '${Store.get(StoreKey.serverEndpoint)}/user/profile-image/${user.id}?d=${Random().nextInt(1024)}';
 
     final textIcon = Text(
-      user.firstName[0].toUpperCase(),
+      user.name[0].toUpperCase(),
       style: TextStyle(
         fontWeight: FontWeight.bold,
         color: context.isDarkTheme ? Colors.black : Colors.white,

+ 1 - 2
mobile/openapi/doc/CreateUserDto.md

@@ -10,9 +10,8 @@ Name | Type | Description | Notes
 ------------ | ------------- | ------------- | -------------
 **email** | **String** |  | 
 **externalPath** | **String** |  | [optional] 
-**firstName** | **String** |  | 
-**lastName** | **String** |  | 
 **memoriesEnabled** | **bool** |  | [optional] 
+**name** | **String** |  | 
 **password** | **String** |  | 
 **storageLabel** | **String** |  | [optional] 
 

+ 1 - 2
mobile/openapi/doc/LoginResponseDto.md

@@ -9,9 +9,8 @@ import 'package:openapi/api.dart';
 Name | Type | Description | Notes
 ------------ | ------------- | ------------- | -------------
 **accessToken** | **String** |  | 
-**firstName** | **String** |  | 
 **isAdmin** | **bool** |  | 
-**lastName** | **String** |  | 
+**name** | **String** |  | 
 **profileImagePath** | **String** |  | 
 **shouldChangePassword** | **bool** |  | 
 **userEmail** | **String** |  | 

+ 1 - 2
mobile/openapi/doc/PartnerResponseDto.md

@@ -12,12 +12,11 @@ Name | Type | Description | Notes
 **deletedAt** | [**DateTime**](DateTime.md) |  | 
 **email** | **String** |  | 
 **externalPath** | **String** |  | 
-**firstName** | **String** |  | 
 **id** | **String** |  | 
 **inTimeline** | **bool** |  | [optional] 
 **isAdmin** | **bool** |  | 
-**lastName** | **String** |  | 
 **memoriesEnabled** | **bool** |  | [optional] 
+**name** | **String** |  | 
 **oauthId** | **String** |  | 
 **profileImagePath** | **String** |  | 
 **shouldChangePassword** | **bool** |  | 

+ 1 - 2
mobile/openapi/doc/SignUpDto.md

@@ -9,8 +9,7 @@ import 'package:openapi/api.dart';
 Name | Type | Description | Notes
 ------------ | ------------- | ------------- | -------------
 **email** | **String** |  | 
-**firstName** | **String** |  | 
-**lastName** | **String** |  | 
+**name** | **String** |  | 
 **password** | **String** |  | 
 
 [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

+ 1 - 2
mobile/openapi/doc/UpdateUserDto.md

@@ -10,11 +10,10 @@ Name | Type | Description | Notes
 ------------ | ------------- | ------------- | -------------
 **email** | **String** |  | [optional] 
 **externalPath** | **String** |  | [optional] 
-**firstName** | **String** |  | [optional] 
 **id** | **String** |  | 
 **isAdmin** | **bool** |  | [optional] 
-**lastName** | **String** |  | [optional] 
 **memoriesEnabled** | **bool** |  | [optional] 
+**name** | **String** |  | [optional] 
 **password** | **String** |  | [optional] 
 **shouldChangePassword** | **bool** |  | [optional] 
 **storageLabel** | **String** |  | [optional] 

+ 1 - 2
mobile/openapi/doc/UsageByUserDto.md

@@ -10,9 +10,8 @@ Name | Type | Description | Notes
 ------------ | ------------- | ------------- | -------------
 **photos** | **int** |  | 
 **usage** | **int** |  | 
-**userFirstName** | **String** |  | 
 **userId** | **String** |  | 
-**userLastName** | **String** |  | 
+**userName** | **String** |  | 
 **videos** | **int** |  | 
 
 [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

+ 1 - 2
mobile/openapi/doc/UserDto.md

@@ -9,9 +9,8 @@ import 'package:openapi/api.dart';
 Name | Type | Description | Notes
 ------------ | ------------- | ------------- | -------------
 **email** | **String** |  | 
-**firstName** | **String** |  | 
 **id** | **String** |  | 
-**lastName** | **String** |  | 
+**name** | **String** |  | 
 **profileImagePath** | **String** |  | 
 
 [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

+ 1 - 2
mobile/openapi/doc/UserResponseDto.md

@@ -12,11 +12,10 @@ Name | Type | Description | Notes
 **deletedAt** | [**DateTime**](DateTime.md) |  | 
 **email** | **String** |  | 
 **externalPath** | **String** |  | 
-**firstName** | **String** |  | 
 **id** | **String** |  | 
 **isAdmin** | **bool** |  | 
-**lastName** | **String** |  | 
 **memoriesEnabled** | **bool** |  | [optional] 
+**name** | **String** |  | 
 **oauthId** | **String** |  | 
 **profileImagePath** | **String** |  | 
 **shouldChangePassword** | **bool** |  | 

+ 9 - 17
mobile/openapi/lib/model/create_user_dto.dart

@@ -15,9 +15,8 @@ class CreateUserDto {
   CreateUserDto({
     required this.email,
     this.externalPath,
-    required this.firstName,
-    required this.lastName,
     this.memoriesEnabled,
+    required this.name,
     required this.password,
     this.storageLabel,
   });
@@ -26,10 +25,6 @@ class CreateUserDto {
 
   String? externalPath;
 
-  String firstName;
-
-  String lastName;
-
   ///
   /// 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
@@ -38,6 +33,8 @@ class CreateUserDto {
   ///
   bool? memoriesEnabled;
 
+  String name;
+
   String password;
 
   String? storageLabel;
@@ -46,9 +43,8 @@ class CreateUserDto {
   bool operator ==(Object other) => identical(this, other) || other is CreateUserDto &&
      other.email == email &&
      other.externalPath == externalPath &&
-     other.firstName == firstName &&
-     other.lastName == lastName &&
      other.memoriesEnabled == memoriesEnabled &&
+     other.name == name &&
      other.password == password &&
      other.storageLabel == storageLabel;
 
@@ -57,14 +53,13 @@ class CreateUserDto {
     // ignore: unnecessary_parenthesis
     (email.hashCode) +
     (externalPath == null ? 0 : externalPath!.hashCode) +
-    (firstName.hashCode) +
-    (lastName.hashCode) +
     (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
+    (name.hashCode) +
     (password.hashCode) +
     (storageLabel == null ? 0 : storageLabel!.hashCode);
 
   @override
-  String toString() => 'CreateUserDto[email=$email, externalPath=$externalPath, firstName=$firstName, lastName=$lastName, memoriesEnabled=$memoriesEnabled, password=$password, storageLabel=$storageLabel]';
+  String toString() => 'CreateUserDto[email=$email, externalPath=$externalPath, memoriesEnabled=$memoriesEnabled, name=$name, password=$password, storageLabel=$storageLabel]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
@@ -74,13 +69,12 @@ class CreateUserDto {
     } else {
     //  json[r'externalPath'] = null;
     }
-      json[r'firstName'] = this.firstName;
-      json[r'lastName'] = this.lastName;
     if (this.memoriesEnabled != null) {
       json[r'memoriesEnabled'] = this.memoriesEnabled;
     } else {
     //  json[r'memoriesEnabled'] = null;
     }
+      json[r'name'] = this.name;
       json[r'password'] = this.password;
     if (this.storageLabel != null) {
       json[r'storageLabel'] = this.storageLabel;
@@ -100,9 +94,8 @@ class CreateUserDto {
       return CreateUserDto(
         email: mapValueOfType<String>(json, r'email')!,
         externalPath: mapValueOfType<String>(json, r'externalPath'),
-        firstName: mapValueOfType<String>(json, r'firstName')!,
-        lastName: mapValueOfType<String>(json, r'lastName')!,
         memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
+        name: mapValueOfType<String>(json, r'name')!,
         password: mapValueOfType<String>(json, r'password')!,
         storageLabel: mapValueOfType<String>(json, r'storageLabel'),
       );
@@ -153,8 +146,7 @@ class CreateUserDto {
   /// The list of required keys that must be present in a JSON.
   static const requiredKeys = <String>{
     'email',
-    'firstName',
-    'lastName',
+    'name',
     'password',
   };
 }

+ 8 - 16
mobile/openapi/lib/model/login_response_dto.dart

@@ -14,9 +14,8 @@ class LoginResponseDto {
   /// Returns a new [LoginResponseDto] instance.
   LoginResponseDto({
     required this.accessToken,
-    required this.firstName,
     required this.isAdmin,
-    required this.lastName,
+    required this.name,
     required this.profileImagePath,
     required this.shouldChangePassword,
     required this.userEmail,
@@ -25,11 +24,9 @@ class LoginResponseDto {
 
   String accessToken;
 
-  String firstName;
-
   bool isAdmin;
 
-  String lastName;
+  String name;
 
   String profileImagePath;
 
@@ -42,9 +39,8 @@ class LoginResponseDto {
   @override
   bool operator ==(Object other) => identical(this, other) || other is LoginResponseDto &&
      other.accessToken == accessToken &&
-     other.firstName == firstName &&
      other.isAdmin == isAdmin &&
-     other.lastName == lastName &&
+     other.name == name &&
      other.profileImagePath == profileImagePath &&
      other.shouldChangePassword == shouldChangePassword &&
      other.userEmail == userEmail &&
@@ -54,23 +50,21 @@ class LoginResponseDto {
   int get hashCode =>
     // ignore: unnecessary_parenthesis
     (accessToken.hashCode) +
-    (firstName.hashCode) +
     (isAdmin.hashCode) +
-    (lastName.hashCode) +
+    (name.hashCode) +
     (profileImagePath.hashCode) +
     (shouldChangePassword.hashCode) +
     (userEmail.hashCode) +
     (userId.hashCode);
 
   @override
-  String toString() => 'LoginResponseDto[accessToken=$accessToken, firstName=$firstName, isAdmin=$isAdmin, lastName=$lastName, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, userEmail=$userEmail, userId=$userId]';
+  String toString() => 'LoginResponseDto[accessToken=$accessToken, isAdmin=$isAdmin, name=$name, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, userEmail=$userEmail, userId=$userId]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
       json[r'accessToken'] = this.accessToken;
-      json[r'firstName'] = this.firstName;
       json[r'isAdmin'] = this.isAdmin;
-      json[r'lastName'] = this.lastName;
+      json[r'name'] = this.name;
       json[r'profileImagePath'] = this.profileImagePath;
       json[r'shouldChangePassword'] = this.shouldChangePassword;
       json[r'userEmail'] = this.userEmail;
@@ -87,9 +81,8 @@ class LoginResponseDto {
 
       return LoginResponseDto(
         accessToken: mapValueOfType<String>(json, r'accessToken')!,
-        firstName: mapValueOfType<String>(json, r'firstName')!,
         isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
-        lastName: mapValueOfType<String>(json, r'lastName')!,
+        name: mapValueOfType<String>(json, r'name')!,
         profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
         shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword')!,
         userEmail: mapValueOfType<String>(json, r'userEmail')!,
@@ -142,9 +135,8 @@ class LoginResponseDto {
   /// The list of required keys that must be present in a JSON.
   static const requiredKeys = <String>{
     'accessToken',
-    'firstName',
     'isAdmin',
-    'lastName',
+    'name',
     'profileImagePath',
     'shouldChangePassword',
     'userEmail',

+ 9 - 17
mobile/openapi/lib/model/partner_response_dto.dart

@@ -17,12 +17,11 @@ class PartnerResponseDto {
     required this.deletedAt,
     required this.email,
     required this.externalPath,
-    required this.firstName,
     required this.id,
     this.inTimeline,
     required this.isAdmin,
-    required this.lastName,
     this.memoriesEnabled,
+    required this.name,
     required this.oauthId,
     required this.profileImagePath,
     required this.shouldChangePassword,
@@ -38,8 +37,6 @@ class PartnerResponseDto {
 
   String? externalPath;
 
-  String firstName;
-
   String id;
 
   ///
@@ -52,8 +49,6 @@ class PartnerResponseDto {
 
   bool isAdmin;
 
-  String lastName;
-
   ///
   /// 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,6 +57,8 @@ class PartnerResponseDto {
   ///
   bool? memoriesEnabled;
 
+  String name;
+
   String oauthId;
 
   String profileImagePath;
@@ -78,12 +75,11 @@ class PartnerResponseDto {
      other.deletedAt == deletedAt &&
      other.email == email &&
      other.externalPath == externalPath &&
-     other.firstName == firstName &&
      other.id == id &&
      other.inTimeline == inTimeline &&
      other.isAdmin == isAdmin &&
-     other.lastName == lastName &&
      other.memoriesEnabled == memoriesEnabled &&
+     other.name == name &&
      other.oauthId == oauthId &&
      other.profileImagePath == profileImagePath &&
      other.shouldChangePassword == shouldChangePassword &&
@@ -97,12 +93,11 @@ class PartnerResponseDto {
     (deletedAt == null ? 0 : deletedAt!.hashCode) +
     (email.hashCode) +
     (externalPath == null ? 0 : externalPath!.hashCode) +
-    (firstName.hashCode) +
     (id.hashCode) +
     (inTimeline == null ? 0 : inTimeline!.hashCode) +
     (isAdmin.hashCode) +
-    (lastName.hashCode) +
     (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
+    (name.hashCode) +
     (oauthId.hashCode) +
     (profileImagePath.hashCode) +
     (shouldChangePassword.hashCode) +
@@ -110,7 +105,7 @@ class PartnerResponseDto {
     (updatedAt.hashCode);
 
   @override
-  String toString() => 'PartnerResponseDto[createdAt=$createdAt, deletedAt=$deletedAt, email=$email, externalPath=$externalPath, firstName=$firstName, id=$id, inTimeline=$inTimeline, isAdmin=$isAdmin, lastName=$lastName, memoriesEnabled=$memoriesEnabled, oauthId=$oauthId, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel, updatedAt=$updatedAt]';
+  String toString() => 'PartnerResponseDto[createdAt=$createdAt, deletedAt=$deletedAt, email=$email, externalPath=$externalPath, id=$id, inTimeline=$inTimeline, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, oauthId=$oauthId, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel, updatedAt=$updatedAt]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
@@ -126,7 +121,6 @@ class PartnerResponseDto {
     } else {
     //  json[r'externalPath'] = null;
     }
-      json[r'firstName'] = this.firstName;
       json[r'id'] = this.id;
     if (this.inTimeline != null) {
       json[r'inTimeline'] = this.inTimeline;
@@ -134,12 +128,12 @@ class PartnerResponseDto {
     //  json[r'inTimeline'] = null;
     }
       json[r'isAdmin'] = this.isAdmin;
-      json[r'lastName'] = this.lastName;
     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;
       json[r'shouldChangePassword'] = this.shouldChangePassword;
@@ -164,12 +158,11 @@ class PartnerResponseDto {
         deletedAt: mapDateTime(json, r'deletedAt', ''),
         email: mapValueOfType<String>(json, r'email')!,
         externalPath: mapValueOfType<String>(json, r'externalPath'),
-        firstName: mapValueOfType<String>(json, r'firstName')!,
         id: mapValueOfType<String>(json, r'id')!,
         inTimeline: mapValueOfType<bool>(json, r'inTimeline'),
         isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
-        lastName: mapValueOfType<String>(json, r'lastName')!,
         memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
+        name: mapValueOfType<String>(json, r'name')!,
         oauthId: mapValueOfType<String>(json, r'oauthId')!,
         profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
         shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword')!,
@@ -226,10 +219,9 @@ class PartnerResponseDto {
     'deletedAt',
     'email',
     'externalPath',
-    'firstName',
     'id',
     'isAdmin',
-    'lastName',
+    'name',
     'oauthId',
     'profileImagePath',
     'shouldChangePassword',

+ 8 - 16
mobile/openapi/lib/model/sign_up_dto.dart

@@ -14,42 +14,36 @@ class SignUpDto {
   /// Returns a new [SignUpDto] instance.
   SignUpDto({
     required this.email,
-    required this.firstName,
-    required this.lastName,
+    required this.name,
     required this.password,
   });
 
   String email;
 
-  String firstName;
-
-  String lastName;
+  String name;
 
   String password;
 
   @override
   bool operator ==(Object other) => identical(this, other) || other is SignUpDto &&
      other.email == email &&
-     other.firstName == firstName &&
-     other.lastName == lastName &&
+     other.name == name &&
      other.password == password;
 
   @override
   int get hashCode =>
     // ignore: unnecessary_parenthesis
     (email.hashCode) +
-    (firstName.hashCode) +
-    (lastName.hashCode) +
+    (name.hashCode) +
     (password.hashCode);
 
   @override
-  String toString() => 'SignUpDto[email=$email, firstName=$firstName, lastName=$lastName, password=$password]';
+  String toString() => 'SignUpDto[email=$email, name=$name, password=$password]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
       json[r'email'] = this.email;
-      json[r'firstName'] = this.firstName;
-      json[r'lastName'] = this.lastName;
+      json[r'name'] = this.name;
       json[r'password'] = this.password;
     return json;
   }
@@ -63,8 +57,7 @@ class SignUpDto {
 
       return SignUpDto(
         email: mapValueOfType<String>(json, r'email')!,
-        firstName: mapValueOfType<String>(json, r'firstName')!,
-        lastName: mapValueOfType<String>(json, r'lastName')!,
+        name: mapValueOfType<String>(json, r'name')!,
         password: mapValueOfType<String>(json, r'password')!,
       );
     }
@@ -114,8 +107,7 @@ class SignUpDto {
   /// The list of required keys that must be present in a JSON.
   static const requiredKeys = <String>{
     'email',
-    'firstName',
-    'lastName',
+    'name',
     'password',
   };
 }

+ 12 - 29
mobile/openapi/lib/model/update_user_dto.dart

@@ -15,11 +15,10 @@ class UpdateUserDto {
   UpdateUserDto({
     this.email,
     this.externalPath,
-    this.firstName,
     required this.id,
     this.isAdmin,
-    this.lastName,
     this.memoriesEnabled,
+    this.name,
     this.password,
     this.shouldChangePassword,
     this.storageLabel,
@@ -41,14 +40,6 @@ class UpdateUserDto {
   ///
   String? externalPath;
 
-  ///
-  /// 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.
-  ///
-  String? firstName;
-
   String id;
 
   ///
@@ -65,7 +56,7 @@ class UpdateUserDto {
   /// source code must fall back to having a nullable type.
   /// Consider adding a "default:" property in the specification file to hide this note.
   ///
-  String? lastName;
+  bool? memoriesEnabled;
 
   ///
   /// Please note: This property should have been non-nullable! Since the specification file
@@ -73,7 +64,7 @@ class UpdateUserDto {
   /// 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;
 
   ///
   /// Please note: This property should have been non-nullable! Since the specification file
@@ -103,11 +94,10 @@ class UpdateUserDto {
   bool operator ==(Object other) => identical(this, other) || other is UpdateUserDto &&
      other.email == email &&
      other.externalPath == externalPath &&
-     other.firstName == firstName &&
      other.id == id &&
      other.isAdmin == isAdmin &&
-     other.lastName == lastName &&
      other.memoriesEnabled == memoriesEnabled &&
+     other.name == name &&
      other.password == password &&
      other.shouldChangePassword == shouldChangePassword &&
      other.storageLabel == storageLabel;
@@ -117,17 +107,16 @@ class UpdateUserDto {
     // ignore: unnecessary_parenthesis
     (email == null ? 0 : email!.hashCode) +
     (externalPath == null ? 0 : externalPath!.hashCode) +
-    (firstName == null ? 0 : firstName!.hashCode) +
     (id.hashCode) +
     (isAdmin == null ? 0 : isAdmin!.hashCode) +
-    (lastName == null ? 0 : lastName!.hashCode) +
     (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
+    (name == null ? 0 : name!.hashCode) +
     (password == null ? 0 : password!.hashCode) +
     (shouldChangePassword == null ? 0 : shouldChangePassword!.hashCode) +
     (storageLabel == null ? 0 : storageLabel!.hashCode);
 
   @override
-  String toString() => 'UpdateUserDto[email=$email, externalPath=$externalPath, firstName=$firstName, id=$id, isAdmin=$isAdmin, lastName=$lastName, memoriesEnabled=$memoriesEnabled, password=$password, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
+  String toString() => 'UpdateUserDto[email=$email, externalPath=$externalPath, id=$id, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, password=$password, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
@@ -140,11 +129,6 @@ class UpdateUserDto {
       json[r'externalPath'] = this.externalPath;
     } else {
     //  json[r'externalPath'] = null;
-    }
-    if (this.firstName != null) {
-      json[r'firstName'] = this.firstName;
-    } else {
-    //  json[r'firstName'] = null;
     }
       json[r'id'] = this.id;
     if (this.isAdmin != null) {
@@ -152,16 +136,16 @@ class UpdateUserDto {
     } else {
     //  json[r'isAdmin'] = null;
     }
-    if (this.lastName != null) {
-      json[r'lastName'] = this.lastName;
-    } else {
-    //  json[r'lastName'] = 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 {
+    //  json[r'name'] = null;
+    }
     if (this.password != null) {
       json[r'password'] = this.password;
     } else {
@@ -190,11 +174,10 @@ class UpdateUserDto {
       return UpdateUserDto(
         email: mapValueOfType<String>(json, r'email'),
         externalPath: mapValueOfType<String>(json, r'externalPath'),
-        firstName: mapValueOfType<String>(json, r'firstName'),
         id: mapValueOfType<String>(json, r'id')!,
         isAdmin: mapValueOfType<bool>(json, r'isAdmin'),
-        lastName: mapValueOfType<String>(json, r'lastName'),
         memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
+        name: mapValueOfType<String>(json, r'name'),
         password: mapValueOfType<String>(json, r'password'),
         shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword'),
         storageLabel: mapValueOfType<String>(json, r'storageLabel'),

+ 8 - 16
mobile/openapi/lib/model/usage_by_user_dto.dart

@@ -15,9 +15,8 @@ class UsageByUserDto {
   UsageByUserDto({
     required this.photos,
     required this.usage,
-    required this.userFirstName,
     required this.userId,
-    required this.userLastName,
+    required this.userName,
     required this.videos,
   });
 
@@ -25,11 +24,9 @@ class UsageByUserDto {
 
   int usage;
 
-  String userFirstName;
-
   String userId;
 
-  String userLastName;
+  String userName;
 
   int videos;
 
@@ -37,9 +34,8 @@ class UsageByUserDto {
   bool operator ==(Object other) => identical(this, other) || other is UsageByUserDto &&
      other.photos == photos &&
      other.usage == usage &&
-     other.userFirstName == userFirstName &&
      other.userId == userId &&
-     other.userLastName == userLastName &&
+     other.userName == userName &&
      other.videos == videos;
 
   @override
@@ -47,21 +43,19 @@ class UsageByUserDto {
     // ignore: unnecessary_parenthesis
     (photos.hashCode) +
     (usage.hashCode) +
-    (userFirstName.hashCode) +
     (userId.hashCode) +
-    (userLastName.hashCode) +
+    (userName.hashCode) +
     (videos.hashCode);
 
   @override
-  String toString() => 'UsageByUserDto[photos=$photos, usage=$usage, userFirstName=$userFirstName, userId=$userId, userLastName=$userLastName, videos=$videos]';
+  String toString() => 'UsageByUserDto[photos=$photos, usage=$usage, userId=$userId, userName=$userName, videos=$videos]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
       json[r'photos'] = this.photos;
       json[r'usage'] = this.usage;
-      json[r'userFirstName'] = this.userFirstName;
       json[r'userId'] = this.userId;
-      json[r'userLastName'] = this.userLastName;
+      json[r'userName'] = this.userName;
       json[r'videos'] = this.videos;
     return json;
   }
@@ -76,9 +70,8 @@ class UsageByUserDto {
       return UsageByUserDto(
         photos: mapValueOfType<int>(json, r'photos')!,
         usage: mapValueOfType<int>(json, r'usage')!,
-        userFirstName: mapValueOfType<String>(json, r'userFirstName')!,
         userId: mapValueOfType<String>(json, r'userId')!,
-        userLastName: mapValueOfType<String>(json, r'userLastName')!,
+        userName: mapValueOfType<String>(json, r'userName')!,
         videos: mapValueOfType<int>(json, r'videos')!,
       );
     }
@@ -129,9 +122,8 @@ class UsageByUserDto {
   static const requiredKeys = <String>{
     'photos',
     'usage',
-    'userFirstName',
     'userId',
-    'userLastName',
+    'userName',
     'videos',
   };
 }

+ 8 - 16
mobile/openapi/lib/model/user_dto.dart

@@ -14,48 +14,42 @@ class UserDto {
   /// Returns a new [UserDto] instance.
   UserDto({
     required this.email,
-    required this.firstName,
     required this.id,
-    required this.lastName,
+    required this.name,
     required this.profileImagePath,
   });
 
   String email;
 
-  String firstName;
-
   String id;
 
-  String lastName;
+  String name;
 
   String profileImagePath;
 
   @override
   bool operator ==(Object other) => identical(this, other) || other is UserDto &&
      other.email == email &&
-     other.firstName == firstName &&
      other.id == id &&
-     other.lastName == lastName &&
+     other.name == name &&
      other.profileImagePath == profileImagePath;
 
   @override
   int get hashCode =>
     // ignore: unnecessary_parenthesis
     (email.hashCode) +
-    (firstName.hashCode) +
     (id.hashCode) +
-    (lastName.hashCode) +
+    (name.hashCode) +
     (profileImagePath.hashCode);
 
   @override
-  String toString() => 'UserDto[email=$email, firstName=$firstName, id=$id, lastName=$lastName, profileImagePath=$profileImagePath]';
+  String toString() => 'UserDto[email=$email, id=$id, name=$name, profileImagePath=$profileImagePath]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
       json[r'email'] = this.email;
-      json[r'firstName'] = this.firstName;
       json[r'id'] = this.id;
-      json[r'lastName'] = this.lastName;
+      json[r'name'] = this.name;
       json[r'profileImagePath'] = this.profileImagePath;
     return json;
   }
@@ -69,9 +63,8 @@ class UserDto {
 
       return UserDto(
         email: mapValueOfType<String>(json, r'email')!,
-        firstName: mapValueOfType<String>(json, r'firstName')!,
         id: mapValueOfType<String>(json, r'id')!,
-        lastName: mapValueOfType<String>(json, r'lastName')!,
+        name: mapValueOfType<String>(json, r'name')!,
         profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
       );
     }
@@ -121,9 +114,8 @@ class UserDto {
   /// The list of required keys that must be present in a JSON.
   static const requiredKeys = <String>{
     'email',
-    'firstName',
     'id',
-    'lastName',
+    'name',
     'profileImagePath',
   };
 }

+ 9 - 17
mobile/openapi/lib/model/user_response_dto.dart

@@ -17,11 +17,10 @@ class UserResponseDto {
     required this.deletedAt,
     required this.email,
     required this.externalPath,
-    required this.firstName,
     required this.id,
     required this.isAdmin,
-    required this.lastName,
     this.memoriesEnabled,
+    required this.name,
     required this.oauthId,
     required this.profileImagePath,
     required this.shouldChangePassword,
@@ -37,14 +36,10 @@ class UserResponseDto {
 
   String? externalPath;
 
-  String firstName;
-
   String id;
 
   bool isAdmin;
 
-  String lastName;
-
   ///
   /// 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
@@ -53,6 +48,8 @@ class UserResponseDto {
   ///
   bool? memoriesEnabled;
 
+  String name;
+
   String oauthId;
 
   String profileImagePath;
@@ -69,11 +66,10 @@ class UserResponseDto {
      other.deletedAt == deletedAt &&
      other.email == email &&
      other.externalPath == externalPath &&
-     other.firstName == firstName &&
      other.id == id &&
      other.isAdmin == isAdmin &&
-     other.lastName == lastName &&
      other.memoriesEnabled == memoriesEnabled &&
+     other.name == name &&
      other.oauthId == oauthId &&
      other.profileImagePath == profileImagePath &&
      other.shouldChangePassword == shouldChangePassword &&
@@ -87,11 +83,10 @@ class UserResponseDto {
     (deletedAt == null ? 0 : deletedAt!.hashCode) +
     (email.hashCode) +
     (externalPath == null ? 0 : externalPath!.hashCode) +
-    (firstName.hashCode) +
     (id.hashCode) +
     (isAdmin.hashCode) +
-    (lastName.hashCode) +
     (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
+    (name.hashCode) +
     (oauthId.hashCode) +
     (profileImagePath.hashCode) +
     (shouldChangePassword.hashCode) +
@@ -99,7 +94,7 @@ class UserResponseDto {
     (updatedAt.hashCode);
 
   @override
-  String toString() => 'UserResponseDto[createdAt=$createdAt, deletedAt=$deletedAt, email=$email, externalPath=$externalPath, firstName=$firstName, id=$id, isAdmin=$isAdmin, lastName=$lastName, memoriesEnabled=$memoriesEnabled, oauthId=$oauthId, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel, updatedAt=$updatedAt]';
+  String toString() => 'UserResponseDto[createdAt=$createdAt, deletedAt=$deletedAt, email=$email, externalPath=$externalPath, id=$id, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, oauthId=$oauthId, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel, updatedAt=$updatedAt]';
 
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
@@ -115,15 +110,14 @@ class UserResponseDto {
     } else {
     //  json[r'externalPath'] = null;
     }
-      json[r'firstName'] = this.firstName;
       json[r'id'] = this.id;
       json[r'isAdmin'] = this.isAdmin;
-      json[r'lastName'] = this.lastName;
     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;
       json[r'shouldChangePassword'] = this.shouldChangePassword;
@@ -148,11 +142,10 @@ class UserResponseDto {
         deletedAt: mapDateTime(json, r'deletedAt', ''),
         email: mapValueOfType<String>(json, r'email')!,
         externalPath: mapValueOfType<String>(json, r'externalPath'),
-        firstName: mapValueOfType<String>(json, r'firstName')!,
         id: mapValueOfType<String>(json, r'id')!,
         isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
-        lastName: mapValueOfType<String>(json, r'lastName')!,
         memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
+        name: mapValueOfType<String>(json, r'name')!,
         oauthId: mapValueOfType<String>(json, r'oauthId')!,
         profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
         shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword')!,
@@ -209,10 +202,9 @@ class UserResponseDto {
     'deletedAt',
     'email',
     'externalPath',
-    'firstName',
     'id',
     'isAdmin',
-    'lastName',
+    'name',
     'oauthId',
     'profileImagePath',
     'shouldChangePassword',

+ 4 - 9
mobile/openapi/test/create_user_dto_test.dart

@@ -26,18 +26,13 @@ void main() {
       // TODO
     });
 
-    // String firstName
-    test('to test the property `firstName`', () async {
-      // TODO
-    });
-
-    // String lastName
-    test('to test the property `lastName`', () async {
+    // bool memoriesEnabled
+    test('to test the property `memoriesEnabled`', () async {
       // TODO
     });
 
-    // bool memoriesEnabled
-    test('to test the property `memoriesEnabled`', () async {
+    // String name
+    test('to test the property `name`', () async {
       // TODO
     });
 

+ 2 - 7
mobile/openapi/test/login_response_dto_test.dart

@@ -21,18 +21,13 @@ void main() {
       // TODO
     });
 
-    // String firstName
-    test('to test the property `firstName`', () async {
-      // TODO
-    });
-
     // bool isAdmin
     test('to test the property `isAdmin`', () async {
       // TODO
     });
 
-    // String lastName
-    test('to test the property `lastName`', () async {
+    // String name
+    test('to test the property `name`', () async {
       // TODO
     });
 

+ 4 - 9
mobile/openapi/test/partner_response_dto_test.dart

@@ -36,11 +36,6 @@ void main() {
       // TODO
     });
 
-    // String firstName
-    test('to test the property `firstName`', () async {
-      // TODO
-    });
-
     // String id
     test('to test the property `id`', () async {
       // TODO
@@ -56,13 +51,13 @@ void main() {
       // TODO
     });
 
-    // String lastName
-    test('to test the property `lastName`', () async {
+    // bool memoriesEnabled
+    test('to test the property `memoriesEnabled`', () async {
       // TODO
     });
 
-    // bool memoriesEnabled
-    test('to test the property `memoriesEnabled`', () async {
+    // String name
+    test('to test the property `name`', () async {
       // TODO
     });
 

+ 2 - 7
mobile/openapi/test/sign_up_dto_test.dart

@@ -21,13 +21,8 @@ void main() {
       // TODO
     });
 
-    // String firstName
-    test('to test the property `firstName`', () async {
-      // TODO
-    });
-
-    // String lastName
-    test('to test the property `lastName`', () async {
+    // String name
+    test('to test the property `name`', () async {
       // TODO
     });
 

+ 4 - 9
mobile/openapi/test/update_user_dto_test.dart

@@ -26,11 +26,6 @@ void main() {
       // TODO
     });
 
-    // String firstName
-    test('to test the property `firstName`', () async {
-      // TODO
-    });
-
     // String id
     test('to test the property `id`', () async {
       // TODO
@@ -41,13 +36,13 @@ void main() {
       // TODO
     });
 
-    // String lastName
-    test('to test the property `lastName`', () async {
+    // bool memoriesEnabled
+    test('to test the property `memoriesEnabled`', () async {
       // TODO
     });
 
-    // bool memoriesEnabled
-    test('to test the property `memoriesEnabled`', () async {
+    // String name
+    test('to test the property `name`', () async {
       // TODO
     });
 

+ 2 - 7
mobile/openapi/test/usage_by_user_dto_test.dart

@@ -26,18 +26,13 @@ void main() {
       // TODO
     });
 
-    // String userFirstName
-    test('to test the property `userFirstName`', () async {
-      // TODO
-    });
-
     // String userId
     test('to test the property `userId`', () async {
       // TODO
     });
 
-    // String userLastName
-    test('to test the property `userLastName`', () async {
+    // String userName
+    test('to test the property `userName`', () async {
       // TODO
     });
 

+ 2 - 7
mobile/openapi/test/user_dto_test.dart

@@ -21,18 +21,13 @@ void main() {
       // TODO
     });
 
-    // String firstName
-    test('to test the property `firstName`', () async {
-      // TODO
-    });
-
     // String id
     test('to test the property `id`', () async {
       // TODO
     });
 
-    // String lastName
-    test('to test the property `lastName`', () async {
+    // String name
+    test('to test the property `name`', () async {
       // TODO
     });
 

+ 4 - 9
mobile/openapi/test/user_response_dto_test.dart

@@ -36,11 +36,6 @@ void main() {
       // TODO
     });
 
-    // String firstName
-    test('to test the property `firstName`', () async {
-      // TODO
-    });
-
     // String id
     test('to test the property `id`', () async {
       // TODO
@@ -51,13 +46,13 @@ void main() {
       // TODO
     });
 
-    // String lastName
-    test('to test the property `lastName`', () async {
+    // bool memoriesEnabled
+    test('to test the property `memoriesEnabled`', () async {
       // TODO
     });
 
-    // bool memoriesEnabled
-    test('to test the property `memoriesEnabled`', () async {
+    // String name
+    test('to test the property `name`', () async {
       // TODO
     });
 

+ 1 - 2
mobile/test/sync_service_test.dart

@@ -62,8 +62,7 @@ void main() {
       id: "1",
       updatedAt: DateTime.now(),
       email: "a@b.c",
-      firstName: "first",
-      lastName: "last",
+      name: "first last",
       isAdmin: false,
     );
     setUpAll(() async {

+ 23 - 55
server/immich-openapi-specs.json

@@ -6844,15 +6844,12 @@
             "nullable": true,
             "type": "string"
           },
-          "firstName": {
-            "type": "string"
-          },
-          "lastName": {
-            "type": "string"
-          },
           "memoriesEnabled": {
             "type": "boolean"
           },
+          "name": {
+            "type": "string"
+          },
           "password": {
             "type": "string"
           },
@@ -6864,8 +6861,7 @@
         "required": [
           "email",
           "password",
-          "firstName",
-          "lastName"
+          "name"
         ],
         "type": "object"
       },
@@ -7463,13 +7459,10 @@
           "accessToken": {
             "type": "string"
           },
-          "firstName": {
-            "type": "string"
-          },
           "isAdmin": {
             "type": "boolean"
           },
-          "lastName": {
+          "name": {
             "type": "string"
           },
           "profileImagePath": {
@@ -7489,8 +7482,7 @@
           "accessToken",
           "userId",
           "userEmail",
-          "firstName",
-          "lastName",
+          "name",
           "profileImagePath",
           "isAdmin",
           "shouldChangePassword"
@@ -7656,9 +7648,6 @@
             "nullable": true,
             "type": "string"
           },
-          "firstName": {
-            "type": "string"
-          },
           "id": {
             "type": "string"
           },
@@ -7668,12 +7657,12 @@
           "isAdmin": {
             "type": "boolean"
           },
-          "lastName": {
-            "type": "string"
-          },
           "memoriesEnabled": {
             "type": "boolean"
           },
+          "name": {
+            "type": "string"
+          },
           "oauthId": {
             "type": "string"
           },
@@ -7694,8 +7683,7 @@
         },
         "required": [
           "id",
-          "firstName",
-          "lastName",
+          "name",
           "email",
           "profileImagePath",
           "storageLabel",
@@ -8464,14 +8452,10 @@
             "example": "testuser@email.com",
             "type": "string"
           },
-          "firstName": {
+          "name": {
             "example": "Admin",
             "type": "string"
           },
-          "lastName": {
-            "example": "Doe",
-            "type": "string"
-          },
           "password": {
             "example": "password",
             "type": "string"
@@ -8480,8 +8464,7 @@
         "required": [
           "email",
           "password",
-          "firstName",
-          "lastName"
+          "name"
         ],
         "type": "object"
       },
@@ -9163,9 +9146,6 @@
           "externalPath": {
             "type": "string"
           },
-          "firstName": {
-            "type": "string"
-          },
           "id": {
             "format": "uuid",
             "type": "string"
@@ -9173,12 +9153,12 @@
           "isAdmin": {
             "type": "boolean"
           },
-          "lastName": {
-            "type": "string"
-          },
           "memoriesEnabled": {
             "type": "boolean"
           },
+          "name": {
+            "type": "string"
+          },
           "password": {
             "type": "string"
           },
@@ -9203,13 +9183,10 @@
             "format": "int64",
             "type": "integer"
           },
-          "userFirstName": {
-            "type": "string"
-          },
           "userId": {
             "type": "string"
           },
-          "userLastName": {
+          "userName": {
             "type": "string"
           },
           "videos": {
@@ -9218,8 +9195,7 @@
         },
         "required": [
           "userId",
-          "userFirstName",
-          "userLastName",
+          "userName",
           "photos",
           "videos",
           "usage"
@@ -9231,13 +9207,10 @@
           "email": {
             "type": "string"
           },
-          "firstName": {
-            "type": "string"
-          },
           "id": {
             "type": "string"
           },
-          "lastName": {
+          "name": {
             "type": "string"
           },
           "profileImagePath": {
@@ -9246,8 +9219,7 @@
         },
         "required": [
           "id",
-          "firstName",
-          "lastName",
+          "name",
           "email",
           "profileImagePath"
         ],
@@ -9271,21 +9243,18 @@
             "nullable": true,
             "type": "string"
           },
-          "firstName": {
-            "type": "string"
-          },
           "id": {
             "type": "string"
           },
           "isAdmin": {
             "type": "boolean"
           },
-          "lastName": {
-            "type": "string"
-          },
           "memoriesEnabled": {
             "type": "boolean"
           },
+          "name": {
+            "type": "string"
+          },
           "oauthId": {
             "type": "string"
           },
@@ -9306,8 +9275,7 @@
         },
         "required": [
           "id",
-          "firstName",
-          "lastName",
+          "name",
           "email",
           "profileImagePath",
           "storageLabel",

+ 0 - 5
server/package-lock.json

@@ -16845,11 +16845,6 @@
         "luxon": "^3.2.1"
       }
     },
-    "cron-validator": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/cron-validator/-/cron-validator-1.3.1.tgz",
-      "integrity": "sha512-C1HsxuPCY/5opR55G5/WNzyEGDWFVG+6GLrA+fW/sCTcP6A6NTjUP2AK7B8n2PyFs90kDG2qzwm8LMheADku6A=="
-    },
     "cross-spawn": {
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",

+ 2 - 2
server/src/admin-cli/commands/reset-admin-password.command.ts

@@ -15,12 +15,12 @@ export class ResetAdminPasswordCommand extends CommandRunner {
 
   async run(): Promise<void> {
     const ask = (admin: UserResponseDto) => {
-      const { id, oauthId, email, firstName, lastName } = admin;
+      const { id, oauthId, email, name } = admin;
       console.log(`Found Admin:
 - ID=${id}
 - OAuth ID=${oauthId}
 - Email=${email}
-- Name=${firstName} ${lastName}`);
+- Name=${name}`);
 
       return this.inquirer.ask<{ password: string }>('prompt-password', undefined).then(({ password }) => password);
     };

+ 3 - 10
server/src/domain/auth/auth.dto.ts

@@ -33,8 +33,7 @@ export class LoginResponseDto {
   accessToken!: string;
   userId!: string;
   userEmail!: string;
-  firstName!: string;
-  lastName!: string;
+  name!: string;
   profileImagePath!: string;
   isAdmin!: boolean;
   shouldChangePassword!: boolean;
@@ -45,8 +44,7 @@ export function mapLoginResponse(entity: UserEntity, accessToken: string): Login
     accessToken: accessToken,
     userId: entity.id,
     userEmail: entity.email,
-    firstName: entity.firstName,
-    lastName: entity.lastName,
+    name: entity.name,
     isAdmin: entity.isAdmin,
     profileImagePath: entity.profileImagePath,
     shouldChangePassword: entity.shouldChangePassword,
@@ -62,12 +60,7 @@ export class SignUpDto extends LoginCredentialDto {
   @IsString()
   @IsNotEmpty()
   @ApiProperty({ example: 'Admin' })
-  firstName!: string;
-
-  @IsString()
-  @IsNotEmpty()
-  @ApiProperty({ example: 'Doe' })
-  lastName!: string;
+  name!: string;
 }
 
 export class ChangePasswordDto {

+ 2 - 3
server/src/domain/auth/auth.service.spec.ts

@@ -236,7 +236,7 @@ describe('AuthService', () => {
   });
 
   describe('adminSignUp', () => {
-    const dto: SignUpDto = { email: 'test@immich.com', password: 'password', firstName: 'immich', lastName: 'admin' };
+    const dto: SignUpDto = { email: 'test@immich.com', password: 'password', name: 'immich admin' };
 
     it('should only allow one admin', async () => {
       userMock.getAdmin.mockResolvedValue({} as UserEntity);
@@ -251,8 +251,7 @@ describe('AuthService', () => {
         id: 'admin',
         createdAt: new Date('2021-01-01'),
         email: 'test@immich.com',
-        firstName: 'immich',
-        lastName: 'admin',
+        name: 'immich admin',
       });
       expect(userMock.getAdmin).toHaveBeenCalled();
       expect(userMock.create).toHaveBeenCalled();

+ 3 - 4
server/src/domain/auth/auth.service.ts

@@ -146,8 +146,7 @@ export class AuthService {
     const admin = await this.userCore.createUser({
       isAdmin: true,
       email: dto.email,
-      firstName: dto.firstName,
-      lastName: dto.lastName,
+      name: dto.name,
       password: dto.password,
       storageLabel: 'admin',
     });
@@ -273,9 +272,9 @@ export class AuthService {
         storageLabel = null;
       }
 
+      const userName = profile.name ?? `${profile.given_name || ''} ${profile.family_name || ''}`;
       user = await this.userCore.createUser({
-        firstName: profile.given_name || '',
-        lastName: profile.family_name || '',
+        name: userName,
         email: profile.email,
         oauthId: profile.sub,
         storageLabel,

+ 2 - 4
server/src/domain/partner/partner.service.spec.ts

@@ -7,10 +7,9 @@ import { PartnerService } from './partner.service';
 const responseDto = {
   admin: <PartnerResponseDto>{
     email: 'admin@test.com',
-    firstName: 'admin_first_name',
+    name: 'admin_name',
     id: 'admin_id',
     isAdmin: true,
-    lastName: 'admin_last_name',
     oauthId: '',
     profileImagePath: '',
     shouldChangePassword: false,
@@ -24,10 +23,9 @@ const responseDto = {
   },
   user1: <PartnerResponseDto>{
     email: 'immich@test.com',
-    firstName: 'immich_first_name',
+    name: 'immich_name',
     id: 'user-id',
     isAdmin: false,
-    lastName: 'immich_last_name',
     oauthId: '',
     profileImagePath: '',
     shouldChangePassword: false,

+ 1 - 2
server/src/domain/repositories/user.repository.ts

@@ -6,8 +6,7 @@ export interface UserListFilter {
 
 export interface UserStatsQueryResponse {
   userId: string;
-  userFirstName: string;
-  userLastName: string;
+  userName: string;
   photos: number;
   videos: number;
   usage: number;

+ 1 - 3
server/src/domain/server-info/server-info.dto.ts

@@ -38,9 +38,7 @@ export class UsageByUserDto {
   @ApiProperty({ type: 'string' })
   userId!: string;
   @ApiProperty({ type: 'string' })
-  userFirstName!: string;
-  @ApiProperty({ type: 'string' })
-  userLastName!: string;
+  userName!: string;
   @ApiProperty({ type: 'integer' })
   photos!: number;
   @ApiProperty({ type: 'integer' })

+ 6 - 12
server/src/domain/server-info/server-info.service.spec.ts

@@ -195,24 +195,21 @@ describe(ServerInfoService.name, () => {
       userMock.getUserStats.mockResolvedValue([
         {
           userId: 'user1',
-          userFirstName: '1',
-          userLastName: 'User',
+          userName: '1 User',
           photos: 10,
           videos: 11,
           usage: 12345,
         },
         {
           userId: 'user2',
-          userFirstName: '2',
-          userLastName: 'User',
+          userName: '2 User',
           photos: 10,
           videos: 20,
           usage: 123456,
         },
         {
           userId: 'user3',
-          userFirstName: '3',
-          userLastName: 'User',
+          userName: '3 User',
           photos: 100,
           videos: 0,
           usage: 987654,
@@ -227,25 +224,22 @@ describe(ServerInfoService.name, () => {
           {
             photos: 10,
             usage: 12345,
-            userFirstName: '1',
+            userName: '1 User',
             userId: 'user1',
-            userLastName: 'User',
             videos: 11,
           },
           {
             photos: 10,
             usage: 123456,
-            userFirstName: '2',
+            userName: '2 User',
             userId: 'user2',
-            userLastName: 'User',
             videos: 20,
           },
           {
             photos: 100,
             usage: 987654,
-            userFirstName: '3',
+            userName: '3 User',
             userId: 'user3',
-            userLastName: 'User',
             videos: 0,
           },
         ],

+ 1 - 2
server/src/domain/server-info/server-info.service.ts

@@ -98,8 +98,7 @@ export class ServerInfoService {
     for (const user of userStats) {
       const usage = new UsageByUserDto();
       usage.userId = user.userId;
-      usage.userFirstName = user.userFirstName;
-      usage.userLastName = user.userLastName;
+      usage.userName = user.userName;
       usage.photos = user.photos;
       usage.videos = user.videos;
       usage.usage = user.usage;

+ 4 - 8
server/src/domain/user/dto/create-user.dto.spec.ts

@@ -7,8 +7,7 @@ describe('create user DTO', () => {
     const params: Partial<CreateUserDto> = {
       email: undefined,
       password: 'password',
-      firstName: 'first name',
-      lastName: 'last name',
+      name: 'name',
     };
     let dto: CreateUserDto = plainToInstance(CreateUserDto, params);
     let errors = await validate(dto);
@@ -31,8 +30,7 @@ describe('create user DTO', () => {
     const dto = plainToInstance(CreateUserDto, {
       email: someEmail,
       password: 'some password',
-      firstName: 'some first name',
-      lastName: 'some last name',
+      name: 'some name',
     });
     const errors = await validate(dto);
     expect(errors).toHaveLength(0);
@@ -48,8 +46,7 @@ describe('create admin DTO', () => {
       isAdmin: true,
       email: someEmail,
       password: 'some password',
-      firstName: 'some first name',
-      lastName: 'some last name',
+      name: 'some name',
     });
     const errors = await validate(dto);
     expect(errors).toHaveLength(0);
@@ -64,8 +61,7 @@ describe('create user oauth DTO', () => {
     const dto = plainToInstance(CreateUserOAuthDto, {
       email: someEmail,
       oauthId: 'some oauth id',
-      firstName: 'some first name',
-      lastName: 'some last name',
+      name: 'some name',
     });
     const errors = await validate(dto);
     expect(errors).toHaveLength(0);

+ 3 - 12
server/src/domain/user/dto/create-user.dto.ts

@@ -13,11 +13,7 @@ export class CreateUserDto {
 
   @IsNotEmpty()
   @IsString()
-  firstName!: string;
-
-  @IsNotEmpty()
-  @IsString()
-  lastName!: string;
+  name!: string;
 
   @Optional({ nullable: true })
   @IsString()
@@ -45,10 +41,7 @@ export class CreateAdminDto {
   password!: string;
 
   @IsNotEmpty()
-  firstName!: string;
-
-  @IsNotEmpty()
-  lastName!: string;
+  name!: string;
 }
 
 export class CreateUserOAuthDto {
@@ -59,7 +52,5 @@ export class CreateUserOAuthDto {
   @IsNotEmpty()
   oauthId!: string;
 
-  firstName?: string;
-
-  lastName?: string;
+  name?: string;
 }

+ 1 - 6
server/src/domain/user/dto/update-user.dto.ts

@@ -17,12 +17,7 @@ export class UpdateUserDto {
   @Optional()
   @IsString()
   @IsNotEmpty()
-  firstName?: string;
-
-  @Optional()
-  @IsString()
-  @IsNotEmpty()
-  lastName?: string;
+  name?: string;
 
   @Optional()
   @IsString()

+ 2 - 4
server/src/domain/user/response-dto/user-response.dto.ts

@@ -2,8 +2,7 @@ import { UserEntity } from '@app/infra/entities';
 
 export class UserDto {
   id!: string;
-  firstName!: string;
-  lastName!: string;
+  name!: string;
   email!: string;
   profileImagePath!: string;
 }
@@ -24,8 +23,7 @@ export const mapSimpleUser = (entity: UserEntity): UserDto => {
   return {
     id: entity.id,
     email: entity.email,
-    firstName: entity.firstName,
-    lastName: entity.lastName,
+    name: entity.name,
     profileImagePath: entity.profileImagePath,
   };
 };

+ 3 - 6
server/src/domain/user/user.service.spec.ts

@@ -289,8 +289,7 @@ describe(UserService.name, () => {
       await expect(
         sut.create({
           email: 'john_smith@email.com',
-          firstName: 'John',
-          lastName: 'Smith',
+          name: 'John Smith',
           password: 'password',
         }),
       ).rejects.toBeInstanceOf(BadRequestException);
@@ -303,8 +302,7 @@ describe(UserService.name, () => {
       await expect(
         sut.create({
           email: userStub.user1.email,
-          firstName: userStub.user1.firstName,
-          lastName: userStub.user1.lastName,
+          name: userStub.user1.name,
           password: 'password',
           storageLabel: 'label',
         }),
@@ -313,8 +311,7 @@ describe(UserService.name, () => {
       expect(userMock.getAdmin).toBeCalled();
       expect(userMock.create).toBeCalledWith({
         email: userStub.user1.email,
-        firstName: userStub.user1.firstName,
-        lastName: userStub.user1.lastName,
+        name: userStub.user1.name,
         storageLabel: 'label',
         password: expect.anything(),
       });

+ 1 - 4
server/src/infra/entities/user.entity.ts

@@ -16,10 +16,7 @@ export class UserEntity {
   id!: string;
 
   @Column({ default: '' })
-  firstName!: string;
-
-  @Column({ default: '' })
-  lastName!: string;
+  name!: string;
 
   @Column({ default: false })
   isAdmin!: boolean;

+ 21 - 0
server/src/infra/migrations/1699322864544-UserNameConsolidation.ts

@@ -0,0 +1,21 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class AddUsername1699322864544 implements MigrationInterface {
+    name = 'AddUsername1699322864544'
+
+    public async up(queryRunner: QueryRunner): Promise<void> {
+        await queryRunner.query(`ALTER TABLE "users" ADD "name" character varying NOT NULL DEFAULT ''`);
+        await queryRunner.query(`UPDATE "users" SET "name" = CONCAT(COALESCE("firstName", ''), ' ', COALESCE("lastName", ''))`);
+        await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "firstName"`);
+        await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "lastName"`);
+    }
+
+    public async down(queryRunner: QueryRunner): Promise<void> {
+        await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "name"`);
+        await queryRunner.query(`ALTER TABLE "users" ADD "lastName" character varying NOT NULL DEFAULT ''`);
+        await queryRunner.query(`ALTER TABLE "users" ADD "firstName" character varying NOT NULL DEFAULT ''`);
+        await queryRunner.query(`UPDATE "users" SET "lastName" = COALESCE("email", '')`);
+        await queryRunner.query(`UPDATE "users" SET "firstName" = COALESCE("email", '')`);
+    }
+
+}

+ 1 - 2
server/src/infra/repositories/user.repository.ts

@@ -80,8 +80,7 @@ export class UserRepository implements IUserRepository {
     const stats = await this.userRepository
       .createQueryBuilder('users')
       .select('users.id', 'userId')
-      .addSelect('users.firstName', 'userFirstName')
-      .addSelect('users.lastName', 'userLastName')
+      .addSelect('users.name', 'userName')
       .addSelect(`COUNT(assets.id) FILTER (WHERE assets.type = 'IMAGE' AND assets.isVisible)`, 'photos')
       .addSelect(`COUNT(assets.id) FILTER (WHERE assets.type = 'VIDEO' AND assets.isVisible)`, 'videos')
       .addSelect('COALESCE(SUM(exif.fileSizeInByte), 0)', 'usage')

+ 3 - 6
server/test/e2e/activity.e2e-spec.ts

@@ -350,8 +350,7 @@ describe(`${ActivityController.name} (e2e)`, () => {
       const { id: userId } = await api.userApi.create(server, admin.accessToken, {
         email: 'user1@immich.app',
         password: 'Password123',
-        firstName: 'User 1',
-        lastName: 'Test',
+        name: 'User 1',
       });
       await api.albumApi.addUsers(server, admin.accessToken, album.id, { sharedUserIds: [userId] });
       const nonOwner = await api.authApi.login(server, { email: 'user1@immich.app', password: 'Password123' });
@@ -371,8 +370,7 @@ describe(`${ActivityController.name} (e2e)`, () => {
       const { id: userId } = await api.userApi.create(server, admin.accessToken, {
         email: 'user1@immich.app',
         password: 'Password123',
-        firstName: 'User 1',
-        lastName: 'Test',
+        name: 'User 1',
       });
       await api.albumApi.addUsers(server, admin.accessToken, album.id, { sharedUserIds: [userId] });
       const nonOwner = await api.authApi.login(server, { email: 'user1@immich.app', password: 'Password123' });
@@ -393,8 +391,7 @@ describe(`${ActivityController.name} (e2e)`, () => {
       const { id: userId } = await api.userApi.create(server, admin.accessToken, {
         email: 'user1@immich.app',
         password: 'Password123',
-        firstName: 'User 1',
-        lastName: 'Test',
+        name: 'User 1',
       });
       await api.albumApi.addUsers(server, admin.accessToken, album.id, { sharedUserIds: [userId] });
       const nonOwner = await api.authApi.login(server, { email: 'user1@immich.app', password: 'Password123' });

+ 2 - 4
server/test/e2e/album.e2e-spec.ts

@@ -41,14 +41,12 @@ describe(`${AlbumController.name} (e2e)`, () => {
       api.userApi.create(server, admin.accessToken, {
         email: 'user1@immich.app',
         password: 'Password123',
-        firstName: 'User 1',
-        lastName: 'Test',
+        name: 'User 1',
       }),
       api.userApi.create(server, admin.accessToken, {
         email: 'user2@immich.app',
         password: 'Password123',
-        firstName: 'User 2',
-        lastName: 'Test',
+        name: 'User 2',
       }),
     ]);
 

+ 2 - 4
server/test/e2e/asset.e2e-spec.ts

@@ -22,15 +22,13 @@ import request from 'supertest';
 const user1Dto = {
   email: 'user1@immich.app',
   password: 'Password123',
-  firstName: 'User 1',
-  lastName: 'Test',
+  name: 'User 1',
 };
 
 const user2Dto = {
   email: 'user2@immich.app',
   password: 'Password123',
-  firstName: 'User 2',
-  lastName: 'Test',
+  name: 'User 2',
 };
 
 const makeUploadDto = (options?: { omit: string }): Record<string, any> => {

+ 7 - 13
server/test/e2e/auth.e2e-spec.ts

@@ -13,15 +13,13 @@ import {
 import { testApp } from '@test/test-utils';
 import request from 'supertest';
 
-const firstName = 'Immich';
-const lastName = 'Admin';
+const name = 'Immich Admin';
 const password = 'Password123';
 const email = 'admin@immich.app';
 
 const adminSignupResponse = {
   id: expect.any(String),
-  firstName: 'Immich',
-  lastName: 'Admin',
+  name: 'Immich Admin',
   email: 'admin@immich.app',
   storageLabel: 'admin',
   externalPath: null,
@@ -64,23 +62,19 @@ describe(`${AuthController.name} (e2e)`, () => {
     const invalid = [
       {
         should: 'require an email address',
-        data: { firstName, lastName, password },
+        data: { name, password },
       },
       {
         should: 'require a password',
-        data: { firstName, lastName, email },
+        data: { name, email },
       },
       {
-        should: 'require a first name ',
-        data: { lastName, email, password },
-      },
-      {
-        should: 'require a last name ',
-        data: { firstName, email, password },
+        should: 'require a name',
+        data: { email, password },
       },
       {
         should: 'require a valid email',
-        data: { firstName, lastName, email: 'immich', password },
+        data: { name, email: 'immich', password },
       },
     ];
 

+ 2 - 4
server/test/e2e/library.e2e-spec.ts

@@ -15,15 +15,13 @@ describe(`${LibraryController.name} (e2e)`, () => {
   const user1Dto = {
     email: 'user1@immich.app',
     password: 'Password123',
-    firstName: 'User 1',
-    lastName: 'Test',
+    name: 'User 1',
   };
 
   const user2Dto = {
     email: 'user2@immich.app',
     password: 'Password123',
-    firstName: 'User 2',
-    lastName: 'Test',
+    name: 'User 2',
   };
 
   beforeAll(async () => {

+ 2 - 4
server/test/e2e/partner.e2e-spec.ts

@@ -10,15 +10,13 @@ import request from 'supertest';
 const user1Dto = {
   email: 'user1@immich.app',
   password: 'Password123',
-  firstName: 'User 1',
-  lastName: 'Test',
+  name: 'User 1',
 };
 
 const user2Dto = {
   email: 'user2@immich.app',
   password: 'Password123',
-  firstName: 'User 2',
-  lastName: 'Test',
+  name: 'User 2',
 };
 
 describe(`${PartnerController.name} (e2e)`, () => {

+ 2 - 3
server/test/e2e/server-info.e2e-spec.ts

@@ -111,7 +111,7 @@ describe(`${ServerInfoController.name} (e2e)`, () => {
 
     it('should only work for admins', async () => {
       const loginDto = { email: 'test@immich.app', password: 'Immich123' };
-      await api.userApi.create(server, accessToken, { ...loginDto, firstName: 'test', lastName: 'test' });
+      await api.userApi.create(server, accessToken, { ...loginDto, name: 'test' });
       const { accessToken: userAccessToken } = await api.authApi.login(server, loginDto);
       const { status, body } = await request(server)
         .get('/server-info/statistics')
@@ -132,9 +132,8 @@ describe(`${ServerInfoController.name} (e2e)`, () => {
           {
             photos: 0,
             usage: 0,
-            userFirstName: 'Immich',
+            userName: 'Immich Admin',
             userId: loginResponse.userId,
-            userLastName: 'Admin',
             videos: 0,
           },
         ],

+ 1 - 2
server/test/e2e/shared-link.e2e-spec.ts

@@ -11,8 +11,7 @@ import request from 'supertest';
 const user1Dto = {
   email: 'user1@immich.app',
   password: 'Password123',
-  firstName: 'User 1',
-  lastName: 'Test',
+  name: 'User 1',
 };
 
 describe(`${PartnerController.name} (e2e)`, () => {

+ 1 - 2
server/test/e2e/system-config.e2e-spec.ts

@@ -64,8 +64,7 @@ describe(`${SystemConfigController.name} (e2e)`, () => {
       const credentials = { email: 'user1@immich.app', password: 'Password123' };
       await api.userApi.create(server, admin.accessToken, {
         ...credentials,
-        firstName: 'User 1',
-        lastName: 'Test',
+        name: 'User 1',
       });
       const { accessToken } = await api.authApi.login(server, credentials);
       const { status, body } = await request(server)

+ 8 - 16
server/test/e2e/user.e2e-spec.ts

@@ -59,8 +59,7 @@ describe(`${UserController.name}`, () => {
       const user1 = await api.userApi.create(server, accessToken, {
         email: `user1@immich.app`,
         password: 'Password123',
-        firstName: `User 1`,
-        lastName: 'Test',
+        name: `User 1`,
       });
 
       await api.userApi.delete(server, accessToken, user1.id);
@@ -78,8 +77,7 @@ describe(`${UserController.name}`, () => {
       const user1 = await api.userApi.create(server, accessToken, {
         email: `user1@immich.app`,
         password: 'Password123',
-        firstName: `User 1`,
-        lastName: 'Test',
+        name: `User 1`,
       });
 
       await api.userApi.delete(server, accessToken, user1.id);
@@ -149,8 +147,7 @@ describe(`${UserController.name}`, () => {
           isAdmin: true,
           email: 'user1@immich.app',
           password: 'Password123',
-          firstName: 'Immich',
-          lastName: 'User',
+          name: 'Immich',
         })
         .set('Authorization', `Bearer ${accessToken}`);
       expect(body).toMatchObject({
@@ -167,8 +164,7 @@ describe(`${UserController.name}`, () => {
         .send({
           email: 'no-memories@immich.app',
           password: 'Password123',
-          firstName: 'No Memories',
-          lastName: 'User',
+          name: 'No Memories',
           memoriesEnabled: false,
         })
         .set('Authorization', `Bearer ${accessToken}`);
@@ -186,8 +182,7 @@ describe(`${UserController.name}`, () => {
     beforeEach(async () => {
       userToDelete = await api.userApi.create(server, accessToken, {
         email: userStub.user1.email,
-        firstName: userStub.user1.firstName,
-        lastName: userStub.user1.lastName,
+        name: userStub.user1.name,
         password: 'superSecurePassword',
       });
     });
@@ -246,8 +241,7 @@ describe(`${UserController.name}`, () => {
       const user = await api.userApi.create(server, accessToken, {
         email: 'user1@immich.app',
         password: 'Password123',
-        firstName: 'Immich',
-        lastName: 'User',
+        name: 'Immich User',
       });
 
       const { status, body } = await request(server)
@@ -284,15 +278,13 @@ describe(`${UserController.name}`, () => {
       const before = await api.userApi.get(server, accessToken, loginResponse.userId);
       const after = await api.userApi.update(server, accessToken, {
         id: before.id,
-        firstName: 'First Name',
-        lastName: 'Last Name',
+        name: 'Name',
       });
 
       expect(after).toEqual({
         ...before,
         updatedAt: expect.any(String),
-        firstName: 'First Name',
-        lastName: 'Last Name',
+        name: 'Name',
       });
       expect(before.updatedAt).not.toEqual(after.updatedAt);
     });

+ 5 - 10
server/test/fixtures/auth.stub.ts

@@ -1,8 +1,7 @@
 import { AuthUserDto } from '@app/domain';
 
 export const adminSignupStub = {
-  firstName: 'Immich',
-  lastName: 'Admin',
+  name: 'Immich Admin',
   email: 'admin@immich.app',
   password: 'Password123',
 };
@@ -103,9 +102,8 @@ export const loginResponseStub = {
   admin: {
     response: {
       accessToken: expect.any(String),
-      firstName: 'Immich',
+      name: 'Immich Admin',
       isAdmin: true,
-      lastName: 'Admin',
       profileImagePath: '',
       shouldChangePassword: true,
       userEmail: 'admin@immich.app',
@@ -117,8 +115,7 @@ export const loginResponseStub = {
       accessToken: 'cmFuZG9tLWJ5dGVz',
       userId: 'user-id',
       userEmail: 'immich@test.com',
-      firstName: 'immich_first_name',
-      lastName: 'immich_last_name',
+      name: 'immich_name',
       profileImagePath: '',
       isAdmin: false,
       shouldChangePassword: false,
@@ -133,8 +130,7 @@ export const loginResponseStub = {
       accessToken: 'cmFuZG9tLWJ5dGVz',
       userId: 'user-id',
       userEmail: 'immich@test.com',
-      firstName: 'immich_first_name',
-      lastName: 'immich_last_name',
+      name: 'immich_name',
       profileImagePath: '',
       isAdmin: false,
       shouldChangePassword: false,
@@ -149,8 +145,7 @@ export const loginResponseStub = {
       accessToken: 'cmFuZG9tLWJ5dGVz',
       userId: 'user-id',
       userEmail: 'immich@test.com',
-      firstName: 'immich_first_name',
-      lastName: 'immich_last_name',
+      name: 'immich_name',
       profileImagePath: '',
       isAdmin: false,
       shouldChangePassword: false,

+ 7 - 14
server/test/fixtures/user.stub.ts

@@ -5,8 +5,7 @@ export const userStub = {
   admin: Object.freeze<UserEntity>({
     ...authStub.admin,
     password: 'admin_password',
-    firstName: 'admin_first_name',
-    lastName: 'admin_last_name',
+    name: 'admin_name',
     storageLabel: 'admin',
     externalPath: null,
     oauthId: '',
@@ -22,8 +21,7 @@ export const userStub = {
   user1: Object.freeze<UserEntity>({
     ...authStub.user1,
     password: 'immich_password',
-    firstName: 'immich_first_name',
-    lastName: 'immich_last_name',
+    name: 'immich_name',
     storageLabel: null,
     externalPath: null,
     oauthId: '',
@@ -39,8 +37,7 @@ export const userStub = {
   user2: Object.freeze<UserEntity>({
     ...authStub.user2,
     password: 'immich_password',
-    firstName: 'immich_first_name',
-    lastName: 'immich_last_name',
+    name: 'immich_name',
     storageLabel: null,
     externalPath: null,
     oauthId: '',
@@ -56,8 +53,7 @@ export const userStub = {
   storageLabel: Object.freeze<UserEntity>({
     ...authStub.user1,
     password: 'immich_password',
-    firstName: 'immich_first_name',
-    lastName: 'immich_last_name',
+    name: 'immich_name',
     storageLabel: 'label-1',
     externalPath: null,
     oauthId: '',
@@ -73,8 +69,7 @@ export const userStub = {
   externalPath1: Object.freeze<UserEntity>({
     ...authStub.user1,
     password: 'immich_password',
-    firstName: 'immich_first_name',
-    lastName: 'immich_last_name',
+    name: 'immich_name',
     storageLabel: 'label-1',
     externalPath: '/data/user1',
     oauthId: '',
@@ -90,8 +85,7 @@ export const userStub = {
   externalPath2: Object.freeze<UserEntity>({
     ...authStub.user1,
     password: 'immich_password',
-    firstName: 'immich_first_name',
-    lastName: 'immich_last_name',
+    name: 'immich_name',
     storageLabel: 'label-1',
     externalPath: '/data/user2',
     oauthId: '',
@@ -107,8 +101,7 @@ export const userStub = {
   profilePath: Object.freeze<UserEntity>({
     ...authStub.user1,
     password: 'immich_password',
-    firstName: 'immich_first_name',
-    lastName: 'immich_last_name',
+    name: 'immich_name',
     storageLabel: 'label-1',
     externalPath: null,
     oauthId: '',

+ 19 - 67
web/src/api/open-api/api.ts

@@ -1343,22 +1343,16 @@ export interface CreateUserDto {
     'externalPath'?: string | null;
     /**
      * 
-     * @type {string}
+     * @type {boolean}
      * @memberof CreateUserDto
      */
-    'firstName': string;
+    'memoriesEnabled'?: boolean;
     /**
      * 
      * @type {string}
      * @memberof CreateUserDto
      */
-    'lastName': string;
-    /**
-     * 
-     * @type {boolean}
-     * @memberof CreateUserDto
-     */
-    'memoriesEnabled'?: boolean;
+    'name': string;
     /**
      * 
      * @type {string}
@@ -2137,12 +2131,6 @@ export interface LoginResponseDto {
      * @memberof LoginResponseDto
      */
     'accessToken': string;
-    /**
-     * 
-     * @type {string}
-     * @memberof LoginResponseDto
-     */
-    'firstName': string;
     /**
      * 
      * @type {boolean}
@@ -2154,7 +2142,7 @@ export interface LoginResponseDto {
      * @type {string}
      * @memberof LoginResponseDto
      */
-    'lastName': string;
+    'name': string;
     /**
      * 
      * @type {string}
@@ -2391,12 +2379,6 @@ export interface PartnerResponseDto {
      * @memberof PartnerResponseDto
      */
     'externalPath': string | null;
-    /**
-     * 
-     * @type {string}
-     * @memberof PartnerResponseDto
-     */
-    'firstName': string;
     /**
      * 
      * @type {string}
@@ -2417,16 +2399,16 @@ export interface PartnerResponseDto {
     'isAdmin': boolean;
     /**
      * 
-     * @type {string}
+     * @type {boolean}
      * @memberof PartnerResponseDto
      */
-    'lastName': string;
+    'memoriesEnabled'?: boolean;
     /**
      * 
-     * @type {boolean}
+     * @type {string}
      * @memberof PartnerResponseDto
      */
-    'memoriesEnabled'?: boolean;
+    'name': string;
     /**
      * 
      * @type {string}
@@ -3431,13 +3413,7 @@ export interface SignUpDto {
      * @type {string}
      * @memberof SignUpDto
      */
-    'firstName': string;
-    /**
-     * 
-     * @type {string}
-     * @memberof SignUpDto
-     */
-    'lastName': string;
+    'name': string;
     /**
      * 
      * @type {string}
@@ -4380,12 +4356,6 @@ export interface UpdateUserDto {
      * @memberof UpdateUserDto
      */
     'externalPath'?: string;
-    /**
-     * 
-     * @type {string}
-     * @memberof UpdateUserDto
-     */
-    'firstName'?: string;
     /**
      * 
      * @type {string}
@@ -4400,16 +4370,16 @@ export interface UpdateUserDto {
     'isAdmin'?: boolean;
     /**
      * 
-     * @type {string}
+     * @type {boolean}
      * @memberof UpdateUserDto
      */
-    'lastName'?: string;
+    'memoriesEnabled'?: boolean;
     /**
      * 
-     * @type {boolean}
+     * @type {string}
      * @memberof UpdateUserDto
      */
-    'memoriesEnabled'?: boolean;
+    'name'?: string;
     /**
      * 
      * @type {string}
@@ -4447,12 +4417,6 @@ export interface UsageByUserDto {
      * @memberof UsageByUserDto
      */
     'usage': number;
-    /**
-     * 
-     * @type {string}
-     * @memberof UsageByUserDto
-     */
-    'userFirstName': string;
     /**
      * 
      * @type {string}
@@ -4464,7 +4428,7 @@ export interface UsageByUserDto {
      * @type {string}
      * @memberof UsageByUserDto
      */
-    'userLastName': string;
+    'userName': string;
     /**
      * 
      * @type {number}
@@ -4484,12 +4448,6 @@ export interface UserDto {
      * @memberof UserDto
      */
     'email': string;
-    /**
-     * 
-     * @type {string}
-     * @memberof UserDto
-     */
-    'firstName': string;
     /**
      * 
      * @type {string}
@@ -4501,7 +4459,7 @@ export interface UserDto {
      * @type {string}
      * @memberof UserDto
      */
-    'lastName': string;
+    'name': string;
     /**
      * 
      * @type {string}
@@ -4539,12 +4497,6 @@ export interface UserResponseDto {
      * @memberof UserResponseDto
      */
     'externalPath': string | null;
-    /**
-     * 
-     * @type {string}
-     * @memberof UserResponseDto
-     */
-    'firstName': string;
     /**
      * 
      * @type {string}
@@ -4559,16 +4511,16 @@ export interface UserResponseDto {
     'isAdmin': boolean;
     /**
      * 
-     * @type {string}
+     * @type {boolean}
      * @memberof UserResponseDto
      */
-    'lastName': string;
+    'memoriesEnabled'?: boolean;
     /**
      * 
-     * @type {boolean}
+     * @type {string}
      * @memberof UserResponseDto
      */
-    'memoriesEnabled'?: boolean;
+    'name': string;
     /**
      * 
      * @type {string}

+ 1 - 1
web/src/lib/components/admin-page/delete-confirm-dialoge.svelte

@@ -27,7 +27,7 @@
   <svelte:fragment slot="prompt">
     <div class="flex flex-col gap-4">
       <p>
-        <b>{user.firstName} {user.lastName}</b>'s account and assets will be permanently deleted after 7 days.
+        <b>{user.name}</b>'s account and assets will be permanently deleted after 7 days.
       </p>
       <p>Are you sure you want to continue?</p>
     </div>

+ 1 - 1
web/src/lib/components/admin-page/restore-dialoge.svelte

@@ -16,6 +16,6 @@
 
 <ConfirmDialogue title="Restore User" confirmText="Continue" confirmColor="green" on:confirm={restoreUser} on:cancel>
   <svelte:fragment slot="prompt">
-    <p><b>{user.firstName} {user.lastName}</b>'s account will be restored.</p>
+    <p><b>{user.name}</b>'s account will be restored.</p>
   </svelte:fragment>
 </ConfirmDialogue>

+ 1 - 1
web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte

@@ -96,7 +96,7 @@
           <tr
             class="flex h-[50px] w-full place-items-center text-center odd:bg-immich-gray even:bg-immich-bg odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50"
           >
-            <td class="w-1/4 text-ellipsis px-2 text-sm">{user.userFirstName} {user.userLastName}</td>
+            <td class="w-1/4 text-ellipsis px-2 text-sm">{user.userName}</td>
             <td class="w-1/4 text-ellipsis px-2 text-sm">{user.photos.toLocaleString($locale)}</td>
             <td class="w-1/4 text-ellipsis px-2 text-sm">{user.videos.toLocaleString($locale)}</td>
             <td class="w-1/4 text-ellipsis px-2 text-sm">{asByteUnitString(user.usage, $locale)}</td>

+ 1 - 2
web/src/lib/components/album-page/album-card.svelte

@@ -123,8 +123,7 @@
             <p>Owned</p>
           {:else}
             <p>
-              Shared by {albumOwner.firstName}
-              {albumOwner.lastName}
+              Shared by {albumOwner.name}
             </p>
           {/if}
         {/await}

+ 2 - 2
web/src/lib/components/album-page/album-options.svelte

@@ -56,7 +56,7 @@
                 <div>
                   <UserAvatar {user} size="md" />
                 </div>
-                <div class="w-full">{`${user.firstName} ${user.lastName}`}</div>
+                <div class="w-full">{user.name}</div>
                 <div>Owner</div>
               </div>
               {#each album.sharedUsers as user (user.id)}
@@ -64,7 +64,7 @@
                   <div>
                     <UserAvatar {user} size="md" />
                   </div>
-                  <div class="w-full">{`${user.firstName} ${user.lastName}`}</div>
+                  <div class="w-full">{user.name}</div>
                 </div>
               {/each}
             </div>

+ 4 - 4
web/src/lib/components/album-page/share-info-modal.svelte

@@ -56,7 +56,7 @@
     try {
       await api.albumApi.removeUserFromAlbum({ id: album.id, userId });
       dispatch('remove', userId);
-      const message = userId === 'me' ? `Left ${album.albumName}` : `Removed ${selectedRemoveUser.firstName}`;
+      const message = userId === 'me' ? `Left ${album.albumName}` : `Removed ${selectedRemoveUser.name}`;
       notificationController.show({ type: NotificationType.Info, message });
     } catch (e) {
       handleError(e, 'Unable to remove user');
@@ -78,7 +78,7 @@
       <div class="flex w-full place-items-center justify-between gap-4 p-5">
         <div class="flex place-items-center gap-4">
           <UserAvatar user={album.owner} size="md" autoColor />
-          <p class="text-sm font-medium">{album.owner.firstName} {album.owner.lastName}</p>
+          <p class="text-sm font-medium">{album.owner.name}</p>
         </div>
 
         <div id="icon-{album.owner.id}" class="flex place-items-center">
@@ -91,7 +91,7 @@
         >
           <div class="flex place-items-center gap-4">
             <UserAvatar {user} size="md" autoColor />
-            <p class="text-sm font-medium">{user.firstName} {user.lastName}</p>
+            <p class="text-sm font-medium">{user.name}</p>
           </div>
 
           <div id="icon-{user.id}" class="flex place-items-center">
@@ -138,7 +138,7 @@
 {#if selectedRemoveUser && selectedRemoveUser?.id !== currentUser?.id}
   <ConfirmDialogue
     title="Remove User?"
-    prompt="Are you sure you want to remove {selectedRemoveUser.firstName} {selectedRemoveUser.lastName}"
+    prompt="Are you sure you want to remove {selectedRemoveUser.name}"
     confirmText="Remove"
     on:confirm={handleRemoveUser}
     on:cancel={() => (selectedRemoveUser = null)}

+ 2 - 3
web/src/lib/components/album-page/user-selection-modal.svelte

@@ -72,7 +72,7 @@
               class="flex place-items-center gap-1 rounded-full border border-gray-400 p-1 transition-colors hover:bg-gray-200 dark:hover:bg-gray-700"
             >
               <UserAvatar {user} size="sm" autoColor />
-              <p class="text-xs font-medium">{user.firstName} {user.lastName}</p>
+              <p class="text-xs font-medium">{user.name}</p>
             </button>
           {/key}
         {/each}
@@ -99,8 +99,7 @@
 
             <div class="text-left">
               <p class="text-immich-fg dark:text-immich-dark-fg">
-                {user.firstName}
-                {user.lastName}
+                {user.name}
               </p>
               <p class="text-xs">
                 {user.email}

+ 2 - 7
web/src/lib/components/asset-viewer/activity-viewer.svelte

@@ -221,13 +221,8 @@
               <div class="flex p-3 mx-2 mt-3 rounded-full gap-4 items-center text-sm">
                 <div class="text-red-600"><Icon path={mdiHeart} size={20} /></div>
 
-                <div
-                  class="w-full"
-                  title={`${reaction.user.firstName} ${reaction.user.lastName} (${reaction.user.email})`}
-                >
-                  {`${reaction.user.firstName} ${reaction.user.lastName} liked ${
-                    assetType ? `this ${getAssetType(assetType).toLowerCase()}` : 'it'
-                  }`}
+                <div class="w-full" title={`${reaction.user.name} (${reaction.user.email})`}>
+                  {`${reaction.user.name} liked ${assetType ? `this ${getAssetType(assetType).toLowerCase()}` : 'it'}`}
                 </div>
                 {#if assetId === undefined && reaction.assetId}
                   <div class="aspect-square w-[75px] h-[75px]">

+ 1 - 2
web/src/lib/components/asset-viewer/detail-panel.svelte

@@ -315,8 +315,7 @@
 
       <div class="mb-auto mt-auto">
         <p>
-          {asset.owner.firstName}
-          {asset.owner.lastName}
+          {asset.owner.name}
         </p>
       </div>
     </div>

+ 4 - 11
web/src/lib/components/forms/admin-registration-form.svelte

@@ -27,15 +27,13 @@
 
       const email = form.get('email');
       const password = form.get('password');
-      const firstName = form.get('firstName');
-      const lastName = form.get('lastName');
+      const name = form.get('name');
 
       const { status } = await api.authenticationApi.signUpAdmin({
         signUpDto: {
           email: String(email),
           password: String(password),
-          firstName: String(firstName),
-          lastName: String(lastName),
+          name: String(name),
         },
       });
 
@@ -83,13 +81,8 @@
   </div>
 
   <div class="flex flex-col gap-2">
-    <label class="immich-form-label" for="firstName">First Name</label>
-    <input class="immich-form-input" id="firstName" name="firstName" type="text" autocomplete="given-name" required />
-  </div>
-
-  <div class="flex flex-col gap-2">
-    <label class="immich-form-label" for="lastName">Last Name</label>
-    <input class="immich-form-input" id="lastName" name="lastName" type="text" autocomplete="family-name" required />
+    <label class="immich-form-label" for="name">Name</label>
+    <input class="immich-form-input" id="name" name="name" type="text" autocomplete="name" required />
   </div>
 
   {#if error}

+ 4 - 11
web/src/lib/components/forms/create-user-form.svelte

@@ -38,16 +38,14 @@
 
       const email = form.get('email');
       const password = form.get('password');
-      const firstName = form.get('firstName');
-      const lastName = form.get('lastName');
+      const name = form.get('name');
 
       try {
         const { status } = await api.userApi.createUser({
           createUserDto: {
             email: String(email),
             password: String(password),
-            firstName: String(firstName),
-            lastName: String(lastName),
+            name: String(name),
           },
         });
 
@@ -112,13 +110,8 @@
     </div>
 
     <div class="m-4 flex flex-col gap-2">
-      <label class="immich-form-label" for="firstName">First Name</label>
-      <input class="immich-form-input" id="firstName" name="firstName" type="text" required />
-    </div>
-
-    <div class="m-4 flex flex-col gap-2">
-      <label class="immich-form-label" for="lastName">Last Name</label>
-      <input class="immich-form-input" id="lastName" name="lastName" type="text" required />
+      <label class="immich-form-label" for="name">Name</label>
+      <input class="immich-form-input" id="name" name="name" type="text" required />
     </div>
 
     {#if error}

+ 5 - 18
web/src/lib/components/forms/edit-user-form.svelte

@@ -20,13 +20,12 @@
 
   const editUser = async () => {
     try {
-      const { id, email, firstName, lastName, storageLabel, externalPath } = user;
+      const { id, email, name, storageLabel, externalPath } = user;
       const { status } = await api.userApi.updateUser({
         updateUserDto: {
           id,
           email,
-          firstName,
-          lastName,
+          name,
           storageLabel: storageLabel || '',
           externalPath: externalPath || '',
         },
@@ -84,20 +83,8 @@
     </div>
 
     <div class="m-4 flex flex-col gap-2">
-      <label class="immich-form-label" for="firstName">First Name</label>
-      <input
-        class="immich-form-input"
-        id="firstName"
-        name="firstName"
-        type="text"
-        required
-        bind:value={user.firstName}
-      />
-    </div>
-
-    <div class="m-4 flex flex-col gap-2">
-      <label class="immich-form-label" for="lastName">Last Name</label>
-      <input class="immich-form-input" id="lastName" name="lastName" type="text" required bind:value={user.lastName} />
+      <label class="immich-form-label" for="name">Name</label>
+      <input class="immich-form-input" id="name" name="name" type="text" required bind:value={user.name} />
     </div>
 
     <div class="m-4 flex flex-col gap-2">
@@ -161,7 +148,7 @@
   >
     <svelte:fragment slot="prompt">
       <p>
-        Are you sure you want to reset <b>{user.firstName} {user.lastName}</b>'s password?
+        Are you sure you want to reset <b>{user.name}</b>'s password?
       </p>
     </svelte:fragment>
   </ConfirmDialogue>

+ 1 - 2
web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte

@@ -26,8 +26,7 @@
 
     <div>
       <p class="text-center text-lg font-medium text-immich-primary dark:text-immich-dark-primary">
-        {user.firstName}
-        {user.lastName}
+        {user.name}
       </p>
       <p class="text-sm text-gray-500 dark:text-immich-dark-fg">{user.email}</p>
     </div>

+ 1 - 1
web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte

@@ -133,7 +133,7 @@
               out:fade={{ delay: 200, duration: 150 }}
               class="absolute -bottom-12 right-5 rounded-md border bg-gray-500 p-2 text-[12px] text-gray-100 shadow-md dark:border-immich-dark-gray dark:bg-immich-dark-gray"
             >
-              <p>{user.firstName} {user.lastName}</p>
+              <p>{user.name}</p>
               <p>{user.email}</p>
             </div>
           {/if}

+ 3 - 4
web/src/lib/components/shared-components/user-avatar.svelte

@@ -9,8 +9,7 @@
 
   interface User {
     id: string;
-    firstName: string;
-    lastName: string;
+    name: string;
     email: string;
     profileImagePath: string;
   }
@@ -51,7 +50,7 @@
 
   $: colorClass = colorClasses[autoColor ? getUserColor() : color];
   $: sizeClass = sizeClasses[size];
-  $: title = `${user.firstName} ${user.lastName} (${user.email})`;
+  $: title = `${user.name} (${user.email})`;
   $: interactiveClass = interactive
     ? 'border-2 border-immich-primary hover:border-immich-dark-primary dark:hover:border-immich-primary dark:border-immich-dark-primary transition-colors'
     : '';
@@ -82,7 +81,7 @@
       class:font-medium={!autoColor}
       class:font-semibold={autoColor}
     >
-      {((user.firstName[0] || '') + (user.lastName[0] || '')).toUpperCase()}
+      {(user.name[0] || '').toUpperCase()}
     </span>
   {/if}
 </figure>

+ 1 - 2
web/src/lib/components/user-settings-page/partner-selection-modal.svelte

@@ -61,8 +61,7 @@
 
           <div class="text-left">
             <p class="text-immich-fg dark:text-immich-dark-fg">
-              {user.firstName}
-              {user.lastName}
+              {user.name}
             </p>
             <p class="text-xs">
               {user.email}

+ 5 - 6
web/src/lib/components/user-settings-page/partner-settings.svelte

@@ -116,8 +116,7 @@
             <UserAvatar user={partner.user} size="md" autoColor />
             <div class="text-left">
               <p class="text-immich-fg dark:text-immich-dark-fg">
-                {partner.user.firstName}
-                {partner.user.lastName}
+                {partner.user.name}
               </p>
               <p class="text-xs text-immich-fg/75 dark:text-immich-dark-fg/75">
                 {partner.user.email}
@@ -139,8 +138,8 @@
           <!-- I am sharing my assets with this user -->
           {#if partner.sharedByMe}
             <hr class="my-4 border border-gray-200 dark:border-gray-700" />
-            <p class="text-xs font-medium my-4">SHARED WITH {partner.user.firstName.toUpperCase()}</p>
-            <p class="text-md">{partner.user.firstName} can access</p>
+            <p class="text-xs font-medium my-4">SHARED WITH {partner.user.name.toUpperCase()}</p>
+            <p class="text-md">{partner.user.name} can access</p>
             <ul class="text-sm">
               <li class="flex gap-2 place-items-center py-1 mt-2">
                 <Icon path={mdiCheck} /> All your photos and videos except those in Archived and Deleted
@@ -154,7 +153,7 @@
           <!-- this user is sharing assets with me -->
           {#if partner.sharedWithMe}
             <hr class="my-4 border border-gray-200 dark:border-gray-700" />
-            <p class="text-xs font-medium my-4">PHOTOS FROM {partner.user.firstName.toUpperCase()}</p>
+            <p class="text-xs font-medium my-4">PHOTOS FROM {partner.user.name.toUpperCase()}</p>
             <SettingSwitch
               title="Show in timeline"
               subtitle="Show photos and videos from this user in your timeline"
@@ -183,7 +182,7 @@
 {#if removePartner}
   <ConfirmDialogue
     title="Stop sharing your photos?"
-    prompt="{removePartner.firstName} will no longer be able to access your photos."
+    prompt="{removePartner.name} will no longer be able to access your photos."
     on:cancel={() => (removePartner = null)}
     on:confirm={() => handleRemovePartner()}
   />

+ 2 - 15
web/src/lib/components/user-settings-page/user-profile-settings.svelte

@@ -17,8 +17,7 @@
         updateUserDto: {
           id: user.id,
           email: user.email,
-          firstName: user.firstName,
-          lastName: user.lastName,
+          name: user.name,
         },
       });
 
@@ -47,19 +46,7 @@
 
         <SettingInputField inputType={SettingInputFieldType.EMAIL} label="EMAIL" bind:value={user.email} />
 
-        <SettingInputField
-          inputType={SettingInputFieldType.TEXT}
-          label="FIRST NAME"
-          bind:value={user.firstName}
-          required={true}
-        />
-
-        <SettingInputField
-          inputType={SettingInputFieldType.TEXT}
-          label="LAST NAME"
-          bind:value={user.lastName}
-          required={true}
-        />
+        <SettingInputField inputType={SettingInputFieldType.TEXT} label="NAME" bind:value={user.name} required={true} />
 
         <SettingInputField
           inputType={SettingInputFieldType.TEXT}

+ 1 - 2
web/src/routes/(user)/partners/[userId]/+page.svelte

@@ -39,8 +39,7 @@
     <ControlAppBar showBackButton backIcon={mdiArrowLeft} on:close-button-click={() => goto(AppRoute.SHARING)}>
       <svelte:fragment slot="leading">
         <p class="whitespace-nowrap text-immich-fg dark:text-immich-dark-fg">
-          {data.partner.firstName}
-          {data.partner.lastName}'s photos
+          {data.partner.name}'s photos
         </p>
       </svelte:fragment>
     </ControlAppBar>

+ 1 - 2
web/src/routes/(user)/sharing/+page.svelte

@@ -72,8 +72,7 @@
               <UserAvatar user={partner} size="lg" autoColor />
               <div class="text-left">
                 <p class="text-immich-fg dark:text-immich-dark-fg">
-                  {partner.firstName}
-                  {partner.lastName}
+                  {partner.name}
                 </p>
                 <p class="text-xs text-immich-fg/75 dark:text-immich-dark-fg/75">
                   {partner.email}

+ 3 - 5
web/src/routes/admin/user-management/+page.svelte

@@ -168,8 +168,7 @@
         >
           <tr class="flex w-full place-items-center">
             <th class="w-4/12 text-center text-sm font-medium">Email</th>
-            <th class="w-2/12 text-center text-sm font-medium">First name</th>
-            <th class="w-2/12 text-center text-sm font-medium">Last name</th>
+            <th class="w-2/12 text-center text-sm font-medium">Name</th>
             <th class="w-2/12 text-center text-sm font-medium">Can import</th>
             <th class="w-2/12 text-center text-sm font-medium">Action</th>
           </tr>
@@ -187,8 +186,7 @@
                 }`}
               >
                 <td class="w-4/12 text-ellipsis break-all px-2 text-sm">{user.email}</td>
-                <td class="w-2/12 text-ellipsis break-all px-2 text-sm">{user.firstName}</td>
-                <td class="w-2/12 text-ellipsis break-all px-2 text-sm">{user.lastName}</td>
+                <td class="w-2/12 text-ellipsis break-all px-2 text-sm">{user.name}</td>
                 <td class="w-2/12 text-ellipsis break-all px-2 text-sm">
                   <div class="container mx-auto flex flex-wrap justify-center">
                     {#if user.externalPath}
@@ -253,7 +251,7 @@
                     : 'bg-immich-bg dark:bg-immich-dark-gray/50'
                 }`}
               >
-                <td class="w-1/4 text-ellipsis break-words px-2 text-sm">{user.firstName} {user.lastName}</td>
+                <td class="w-1/4 text-ellipsis break-words px-2 text-sm">{user.name}</td>
                 <td class="w-1/2 text-ellipsis break-all px-2 text-sm">{user.email}</td>
                 <td class="w-1/4 text-ellipsis px-2 text-sm">
                   {#if !isDeleted(user)}

+ 1 - 2
web/src/routes/auth/change-password/+page.svelte

@@ -16,8 +16,7 @@
 
 <FullscreenContainer title={data.meta.title}>
   <p slot="message">
-    Hi {data.user.firstName}
-    {data.user.lastName} ({data.user.email}),
+    Hi {data.user.name} ({data.user.email}),
     <br />
     <br />
     This is either the first time you are signing into the system or a request has been made to change your password. Please

+ 1 - 2
web/src/test-data/factories/user-factory.ts

@@ -5,8 +5,7 @@ import { Sync } from 'factory.ts';
 export const userFactory = Sync.makeFactory<UserResponseDto>({
   id: Sync.each(() => faker.datatype.uuid()),
   email: Sync.each(() => faker.internet.email()),
-  firstName: Sync.each(() => faker.name.firstName()),
-  lastName: Sync.each(() => faker.name.lastName()),
+  name: Sync.each(() => faker.name.fullName()),
   storageLabel: Sync.each(() => faker.random.alphaNumeric()),
   externalPath: Sync.each(() => faker.random.alphaNumeric()),
   profileImagePath: '',