1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-29 07:01:59 +00:00

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

This commit is contained in:
Brian Austin 2023-11-11 20:03:32 -05:00 committed by GitHub
parent 413ab2c538
commit 7fca0d8da5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
98 changed files with 261 additions and 542 deletions

View file

@ -1341,24 +1341,18 @@ export interface CreateUserDto {
* @memberof CreateUserDto
*/
'externalPath'?: string | null;
/**
*
* @type {string}
* @memberof CreateUserDto
*/
'firstName': string;
/**
*
* @type {string}
* @memberof CreateUserDto
*/
'lastName': string;
/**
*
* @type {boolean}
* @memberof CreateUserDto
*/
'memoriesEnabled'?: boolean;
/**
*
* @type {string}
* @memberof CreateUserDto
*/
'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}
@ -2415,18 +2397,18 @@ export interface PartnerResponseDto {
* @memberof PartnerResponseDto
*/
'isAdmin': boolean;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'lastName': string;
/**
*
* @type {boolean}
* @memberof PartnerResponseDto
*/
'memoriesEnabled'?: boolean;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'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}
@ -4398,18 +4368,18 @@ export interface UpdateUserDto {
* @memberof UpdateUserDto
*/
'isAdmin'?: boolean;
/**
*
* @type {string}
* @memberof UpdateUserDto
*/
'lastName'?: string;
/**
*
* @type {boolean}
* @memberof UpdateUserDto
*/
'memoriesEnabled'?: boolean;
/**
*
* @type {string}
* @memberof UpdateUserDto
*/
'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}
@ -4557,18 +4509,18 @@ export interface UserResponseDto {
* @memberof UserResponseDto
*/
'isAdmin': boolean;
/**
*
* @type {string}
* @memberof UserResponseDto
*/
'lastName': string;
/**
*
* @type {boolean}
* @memberof UserResponseDto
*/
'memoriesEnabled'?: boolean;
/**
*
* @type {string}
* @memberof UserResponseDto
*/
'name': string;
/**
*
* @type {string}

View file

@ -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',

View file

@ -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",

View file

@ -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

View file

@ -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,
),

View file

@ -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,
),

View file

@ -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;

View file

@ -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,

View file

@ -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(

View file

@ -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,

View file

@ -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(

View file

@ -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),
);
},

View file

@ -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(' ');

View file

@ -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 ^

Binary file not shown.

View file

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

View file

@ -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()),
);
}

View file

@ -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,

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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 {

View file

@ -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",

View file

@ -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",

View file

@ -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);
};

View file

@ -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 {

View file

@ -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();

View file

@ -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,

View file

@ -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,

View file

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

View file

@ -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' })

View file

@ -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,
},
],

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

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

View file

@ -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,
};
};

View file

@ -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(),
});

View file

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

View file

@ -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", '')`);
}
}

View file

@ -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')

View file

@ -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' });

View file

@ -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',
}),
]);

View file

@ -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> => {

View file

@ -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 },
},
];

View file

@ -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 () => {

View file

@ -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)`, () => {

View file

@ -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,
},
],

View file

@ -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)`, () => {

View file

@ -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)

View file

@ -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);
});

View file

@ -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,

View file

@ -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: '',

View file

@ -1341,24 +1341,18 @@ export interface CreateUserDto {
* @memberof CreateUserDto
*/
'externalPath'?: string | null;
/**
*
* @type {string}
* @memberof CreateUserDto
*/
'firstName': string;
/**
*
* @type {string}
* @memberof CreateUserDto
*/
'lastName': string;
/**
*
* @type {boolean}
* @memberof CreateUserDto
*/
'memoriesEnabled'?: boolean;
/**
*
* @type {string}
* @memberof CreateUserDto
*/
'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}
@ -2415,18 +2397,18 @@ export interface PartnerResponseDto {
* @memberof PartnerResponseDto
*/
'isAdmin': boolean;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'lastName': string;
/**
*
* @type {boolean}
* @memberof PartnerResponseDto
*/
'memoriesEnabled'?: boolean;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'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}
@ -4398,18 +4368,18 @@ export interface UpdateUserDto {
* @memberof UpdateUserDto
*/
'isAdmin'?: boolean;
/**
*
* @type {string}
* @memberof UpdateUserDto
*/
'lastName'?: string;
/**
*
* @type {boolean}
* @memberof UpdateUserDto
*/
'memoriesEnabled'?: boolean;
/**
*
* @type {string}
* @memberof UpdateUserDto
*/
'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}
@ -4557,18 +4509,18 @@ export interface UserResponseDto {
* @memberof UserResponseDto
*/
'isAdmin': boolean;
/**
*
* @type {string}
* @memberof UserResponseDto
*/
'lastName': string;
/**
*
* @type {boolean}
* @memberof UserResponseDto
*/
'memoriesEnabled'?: boolean;
/**
*
* @type {string}
* @memberof UserResponseDto
*/
'name': string;
/**
*
* @type {string}

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

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

View file

@ -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>

View file

@ -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)}

View file

@ -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}

View file

@ -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]">

View file

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

View file

@ -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}

View file

@ -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}

View file

@ -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>

View file

@ -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>

View file

@ -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}

View file

@ -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>

View file

@ -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}

View file

@ -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()}
/>

View file

@ -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}

View file

@ -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>

View file

@ -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}

View file

@ -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)}

View file

@ -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

View file

@ -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: '',