0
Fork 0
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:
wangsijie 2023-12-13 12:01:07 +09:00 committed by GitHub
parent dcc226b5d9
commit 7c09ac850f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 45 additions and 34 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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