From 85545d4cee4ca1b3eadc21c124b7e07f12ad8965 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Fri, 19 Jul 2024 17:46:55 +0800 Subject: [PATCH] feat(elements): update name --- packages/console/src/pages/Profile/index.tsx | 106 +++++++++--------- packages/core/src/middleware/koa-cors.ts | 4 + packages/elements/index.html | 2 +- packages/elements/package.json | 1 + .../src/elements/logto-profile-card.ts | 20 +++- packages/elements/src/index.ts | 6 +- .../src/providers/logto-user-provider.ts | 37 ++++-- packages/elements/src/react.ts | 7 ++ packages/elements/src/utils/api.ts | 22 ++++ pnpm-lock.yaml | 3 + 10 files changed, 141 insertions(+), 67 deletions(-) create mode 100644 packages/elements/src/utils/api.ts diff --git a/packages/console/src/pages/Profile/index.tsx b/packages/console/src/pages/Profile/index.tsx index aa71c15ac..77e5e0676 100644 --- a/packages/console/src/pages/Profile/index.tsx +++ b/packages/console/src/pages/Profile/index.tsx @@ -36,7 +36,7 @@ if (isDevFeaturesEnabled) { initLocalization(); } -const { LogtoProfileCard, LogtoThemeProvider } = createReactComponents(React); +const { LogtoProfileCard, LogtoThemeProvider, LogtoUserProvider } = createReactComponents(React); function Profile() { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); @@ -70,60 +70,62 @@ function Profile() { return ( -
- - -
- -
- -
- {showLoadingSkeleton && } - {isDevFeaturesEnabled && } - {user && !showLoadingSkeleton && ( -
- - {isCloud && ( - - )} - - (value ? ******** : ), - action: { - name: 'profile.change', - handler: () => { - navigate(user.hasPassword ? 'verify-password' : 'change-password', { - state: { email: user.primaryEmail, action: 'changePassword' }, - }); + +
+ + +
+ +
+ +
+ {showLoadingSkeleton && } + {isDevFeaturesEnabled && } + {user && !showLoadingSkeleton && ( +
+ + {isCloud && ( + + )} + + (value ? ******** : ), + action: { + name: 'profile.change', + handler: () => { + navigate(user.hasPassword ? 'verify-password' : 'change-password', { + state: { email: user.primaryEmail, action: 'changePassword' }, + }); + }, }, }, - }, - ]} - /> - - {isCloud && ( - -
-
- {t('profile.delete_account.description')} -
-
- + ]} + />
- )} -
- )} -
-
- {childrenRoutes} -
+ {isCloud && ( + +
+
+ {t('profile.delete_account.description')} +
+
+ +
+ )} +
+ )} +
+
+ {childrenRoutes} +
+
); diff --git a/packages/core/src/middleware/koa-cors.ts b/packages/core/src/middleware/koa-cors.ts index f67968fdc..89c3c3493 100644 --- a/packages/core/src/middleware/koa-cors.ts +++ b/packages/core/src/middleware/koa-cors.ts @@ -11,6 +11,10 @@ export default function koaCors( origin: (ctx) => { const { origin } = ctx.request.headers; + if (!EnvSet.values.isProduction) { + return origin ?? ''; + } + if ( origin && urlSets.some((set) => { diff --git a/packages/elements/index.html b/packages/elements/index.html index ecc395bd1..1f35da6f3 100644 --- a/packages/elements/index.html +++ b/packages/elements/index.html @@ -10,7 +10,7 @@ - + diff --git a/packages/elements/package.json b/packages/elements/package.json index e08c8a5b2..d98b92d19 100644 --- a/packages/elements/package.json +++ b/packages/elements/package.json @@ -51,6 +51,7 @@ "@lit/localize": "^0.12.1", "@lit/react": "^1.0.5", "@silverhand/essentials": "^2.9.1", + "ky": "^1.2.3", "lit": "^3.1.4" }, "devDependencies": { diff --git a/packages/elements/src/elements/logto-profile-card.ts b/packages/elements/src/elements/logto-profile-card.ts index 568897f3d..6644aeb6f 100644 --- a/packages/elements/src/elements/logto-profile-card.ts +++ b/packages/elements/src/elements/logto-profile-card.ts @@ -25,6 +25,9 @@ export class LogtoProfileCard extends LitElement { @state() updateNameOpened = false; + @state() + name = ''; + render() { const user = this.userContext?.user; @@ -74,6 +77,7 @@ export class LogtoProfileCard extends LitElement { size="small" @click=${() => { this.updateNameOpened = true; + this.name = user.name ?? ''; }} > ${msg('Update', { id: 'general.update' })} @@ -100,10 +104,22 @@ export class LogtoProfileCard extends LitElement { id: 'account.profile.personal-info.name-placeholder', desc: 'The placeholder for the name input field.', })} - value="" + .value=${this.name} + @input=${(event: InputEvent) => { + // eslint-disable-next-line no-restricted-syntax + this.name = (event.target as HTMLInputElement).value; + }} /> - + { + await this.userContext?.updateUser({ name: this.name }); + this.updateNameOpened = false; + }} + > ${msg('Save', { id: 'general.save' })} diff --git a/packages/elements/src/index.ts b/packages/elements/src/index.ts index 566659ed9..fdd0920e8 100644 --- a/packages/elements/src/index.ts +++ b/packages/elements/src/index.ts @@ -10,5 +10,9 @@ export * from './components/logto-modal-layout.js'; export * from './components/logto-modal.js'; export * from './components/logto-text-input.js'; export * from './elements/logto-profile-card.js'; -export * from './utils/locale.js'; + export * from './providers/logto-theme-provider.js'; +export * from './providers/logto-user-provider.js'; + +export * from './utils/api.js'; +export * from './utils/locale.js'; diff --git a/packages/elements/src/providers/logto-user-provider.ts b/packages/elements/src/providers/logto-user-provider.ts index 08ad1b1b6..50b193dab 100644 --- a/packages/elements/src/providers/logto-user-provider.ts +++ b/packages/elements/src/providers/logto-user-provider.ts @@ -1,10 +1,16 @@ import { createContext, provide } from '@lit/context'; import { type UserInfo } from '@logto/schemas'; +import { noop } from '@silverhand/essentials'; import { LitElement, type PropertyValues, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; +import { LogtoAccountApi } from '../utils/api.js'; + /** @see {@link UserContext} */ -export type UserContextType = { user?: UserInfo }; +export type UserContextType = Readonly<{ + user?: UserInfo; + updateUser: (user: Partial) => void | Promise; +}>; /** * Context for the current user. It's a fundamental context for the account-related elements. @@ -12,7 +18,9 @@ export type UserContextType = { user?: UserInfo }; export const UserContext = createContext('modal-context'); /** The default value for the user context. */ -export const userContext: UserContextType = {}; +export const userContext: UserContextType = Object.freeze({ + updateUser: noop, +}); const tagName = 'logto-user-provider'; @@ -24,23 +32,30 @@ export class LogtoUserProvider extends LitElement { context = userContext; @property({ type: Object }) - user?: UserInfo; + api!: LogtoAccountApi | ConstructorParameters[0]; render() { return html``; } - protected handlePropertiesChange(changedProperties: PropertyValues) { - if (changedProperties.has('user')) { - this.context.user = this.user; + protected updateContext(context: Partial) { + this.context = Object.freeze({ ...this.context, ...context }); + } + + protected async handlePropertiesChange(changedProperties: PropertyValues) { + if (changedProperties.has('api')) { + const api = this.api instanceof LogtoAccountApi ? this.api : new LogtoAccountApi(this.api); + this.updateContext({ + updateUser: async (user) => { + const updated = await api.updateUser(user); + this.updateContext({ user: updated }); + }, + user: await api.getUser(), + }); } } protected firstUpdated(changedProperties: PropertyValues): void { - this.handlePropertiesChange(changedProperties); - } - - protected updated(changedProperties: PropertyValues): void { - this.handlePropertiesChange(changedProperties); + void this.handlePropertiesChange(changedProperties); } } diff --git a/packages/elements/src/react.ts b/packages/elements/src/react.ts index 28b363ae4..1cf306810 100644 --- a/packages/elements/src/react.ts +++ b/packages/elements/src/react.ts @@ -6,9 +6,11 @@ import { LogtoFormCard, LogtoProfileCard, LogtoList, + LogtoUserProvider, } from './index.js'; export * from './utils/locale.js'; +export * from './utils/api.js'; export const createReactComponents = (react: Parameters[0]['react']) => { return { @@ -37,5 +39,10 @@ export const createReactComponents = (react: Parameters[ elementClass: LogtoThemeProvider, react, }), + LogtoUserProvider: createComponent({ + tagName: LogtoUserProvider.tagName, + elementClass: LogtoUserProvider, + react, + }), }; }; diff --git a/packages/elements/src/utils/api.ts b/packages/elements/src/utils/api.ts new file mode 100644 index 000000000..f91273796 --- /dev/null +++ b/packages/elements/src/utils/api.ts @@ -0,0 +1,22 @@ +import { type UserInfo } from '@logto/schemas'; +import originalKy, { type Options, type KyInstance } from 'ky'; + +/** + * CAUTION: The current implementation is based on the admin tenant's `/me` API which is interim. + * The final implementation should be based on the Account API. + */ +export class LogtoAccountApi { + protected ky: KyInstance; + + constructor(init: KyInstance | Options) { + this.ky = 'create' in init ? init : originalKy.create(init); + } + + async getUser() { + return this.ky('me').json(); + } + + async updateUser(user: Partial) { + return this.ky.patch('me', { json: user }).json(); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ecdde142e..d22e1a5a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3572,6 +3572,9 @@ importers: '@silverhand/essentials': specifier: ^2.9.1 version: 2.9.1 + ky: + specifier: ^1.2.3 + version: 1.2.3 lit: specifier: ^3.1.4 version: 3.1.4