mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
refactor(core,console,shared): use getUserDisplayName function (#4977)
This commit is contained in:
parent
dcc226b5d9
commit
7c09ac850f
14 changed files with 45 additions and 34 deletions
|
@ -1,6 +1,7 @@
|
|||
import { ServiceConnector } from '@logto/connector-kit';
|
||||
import { emailRegEx, phoneInputRegEx } from '@logto/core-kit';
|
||||
import { ConnectorType } from '@logto/schemas';
|
||||
import { parsePhoneNumber } from '@logto/shared/universal';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useForm, useFormContext } from 'react-hook-form';
|
||||
|
@ -13,7 +14,6 @@ import { Tooltip } from '@/ds-components/Tip';
|
|||
import useApi from '@/hooks/use-api';
|
||||
import { onKeyDownHandler } from '@/utils/a11y';
|
||||
import { trySubmitSafe } from '@/utils/form';
|
||||
import { parsePhoneNumber } from '@/utils/phone';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { User } from '@logto/schemas';
|
||||
import { getUserDisplayName, formatToInternationalPhoneNumber } from '@logto/shared/universal';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -6,7 +7,6 @@ import { useTranslation } from 'react-i18next';
|
|||
import DefaultAvatar from '@/assets/images/default-avatar.svg';
|
||||
import ImageWithErrorFallback from '@/ds-components/ImageWithErrorFallback';
|
||||
import { Tooltip } from '@/ds-components/Tip';
|
||||
import { formatToInternationalPhoneNumber } from '@/utils/phone';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
|
@ -84,7 +84,7 @@ function UserAvatar({ className, size = 'medium', user, hasTooltip = false }: Pr
|
|||
);
|
||||
}
|
||||
|
||||
const nameToDisplay = (name ?? username ?? primaryEmail)?.toLocaleUpperCase();
|
||||
const nameToDisplay = getUserDisplayName({ name, username, primaryEmail })?.toLocaleUpperCase();
|
||||
|
||||
const color = conditional(
|
||||
nameToDisplay &&
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { emailRegEx, usernameRegEx } from '@logto/core-kit';
|
||||
import type { User } from '@logto/schemas';
|
||||
import { parsePhoneNumber } from '@logto/shared/universal';
|
||||
import { trySafe } from '@silverhand/essentials';
|
||||
import { parsePhoneNumberWithError } from 'libphonenumber-js';
|
||||
import { useForm, useController } from 'react-hook-form';
|
||||
|
@ -18,7 +19,6 @@ import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
|||
import useDocumentationUrl from '@/hooks/use-documentation-url';
|
||||
import { trySubmitSafe } from '@/utils/form';
|
||||
import { safeParseJsonObject } from '@/utils/json';
|
||||
import { parsePhoneNumber } from '@/utils/phone';
|
||||
import { uriValidator } from '@/utils/validator';
|
||||
|
||||
import { type UserDetailsForm, type UserDetailsOutletContext } from '../types';
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import type { User } from '@logto/schemas';
|
||||
import { formatToInternationalPhoneNumber } from '@logto/shared/universal';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
|
||||
import { formatToInternationalPhoneNumber } from '@/utils/phone';
|
||||
|
||||
import type { UserDetailsForm } from './types';
|
||||
|
||||
export const userDetailsParser = {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { emailRegEx, phoneInputRegEx, usernameRegEx } from '@logto/core-kit';
|
||||
import type { CreateUser, User } from '@logto/schemas';
|
||||
import { parsePhoneNumber } from '@logto/shared/universal';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
@ -17,7 +18,6 @@ import useTenantPathname from '@/hooks/use-tenant-pathname';
|
|||
import * as modalStyles from '@/scss/modal.module.scss';
|
||||
import { trySubmitSafe } from '@/utils/form';
|
||||
import { generateRandomPassword } from '@/utils/password';
|
||||
import { parsePhoneNumber } from '@/utils/phone';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
import type { User } from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { getUserDisplayName } from '@logto/shared/universal';
|
||||
import { t } from 'i18next';
|
||||
|
||||
import { formatToInternationalPhoneNumber } from './phone';
|
||||
export const getUserTitle = (user?: User): string =>
|
||||
(user ? getUserDisplayName(user) : undefined) ?? t('admin_console.users.unnamed');
|
||||
|
||||
const getSecondaryUserInfo = (user?: User) => {
|
||||
const { primaryEmail, primaryPhone, username } = user ?? {};
|
||||
const formattedPhoneNumber = conditional(
|
||||
primaryPhone && formatToInternationalPhoneNumber(primaryPhone)
|
||||
);
|
||||
return primaryEmail ?? formattedPhoneNumber ?? username;
|
||||
export const getUserSubtitle = (user?: User) => {
|
||||
if (!user?.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { username, primaryEmail, primaryPhone } = user;
|
||||
|
||||
return getUserDisplayName({ username, primaryEmail, primaryPhone });
|
||||
};
|
||||
|
||||
export const getUserTitle = (user?: User) =>
|
||||
user?.name ?? getSecondaryUserInfo(user) ?? t('admin_console.users.unnamed');
|
||||
|
||||
export const getUserSubtitle = (user?: User) =>
|
||||
conditional(user?.name && getSecondaryUserInfo(user));
|
||||
|
|
|
@ -82,7 +82,7 @@ export default function adminUserMfaVerificationsRoutes<T extends AuthedRouter>(
|
|||
const secret = generateTotpSecret();
|
||||
const service = ctx.URL.hostname;
|
||||
const user = getUserDisplayName({ username, primaryEmail, primaryPhone, name });
|
||||
const keyUri = authenticator.keyuri(user, service, secret);
|
||||
const keyUri = authenticator.keyuri(user ?? 'Unnamed User', service, secret);
|
||||
await addUserMfaVerification(id, { type: MfaFactor.TOTP, secret });
|
||||
ctx.body = {
|
||||
type: MfaFactor.TOTP,
|
||||
|
|
|
@ -152,7 +152,7 @@ export default function additionalRoutes<T extends IRouterParamContext>(
|
|||
name = null,
|
||||
} = await parseUserProfile(tenant, profileVerifiedInteraction);
|
||||
const user = getUserDisplayName({ username, primaryEmail, primaryPhone, name });
|
||||
const keyUri = authenticator.keyuri(user, service, secret);
|
||||
const keyUri = authenticator.keyuri(user ?? 'Unnamed User', service, secret);
|
||||
|
||||
ctx.body = {
|
||||
secret,
|
||||
|
@ -166,7 +166,7 @@ export default function additionalRoutes<T extends IRouterParamContext>(
|
|||
const { accountId } = profileVerifiedInteraction;
|
||||
const { username, primaryEmail, primaryPhone, name } = await findUserById(accountId);
|
||||
const user = getUserDisplayName({ username, primaryEmail, primaryPhone, name });
|
||||
const keyUri = authenticator.keyuri(user, service, secret);
|
||||
const keyUri = authenticator.keyuri(user ?? 'Unnamed User', service, secret);
|
||||
|
||||
ctx.body = {
|
||||
secret,
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
type WebAuthnVerificationPayload,
|
||||
type VerifyMfaResult,
|
||||
} from '@logto/schemas';
|
||||
import { getUserDisplayName } from '@logto/shared';
|
||||
import {
|
||||
type GenerateRegistrationOptionsOpts,
|
||||
generateRegistrationOptions,
|
||||
|
@ -31,14 +32,16 @@ export const generateWebAuthnRegistrationOptions = async ({
|
|||
rpId,
|
||||
user,
|
||||
}: GenerateWebAuthnRegistrationOptionsParameters): Promise<WebAuthnRegistrationOptions> => {
|
||||
const { username, primaryEmail, primaryPhone, id, mfaVerifications } = user;
|
||||
|
||||
const options: GenerateRegistrationOptionsOpts = {
|
||||
rpName: rpId,
|
||||
rpID: rpId,
|
||||
userID: user.id,
|
||||
userName: user.username ?? user.primaryEmail ?? user.primaryPhone ?? user.id,
|
||||
userID: id,
|
||||
userName: getUserDisplayName({ username, primaryEmail, primaryPhone }) ?? 'Unnamed User',
|
||||
timeout: 60_000,
|
||||
attestationType: 'none',
|
||||
excludeCredentials: user.mfaVerifications
|
||||
excludeCredentials: mfaVerifications
|
||||
.filter(
|
||||
(verification): verification is MfaVerificationWebAuthn =>
|
||||
verification.type === MfaFactor.WebAuthn
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
"@silverhand/essentials": "^2.8.4",
|
||||
"chalk": "^5.0.0",
|
||||
"find-up": "^6.3.0",
|
||||
"libphonenumber-js": "^1.9.49",
|
||||
"nanoid": "^5.0.1",
|
||||
"slonik": "^30.0.0"
|
||||
}
|
||||
|
|
|
@ -2,3 +2,4 @@ export * from './object.js';
|
|||
export * from './ttl-cache.js';
|
||||
export * from './id.js';
|
||||
export * from './user-display-name.js';
|
||||
export * from './phone.js';
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
import { conditional } from '@silverhand/essentials';
|
||||
|
||||
import { formatToInternationalPhoneNumber } from './phone.js';
|
||||
|
||||
/**
|
||||
* Get user display name from multiple fields
|
||||
*/
|
||||
|
@ -7,10 +11,14 @@ export const getUserDisplayName = ({
|
|||
primaryEmail,
|
||||
primaryPhone,
|
||||
}: {
|
||||
name: string | null;
|
||||
username: string | null;
|
||||
primaryEmail: string | null;
|
||||
primaryPhone: string | null;
|
||||
}): string => {
|
||||
return name ?? username ?? primaryEmail ?? primaryPhone ?? 'Unnamed User';
|
||||
name?: string | null;
|
||||
username?: string | null;
|
||||
primaryEmail?: string | null;
|
||||
primaryPhone?: string | null;
|
||||
}): string | undefined => {
|
||||
const formattedPhoneNumber = conditional(
|
||||
primaryPhone && formatToInternationalPhoneNumber(primaryPhone)
|
||||
);
|
||||
|
||||
return name ?? primaryEmail ?? formattedPhoneNumber ?? username ?? undefined;
|
||||
};
|
||||
|
|
|
@ -3981,6 +3981,9 @@ importers:
|
|||
find-up:
|
||||
specifier: ^6.3.0
|
||||
version: 6.3.0
|
||||
libphonenumber-js:
|
||||
specifier: ^1.9.49
|
||||
version: 1.10.51
|
||||
nanoid:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
|
@ -15604,7 +15607,6 @@ packages:
|
|||
|
||||
/libphonenumber-js@1.10.51:
|
||||
resolution: {integrity: sha512-vY2I+rQwrDQzoPds0JeTEpeWzbUJgqoV0O4v31PauHBb/e+1KCXKylHcDnBMgJZ9fH9mErsEbROJY3Z3JtqEmg==}
|
||||
dev: true
|
||||
|
||||
/lightningcss-darwin-arm64@1.16.1:
|
||||
resolution: {integrity: sha512-/J898YSAiGVqdybHdIF3Ao0Hbh2vyVVj5YNm3NznVzTSvkOi3qQCAtO97sfmNz+bSRHXga7ZPLm+89PpOM5gAg==}
|
||||
|
|
Loading…
Reference in a new issue