diff --git a/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts b/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts index a9503ed1b..fcad5a6c2 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts @@ -30,7 +30,7 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit { ngOnInit () { this.buildForm({ - 'display-name': this.userValidatorsService.USER_DISPLAY_NAME, + 'display-name': this.userValidatorsService.USER_DISPLAY_NAME_REQUIRED, description: this.userValidatorsService.USER_DESCRIPTION }) diff --git a/client/src/app/+signup/+register/register-step-channel.component.html b/client/src/app/+signup/+register/register-step-channel.component.html index 68ea4473a..253374f87 100644 --- a/client/src/app/+signup/+register/register-step-channel.component.html +++ b/client/src/app/+signup/+register/register-step-channel.component.html @@ -11,28 +11,6 @@

-
- - -
- -
- @{{ instanceHost }} -
-
- -
- {{ formErrors.name }} -
- -
- Channel name cannot be the same than your account name. You can click on the first step to update your account name. -
-
-
@@ -47,4 +25,30 @@ {{ formErrors.displayName }}
+ +
+ + +
+ +
+ @{{ instanceHost }} +
+
+ +
+ The channel name is a unique identifier of your channel on this instance. It's like an address mail, so other people can find your channel. +
+ +
+ {{ formErrors.name }} +
+ +
+ Channel name cannot be the same than your account name. You can click on the first step to update your account name. +
+
diff --git a/client/src/app/+signup/+register/register-step-channel.component.ts b/client/src/app/+signup/+register/register-step-channel.component.ts index 9e13f75b3..e434b91a7 100644 --- a/client/src/app/+signup/+register/register-step-channel.component.ts +++ b/client/src/app/+signup/+register/register-step-channel.component.ts @@ -1,8 +1,10 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import { AuthService } from '@app/core' -import { FormReactive, VideoChannelValidatorsService } from '@app/shared' +import { FormReactive, UserService, VideoChannelValidatorsService } from '@app/shared' import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' import { FormGroup } from '@angular/forms' +import { pairwise } from 'rxjs/operators' +import { concat, of } from 'rxjs' @Component({ selector: 'my-register-step-channel', @@ -16,6 +18,7 @@ export class RegisterStepChannelComponent extends FormReactive implements OnInit constructor ( protected formValidatorService: FormValidatorService, private authService: AuthService, + private userService: UserService, private videoChannelValidatorsService: VideoChannelValidatorsService ) { super() @@ -25,16 +28,29 @@ export class RegisterStepChannelComponent extends FormReactive implements OnInit return window.location.host } + ngOnInit () { + this.buildForm({ + displayName: this.videoChannelValidatorsService.VIDEO_CHANNEL_DISPLAY_NAME, + name: this.videoChannelValidatorsService.VIDEO_CHANNEL_NAME + }) + + setTimeout(() => this.formBuilt.emit(this.form)) + + concat( + of(''), + this.form.get('displayName').valueChanges + ).pipe(pairwise()) + .subscribe(([ oldValue, newValue ]) => this.onDisplayNameChange(oldValue, newValue)) + } + isSameThanUsername () { return this.username && this.username === this.form.value['name'] } - ngOnInit () { - this.buildForm({ - name: this.videoChannelValidatorsService.VIDEO_CHANNEL_NAME, - displayName: this.videoChannelValidatorsService.VIDEO_CHANNEL_DISPLAY_NAME - }) + private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) { + const name = this.form.value['name'] || '' - setTimeout(() => this.formBuilt.emit(this.form)) + const newName = this.userService.getNewUsername(oldDisplayName, newDisplayName, name) + this.form.patchValue({ name: newName }) } } diff --git a/client/src/app/+signup/+register/register-step-user.component.html b/client/src/app/+signup/+register/register-step-user.component.html index cd0c78bfa..47b3be8cc 100644 --- a/client/src/app/+signup/+register/register-step-user.component.html +++ b/client/src/app/+signup/+register/register-step-user.component.html @@ -1,5 +1,20 @@
+
+ + +
+ +
+ +
+ {{ formErrors.displayName }} +
+
+
@@ -13,6 +28,10 @@
+
+ The username is a unique identifier of your account on this instance. It's like an address mail, so other people can find you. +
+
{{ formErrors.username }}
diff --git a/client/src/app/+signup/+register/register-step-user.component.ts b/client/src/app/+signup/+register/register-step-user.component.ts index 3825ae371..3b71fd3c4 100644 --- a/client/src/app/+signup/+register/register-step-user.component.ts +++ b/client/src/app/+signup/+register/register-step-user.component.ts @@ -1,8 +1,10 @@ import { Component, EventEmitter, OnInit, Output } from '@angular/core' import { AuthService } from '@app/core' -import { FormReactive, UserValidatorsService } from '@app/shared' +import { FormReactive, UserService, UserValidatorsService } from '@app/shared' import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' import { FormGroup } from '@angular/forms' +import { pairwise } from 'rxjs/operators' +import { concat, of } from 'rxjs' @Component({ selector: 'my-register-step-user', @@ -15,6 +17,7 @@ export class RegisterStepUserComponent extends FormReactive implements OnInit { constructor ( protected formValidatorService: FormValidatorService, private authService: AuthService, + private userService: UserService, private userValidatorsService: UserValidatorsService ) { super() @@ -26,6 +29,7 @@ export class RegisterStepUserComponent extends FormReactive implements OnInit { ngOnInit () { this.buildForm({ + displayName: this.userValidatorsService.USER_DISPLAY_NAME_REQUIRED, username: this.userValidatorsService.USER_USERNAME, password: this.userValidatorsService.USER_PASSWORD, email: this.userValidatorsService.USER_EMAIL, @@ -33,5 +37,18 @@ export class RegisterStepUserComponent extends FormReactive implements OnInit { }) setTimeout(() => this.formBuilt.emit(this.form)) + + concat( + of(''), + this.form.get('displayName').valueChanges + ).pipe(pairwise()) + .subscribe(([ oldValue, newValue ]) => this.onDisplayNameChange(oldValue, newValue)) + } + + private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) { + const username = this.form.value['username'] || '' + + const newUsername = this.userService.getNewUsername(oldDisplayName, newDisplayName, username) + this.form.patchValue({ username: newUsername }) } } diff --git a/client/src/app/+signup/+register/register.component.html b/client/src/app/+signup/+register/register.component.html index 24def68c1..d7e47c1a8 100644 --- a/client/src/app/+signup/+register/register.component.html +++ b/client/src/app/+signup/+register/register.component.html @@ -27,6 +27,12 @@ +
+ + +
PeerTube is creating your account...
+
+
{{ error }}
diff --git a/client/src/app/+signup/+register/register.component.scss b/client/src/app/+signup/+register/register.component.scss index 6f61b78f7..8d14992e7 100644 --- a/client/src/app/+signup/+register/register.component.scss +++ b/client/src/app/+signup/+register/register.component.scss @@ -56,3 +56,26 @@ button { @include peertube-button; @include orange-button; } + +.name-information { + margin-top: 10px; +} + +.done-loader { + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + + my-loader { + margin-bottom: 20px; + + /deep/ .loader div { + border-color: var(--mainColor) transparent transparent transparent; + } + + & + div { + font-size: 15px; + } + } +} diff --git a/client/src/app/shared/forms/form-validators/user-validators.service.ts b/client/src/app/shared/forms/form-validators/user-validators.service.ts index 6589b2580..2dafb1816 100644 --- a/client/src/app/shared/forms/form-validators/user-validators.service.ts +++ b/client/src/app/shared/forms/form-validators/user-validators.service.ts @@ -12,7 +12,7 @@ export class UserValidatorsService { readonly USER_VIDEO_QUOTA: BuildFormValidator readonly USER_VIDEO_QUOTA_DAILY: BuildFormValidator readonly USER_ROLE: BuildFormValidator - readonly USER_DISPLAY_NAME: BuildFormValidator + readonly USER_DISPLAY_NAME_REQUIRED: BuildFormValidator readonly USER_DESCRIPTION: BuildFormValidator readonly USER_TERMS: BuildFormValidator @@ -85,18 +85,7 @@ export class UserValidatorsService { } } - this.USER_DISPLAY_NAME = { - VALIDATORS: [ - Validators.required, - Validators.minLength(1), - Validators.maxLength(50) - ], - MESSAGES: { - 'required': this.i18n('Display name is required.'), - 'minlength': this.i18n('Display name must be at least 1 character long.'), - 'maxlength': this.i18n('Display name cannot be more than 50 characters long.') - } - } + this.USER_DISPLAY_NAME_REQUIRED = this.getDisplayName(true) this.USER_DESCRIPTION = { VALIDATORS: [ @@ -129,4 +118,22 @@ export class UserValidatorsService { } } } + + private getDisplayName (required: boolean) { + const control = { + VALIDATORS: [ + Validators.minLength(1), + Validators.maxLength(120) + ], + MESSAGES: { + 'required': this.i18n('Display name is required.'), + 'minlength': this.i18n('Display name must be at least 1 character long.'), + 'maxlength': this.i18n('Display name cannot be more than 50 characters long.') + } + } + + if (required) control.VALIDATORS.push(Validators.required) + + return control + } } diff --git a/client/src/app/shared/misc/loader.component.html b/client/src/app/shared/misc/loader.component.html index b8b7ad343..ca8ed063e 100644 --- a/client/src/app/shared/misc/loader.component.html +++ b/client/src/app/shared/misc/loader.component.html @@ -1,5 +1,5 @@
-
+
diff --git a/client/src/app/shared/misc/loader.component.scss b/client/src/app/shared/misc/loader.component.scss index ddb64f07a..ffac9c707 100644 --- a/client/src/app/shared/misc/loader.component.scss +++ b/client/src/app/shared/misc/loader.component.scss @@ -3,14 +3,14 @@ // Thanks to https://loading.io/css/ (CC0 License) -.lds-ring { +.loader { display: inline-block; position: relative; width: 50px; height: 50px; } -.lds-ring div { +.loader div { box-sizing: border-box; display: block; position: absolute; @@ -19,23 +19,23 @@ margin: 6px; border: 4px solid; border-radius: 50%; - animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; + animation: loader 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; border-color: #999999 transparent transparent transparent; } -.lds-ring div:nth-child(1) { +.loader div:nth-child(1) { animation-delay: -0.45s; } -.lds-ring div:nth-child(2) { +.loader div:nth-child(2) { animation-delay: -0.3s; } -.lds-ring div:nth-child(3) { +.loader div:nth-child(3) { animation-delay: -0.15s; } -@keyframes lds-ring { +@keyframes loader { 0% { transform: rotate(0deg); } diff --git a/client/src/app/shared/users/user.service.ts b/client/src/app/shared/users/user.service.ts index 20883456f..70ff9a058 100644 --- a/client/src/app/shared/users/user.service.ts +++ b/client/src/app/shared/users/user.service.ts @@ -136,6 +136,22 @@ export class UserService { .pipe(catchError(res => this.restExtractor.handleError(res))) } + getNewUsername (oldDisplayName: string, newDisplayName: string, currentUsername: string) { + // Don't update display name, the user seems to have changed it + if (this.displayNameToUsername(oldDisplayName) !== currentUsername) return currentUsername + + return this.displayNameToUsername(newDisplayName) + } + + displayNameToUsername (displayName: string) { + if (!displayName) return '' + + return displayName + .toLowerCase() + .replace(/\s/g, '_') + .replace(/[^a-z0-9_.]/g, '') + } + /* ###### Admin methods ###### */ addUser (userCreate: UserCreate) { diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts index ed9cb5840..e47624dd6 100644 --- a/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts +++ b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts @@ -100,7 +100,6 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca previewUrl: null })) - this.hydrateFormFromVideo() }, diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index 48a6c63b8..99f51a648 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts @@ -184,7 +184,7 @@ async function createUser (req: express.Request, res: express.Response) { adminFlags: body.adminFlags || UserAdminFlag.NONE }) - const { user, account } = await createUserAccountAndChannelAndPlaylist(userToCreate) + const { user, account } = await createUserAccountAndChannelAndPlaylist({ userToCreate: userToCreate }) auditLogger.create(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON())) logger.info('User %s with its channel and account created.', body.username) @@ -214,7 +214,11 @@ async function registerUser (req: express.Request, res: express.Response) { emailVerified: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION ? false : null }) - const { user } = await createUserAccountAndChannelAndPlaylist(userToCreate, body.channel) + const { user } = await createUserAccountAndChannelAndPlaylist({ + userToCreate: userToCreate, + userDisplayName: body.displayName || undefined, + channelNames: body.channel + }) auditLogger.create(body.username, new UserAuditView(user.toFormattedJSON())) logger.info('User %s with its channel and account registered.', body.username) diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts index e14554ede..cb58454cb 100644 --- a/server/initializers/installer.ts +++ b/server/initializers/installer.ts @@ -146,7 +146,7 @@ async function createOAuthAdminIfNotExist () { } const user = new UserModel(userData) - await createUserAccountAndChannelAndPlaylist(user, undefined, validatePassword) + await createUserAccountAndChannelAndPlaylist({ userToCreate: user, channelNames: undefined, validateUser: validatePassword }) logger.info('Username: ' + username) logger.info('User password: ' + password) } diff --git a/server/initializers/migrations/0100-activitypub.ts b/server/initializers/migrations/0100-activitypub.ts index 2880a97d9..96d44a7ce 100644 --- a/server/initializers/migrations/0100-activitypub.ts +++ b/server/initializers/migrations/0100-activitypub.ts @@ -65,7 +65,12 @@ async function up (utils: { // Create application account { const applicationInstance = await ApplicationModel.findOne() - const accountCreated = await createLocalAccountWithoutKeys(SERVER_ACTOR_NAME, null, applicationInstance.id, undefined) + const accountCreated = await createLocalAccountWithoutKeys({ + name: SERVER_ACTOR_NAME, + userId: null, + applicationId: applicationInstance.id, + t: undefined + }) const { publicKey, privateKey } = await createPrivateAndPublicKeys() accountCreated.Actor.publicKey = publicKey @@ -83,7 +88,7 @@ async function up (utils: { // Recreate accounts for each user const users = await db.User.findAll() for (const user of users) { - const account = await createLocalAccountWithoutKeys(user.username, user.id, null, undefined) + const account = await createLocalAccountWithoutKeys({ name: user.username, userId: user.id, applicationId: null, t: undefined }) const { publicKey, privateKey } = await createPrivateAndPublicKeys() account.Actor.publicKey = publicKey diff --git a/server/lib/user.ts b/server/lib/user.ts index d9fd89e15..b50b09d72 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts @@ -1,4 +1,3 @@ -import * as Sequelize from 'sequelize' import * as uuidv4 from 'uuid/v4' import { ActivityPubActorType } from '../../shared/models/activitypub' import { SERVER_ACTOR_NAME } from '../initializers/constants' @@ -12,9 +11,17 @@ import { UserNotificationSettingModel } from '../models/account/user-notificatio import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' import { createWatchLaterPlaylist } from './video-playlist' import { sequelizeTypescript } from '../initializers/database' +import { Transaction } from 'sequelize/types' type ChannelNames = { name: string, displayName: string } -async function createUserAccountAndChannelAndPlaylist (userToCreate: UserModel, channelNames?: ChannelNames, validateUser = true) { +async function createUserAccountAndChannelAndPlaylist (parameters: { + userToCreate: UserModel, + userDisplayName?: string, + channelNames?: ChannelNames, + validateUser?: boolean +}) { + const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters + const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { const userOptions = { transaction: t, @@ -24,7 +31,13 @@ async function createUserAccountAndChannelAndPlaylist (userToCreate: UserModel, const userCreated = await userToCreate.save(userOptions) userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t) - const accountCreated = await createLocalAccountWithoutKeys(userCreated.username, userCreated.id, null, t) + const accountCreated = await createLocalAccountWithoutKeys({ + name: userCreated.username, + displayName: userDisplayName, + userId: userCreated.id, + applicationId: null, + t: t + }) userCreated.Account = accountCreated const channelAttributes = await buildChannelAttributes(userCreated, channelNames) @@ -46,20 +59,22 @@ async function createUserAccountAndChannelAndPlaylist (userToCreate: UserModel, return { user, account, videoChannel } as { user: UserModel, account: AccountModel, videoChannel: VideoChannelModel } } -async function createLocalAccountWithoutKeys ( +async function createLocalAccountWithoutKeys (parameters: { name: string, + displayName?: string, userId: number | null, applicationId: number | null, - t: Sequelize.Transaction | undefined, - type: ActivityPubActorType= 'Person' -) { + t: Transaction | undefined, + type?: ActivityPubActorType +}) { + const { name, displayName, userId, applicationId, t, type = 'Person' } = parameters const url = getAccountActivityPubUrl(name) const actorInstance = buildActorInstance(type, url, name) const actorInstanceCreated = await actorInstance.save({ transaction: t }) const accountInstance = new AccountModel({ - name, + name: displayName || name, userId, applicationId, actorId: actorInstanceCreated.id @@ -72,7 +87,13 @@ async function createLocalAccountWithoutKeys ( } async function createApplicationActor (applicationId: number) { - const accountCreated = await createLocalAccountWithoutKeys(SERVER_ACTOR_NAME, null, applicationId, undefined, 'Application') + const accountCreated = await createLocalAccountWithoutKeys({ + name: SERVER_ACTOR_NAME, + userId: null, + applicationId: applicationId, + t: undefined, + type: 'Application' + }) accountCreated.Actor = await setAsyncActorKeys(accountCreated.Actor) @@ -89,7 +110,7 @@ export { // --------------------------------------------------------------------------- -function createDefaultUserNotificationSettings (user: UserModel, t: Sequelize.Transaction | undefined) { +function createDefaultUserNotificationSettings (user: UserModel, t: Transaction | undefined) { const values: UserNotificationSetting & { userId: number } = { userId: user.id, newVideoFromSubscription: UserNotificationSettingValue.WEB, diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index 7a081af33..b4e09c9b7 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts @@ -53,8 +53,16 @@ const usersRegisterValidator = [ body('username').custom(isUserUsernameValid).withMessage('Should have a valid username'), body('password').custom(isUserPasswordValid).withMessage('Should have a valid password'), body('email').isEmail().withMessage('Should have a valid email'), - body('channel.name').optional().custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), - body('channel.displayName').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), + body('displayName') + .optional() + .custom(isUserDisplayNameValid).withMessage('Should have a valid display name'), + + body('channel.name') + .optional() + .custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), + body('channel.displayName') + .optional() + .custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking usersRegister parameters', { parameters: omit(req.body, 'password') }) diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts index 95097817b..3268f8c90 100644 --- a/server/tests/api/check-params/users.ts +++ b/server/tests/api/check-params/users.ts @@ -643,6 +643,7 @@ describe('Test users API validators', function () { const registrationPath = path + '/register' const baseCorrectParams = { username: 'user3', + displayName: 'super user', email: 'test3@example.com', password: 'my super password' } @@ -725,6 +726,12 @@ describe('Test users API validators', function () { }) }) + it('Should fail with a bad display name', async function () { + const fields = immutableAssign(baseCorrectParams, { displayName: 'a'.repeat(150) }) + + await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) + }) + it('Should fail with a bad channel name', async function () { const fields = immutableAssign(baseCorrectParams, { channel: { name: '[]azf', displayName: 'toto' } }) diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts index 9d2ef786f..b1f214fe2 100644 --- a/server/tests/api/users/users.ts +++ b/server/tests/api/users/users.ts @@ -17,11 +17,12 @@ import { getUserInformation, getUsersList, getUsersListPaginationAndSort, + getVideoChannel, getVideosList, login, makePutBodyRequest, rateVideo, - registerUser, + registerUserWithChannel, removeUser, removeVideo, ServerInfo, @@ -31,8 +32,7 @@ import { updateMyUser, updateUser, uploadVideo, - userLogin, - registerUserWithChannel, getVideoChannel + userLogin } from '../../../../shared/extra-utils' import { follow } from '../../../../shared/extra-utils/server/follows' import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login' @@ -618,7 +618,7 @@ describe('Test users', function () { describe('Registering a new user', function () { it('Should register a new user', async function () { - const user = { username: 'user_15', password: 'my super password' } + const user = { displayName: 'super user 15', username: 'user_15', password: 'my super password' } const channel = { name: 'my_user_15_channel', displayName: 'my channel rocks' } await registerUserWithChannel({ url: server.url, user, channel }) @@ -633,6 +633,13 @@ describe('Test users', function () { accessToken = await userLogin(server, user15) }) + it('Should have the correct display name', async function () { + const res = await getMyUserInformation(server.url, accessToken) + const user: User = res.body + + expect(user.account.displayName).to.equal('super user 15') + }) + it('Should have the correct video quota', async function () { const res = await getMyUserInformation(server.url, accessToken) const user = res.body diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts index 8690327c4..f82c8cbce 100644 --- a/server/tests/api/videos/video-playlists.ts +++ b/server/tests/api/videos/video-playlists.ts @@ -754,7 +754,6 @@ describe('Test video playlists', function () { } }) - it('Should be able to create a public playlist, and set it to private', async function () { this.timeout(30000) diff --git a/shared/extra-utils/users/users.ts b/shared/extra-utils/users/users.ts index 0f2f0ae15..c09211b71 100644 --- a/shared/extra-utils/users/users.ts +++ b/shared/extra-utils/users/users.ts @@ -73,7 +73,7 @@ function registerUser (url: string, username: string, password: string, specialS function registerUserWithChannel (options: { url: string, - user: { username: string, password: string }, + user: { username: string, password: string, displayName?: string }, channel: { name: string, displayName: string } }) { const path = '/api/v1/users/register' @@ -84,6 +84,10 @@ function registerUserWithChannel (options: { channel: options.channel } + if (options.user.displayName) { + Object.assign(body, { displayName: options.user.displayName }) + } + return makePostBodyRequest({ url: options.url, path, diff --git a/shared/models/users/user-register.model.ts b/shared/models/users/user-register.model.ts index ce5c9c3d2..cf9a43a67 100644 --- a/shared/models/users/user-register.model.ts +++ b/shared/models/users/user-register.model.ts @@ -3,6 +3,8 @@ export interface UserRegister { password: string email: string + displayName?: string + channel?: { name: string displayName: string diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index 5a4f6fcb2..332c0050c 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml @@ -2290,6 +2290,19 @@ components: email: type: string description: 'The email of the user ' + displayName: + type: string + description: 'The user display name' + channel: + type: object + properties: + name: + type: string + description: 'The default channel name' + displayName: + type: string + description: 'The default channel display name' + required: - username - password