mirror of
https://github.com/logto-io/logto.git
synced 2025-02-10 21:58:23 -05:00
refactor(console): merge useMeCustomData
hook into useCurrentUser
(#5196)
This commit is contained in:
parent
0d1a0a9746
commit
7a68967267
8 changed files with 64 additions and 70 deletions
|
@ -22,7 +22,6 @@ import CloudAppRoutes from '@/cloud/AppRoutes';
|
||||||
import AppLoading from '@/components/AppLoading';
|
import AppLoading from '@/components/AppLoading';
|
||||||
import { isCloud } from '@/consts/env';
|
import { isCloud } from '@/consts/env';
|
||||||
import { cloudApi, getManagementApi, meApi } from '@/consts/resources';
|
import { cloudApi, getManagementApi, meApi } from '@/consts/resources';
|
||||||
import useMeCustomData from '@/hooks/use-me-custom-data';
|
|
||||||
import useTrackUserId from '@/hooks/use-track-user-id';
|
import useTrackUserId from '@/hooks/use-track-user-id';
|
||||||
import { OnboardingRoutes } from '@/onboarding';
|
import { OnboardingRoutes } from '@/onboarding';
|
||||||
import useUserOnboardingData from '@/onboarding/hooks/use-user-onboarding-data';
|
import useUserOnboardingData from '@/onboarding/hooks/use-user-onboarding-data';
|
||||||
|
@ -35,6 +34,7 @@ import AppConfirmModalProvider from './contexts/AppConfirmModalProvider';
|
||||||
import AppDataProvider, { AppDataContext } from './contexts/AppDataProvider';
|
import AppDataProvider, { AppDataContext } from './contexts/AppDataProvider';
|
||||||
import { AppThemeProvider } from './contexts/AppThemeProvider';
|
import { AppThemeProvider } from './contexts/AppThemeProvider';
|
||||||
import TenantsProvider, { TenantsContext } from './contexts/TenantsProvider';
|
import TenantsProvider, { TenantsContext } from './contexts/TenantsProvider';
|
||||||
|
import useCurrentUser from './hooks/use-current-user';
|
||||||
import initI18n from './i18n/init';
|
import initI18n from './i18n/init';
|
||||||
|
|
||||||
void initI18n();
|
void initI18n();
|
||||||
|
@ -141,7 +141,7 @@ function Providers() {
|
||||||
/** Renders different routes based on the user's onboarding status. */
|
/** Renders different routes based on the user's onboarding status. */
|
||||||
function AppRoutes() {
|
function AppRoutes() {
|
||||||
const { tenantEndpoint } = useContext(AppDataContext);
|
const { tenantEndpoint } = useContext(AppDataContext);
|
||||||
const { isLoaded } = useMeCustomData();
|
const { isLoaded } = useCurrentUser();
|
||||||
const { isOnboarding } = useUserOnboardingData();
|
const { isOnboarding } = useUserOnboardingData();
|
||||||
const { isAuthenticated } = useLogto();
|
const { isAuthenticated } = useLogto();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import AppLoading from '@/components/AppLoading';
|
import AppLoading from '@/components/AppLoading';
|
||||||
import useMeCustomData from '@/hooks/use-me-custom-data';
|
import useCurrentUser from '@/hooks/use-current-user';
|
||||||
import useUserDefaultTenantId from '@/hooks/use-user-default-tenant-id';
|
import useUserDefaultTenantId from '@/hooks/use-user-default-tenant-id';
|
||||||
import useUserOnboardingData from '@/onboarding/hooks/use-user-onboarding-data';
|
import useUserOnboardingData from '@/onboarding/hooks/use-user-onboarding-data';
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import Redirect from './Redirect';
|
||||||
import TenantLandingPage from './TenantLandingPage';
|
import TenantLandingPage from './TenantLandingPage';
|
||||||
|
|
||||||
export default function Main() {
|
export default function Main() {
|
||||||
const { isLoaded } = useMeCustomData();
|
const { isLoaded } = useCurrentUser();
|
||||||
const { isOnboarding } = useUserOnboardingData();
|
const { isOnboarding } = useUserOnboardingData();
|
||||||
const { defaultTenantId } = useUserDefaultTenantId();
|
const { defaultTenantId } = useUserDefaultTenantId();
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import DynamicT from '@/ds-components/DynamicT';
|
||||||
import FormField from '@/ds-components/FormField';
|
import FormField from '@/ds-components/FormField';
|
||||||
import ModalLayout from '@/ds-components/ModalLayout';
|
import ModalLayout from '@/ds-components/ModalLayout';
|
||||||
import TextInput from '@/ds-components/TextInput';
|
import TextInput from '@/ds-components/TextInput';
|
||||||
import useMeCustomData from '@/hooks/use-me-custom-data';
|
import useCurrentUser from '@/hooks/use-current-user';
|
||||||
import * as modalStyles from '@/scss/modal.module.scss';
|
import * as modalStyles from '@/scss/modal.module.scss';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -32,13 +32,13 @@ export default function RequestForm({
|
||||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const { data, update } = useMeCustomData();
|
const { customData, updateCustomData } = useCurrentUser();
|
||||||
const guideRequests = data?.guideRequests;
|
const guideRequests = customData?.guideRequests;
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
await update({
|
await updateCustomData({
|
||||||
guideRequests: Array.isArray(guideRequests)
|
guideRequests: Array.isArray(guideRequests)
|
||||||
? guideRequests.concat(inputValue)
|
? guideRequests.concat(inputValue)
|
||||||
: [inputValue],
|
: [inputValue],
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import { useLogto } from '@logto/react';
|
import { useLogto } from '@logto/react';
|
||||||
import type { UserProfileResponse } from '@logto/schemas';
|
import type { JsonObject, UserProfileResponse } from '@logto/schemas';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { toast } from 'react-hot-toast';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
import { adminTenantEndpoint, meApi } from '@/consts';
|
import { adminTenantEndpoint, meApi } from '@/consts';
|
||||||
|
@ -10,17 +13,45 @@ import useSwrFetcher from './use-swr-fetcher';
|
||||||
|
|
||||||
const useCurrentUser = () => {
|
const useCurrentUser = () => {
|
||||||
const { isAuthenticated } = useLogto();
|
const { isAuthenticated } = useLogto();
|
||||||
|
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||||
const api = useStaticApi({ prefixUrl: adminTenantEndpoint, resourceIndicator: meApi.indicator });
|
const api = useStaticApi({ prefixUrl: adminTenantEndpoint, resourceIndicator: meApi.indicator });
|
||||||
const fetcher = useSwrFetcher<UserProfileResponse>(api);
|
const fetcher = useSwrFetcher<UserProfileResponse>(api);
|
||||||
const {
|
const {
|
||||||
data: user,
|
data: user,
|
||||||
error,
|
error,
|
||||||
|
isLoading,
|
||||||
mutate,
|
mutate,
|
||||||
} = useSWR<UserProfileResponse, RequestError>(isAuthenticated && 'me', fetcher);
|
} = useSWR<UserProfileResponse, RequestError>(isAuthenticated && 'me', fetcher);
|
||||||
|
|
||||||
const isLoading = !user && !error;
|
const updateCustomData = useCallback(
|
||||||
|
async (customData: JsonObject) => {
|
||||||
|
if (!user) {
|
||||||
|
toast.error(t('errors.unexpected_error'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return { user, isLoading, error, reload: mutate, api };
|
await mutate({
|
||||||
|
...user,
|
||||||
|
customData: await api
|
||||||
|
.patch(`me/custom-data`, {
|
||||||
|
json: customData,
|
||||||
|
})
|
||||||
|
.json<JsonObject>(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[api, mutate, t, user]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
user,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
isLoaded: !isLoading && !error,
|
||||||
|
reload: mutate,
|
||||||
|
customData: user?.customData,
|
||||||
|
/** Patch (shallow merge) the custom data of the current user. */
|
||||||
|
updateCustomData,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useCurrentUser;
|
export default useCurrentUser;
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
import { type JsonObject } from '@logto/schemas';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
import { toast } from 'react-hot-toast';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import useCurrentUser from './use-current-user';
|
|
||||||
|
|
||||||
const useMeCustomData = () => {
|
|
||||||
const { user, isLoading, error, reload, api } = useCurrentUser();
|
|
||||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
|
||||||
|
|
||||||
const update = useCallback(
|
|
||||||
async (customData: JsonObject) => {
|
|
||||||
if (!user) {
|
|
||||||
toast.error(t('errors.unexpected_error'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await reload({
|
|
||||||
...user,
|
|
||||||
customData: await api
|
|
||||||
.patch(`me/custom-data`, {
|
|
||||||
json: customData,
|
|
||||||
})
|
|
||||||
.json<JsonObject>(),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[api, reload, t, user]
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: user?.customData,
|
|
||||||
error,
|
|
||||||
isLoading,
|
|
||||||
isLoaded: !isLoading && !error,
|
|
||||||
/** Patch (shallow merge) the custom data of the current user. */
|
|
||||||
update,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useMeCustomData;
|
|
|
@ -6,7 +6,7 @@ import { z } from 'zod';
|
||||||
import { isCloud } from '@/consts/env';
|
import { isCloud } from '@/consts/env';
|
||||||
import { TenantsContext } from '@/contexts/TenantsProvider';
|
import { TenantsContext } from '@/contexts/TenantsProvider';
|
||||||
|
|
||||||
import useMeCustomData from './use-me-custom-data';
|
import useCurrentUser from './use-current-user';
|
||||||
|
|
||||||
const key = 'defaultTenantId';
|
const key = 'defaultTenantId';
|
||||||
|
|
||||||
|
@ -17,12 +17,12 @@ const key = 'defaultTenantId';
|
||||||
* - If the default tenant ID is not available to the user anymore, it semantically equals to the first tenant ID.
|
* - If the default tenant ID is not available to the user anymore, it semantically equals to the first tenant ID.
|
||||||
*/
|
*/
|
||||||
const useUserDefaultTenantId = () => {
|
const useUserDefaultTenantId = () => {
|
||||||
const { data, update: updateMeCustomData } = useMeCustomData();
|
const { customData, updateCustomData } = useCurrentUser();
|
||||||
const { tenants, currentTenantId } = useContext(TenantsContext);
|
const { tenants } = useContext(TenantsContext);
|
||||||
/** The current stored default tenant ID in the user's `customData`. */
|
/** The current stored default tenant ID in the user's `customData`. */
|
||||||
const storedId = useMemo(
|
const storedId = useMemo(
|
||||||
() => trySafe(() => z.object({ [key]: z.string() }).parse(data)[key]),
|
() => trySafe(() => z.object({ [key]: z.string() }).parse(customData)[key]),
|
||||||
[data]
|
[customData]
|
||||||
);
|
);
|
||||||
|
|
||||||
const defaultTenantId = useMemo(() => {
|
const defaultTenantId = useMemo(() => {
|
||||||
|
@ -47,11 +47,11 @@ const useUserDefaultTenantId = () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateMeCustomData({
|
await updateCustomData({
|
||||||
[key]: tenantId,
|
[key]: tenantId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[updateMeCustomData]
|
[updateCustomData]
|
||||||
);
|
);
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { AppThemeContext, buildDefaultAppearanceMode } from '@/contexts/AppTheme
|
||||||
import type { DynamicAppearanceMode } from '@/types/appearance-mode';
|
import type { DynamicAppearanceMode } from '@/types/appearance-mode';
|
||||||
import { appearanceModeGuard } from '@/types/appearance-mode';
|
import { appearanceModeGuard } from '@/types/appearance-mode';
|
||||||
|
|
||||||
import useMeCustomData from './use-me-custom-data';
|
import useCurrentUser from './use-current-user';
|
||||||
|
|
||||||
const adminConsolePreferencesKey = 'adminConsolePreferences';
|
const adminConsolePreferencesKey = 'adminConsolePreferences';
|
||||||
|
|
||||||
|
@ -33,11 +33,13 @@ const defaultUserPreferences: DefaultUserPreference = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const useUserPreferences = () => {
|
const useUserPreferences = () => {
|
||||||
const { data, error, isLoading, isLoaded, update: updateMeCustomData } = useMeCustomData();
|
const { customData, error, isLoading, isLoaded, updateCustomData } = useCurrentUser();
|
||||||
const { setAppearanceMode } = useContext(AppThemeContext);
|
const { setAppearanceMode } = useContext(AppThemeContext);
|
||||||
|
|
||||||
const userPreferences = useMemo(() => {
|
const userPreferences = useMemo(() => {
|
||||||
const parsed = z.object({ [adminConsolePreferencesKey]: userPreferencesGuard }).safeParse(data);
|
const parsed = z
|
||||||
|
.object({ [adminConsolePreferencesKey]: userPreferencesGuard })
|
||||||
|
.safeParse(customData);
|
||||||
|
|
||||||
return parsed.success
|
return parsed.success
|
||||||
? {
|
? {
|
||||||
|
@ -45,10 +47,10 @@ const useUserPreferences = () => {
|
||||||
...parsed.data[adminConsolePreferencesKey],
|
...parsed.data[adminConsolePreferencesKey],
|
||||||
}
|
}
|
||||||
: defaultUserPreferences;
|
: defaultUserPreferences;
|
||||||
}, [data]);
|
}, [customData]);
|
||||||
|
|
||||||
const update = async (data: Partial<UserPreferences>) => {
|
const update = async (data: Partial<UserPreferences>) => {
|
||||||
await updateMeCustomData({
|
await updateCustomData({
|
||||||
[adminConsolePreferencesKey]: {
|
[adminConsolePreferencesKey]: {
|
||||||
...userPreferences,
|
...userPreferences,
|
||||||
...data,
|
...data,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { z } from 'zod';
|
||||||
|
|
||||||
import { isCloud } from '@/consts/env';
|
import { isCloud } from '@/consts/env';
|
||||||
import { TenantsContext } from '@/contexts/TenantsProvider';
|
import { TenantsContext } from '@/contexts/TenantsProvider';
|
||||||
import useMeCustomData from '@/hooks/use-me-custom-data';
|
import useCurrentUser from '@/hooks/use-current-user';
|
||||||
|
|
||||||
import type { UserOnboardingData } from '../types';
|
import type { UserOnboardingData } from '../types';
|
||||||
import { Project, userOnboardingDataGuard } from '../types';
|
import { Project, userOnboardingDataGuard } from '../types';
|
||||||
|
@ -12,14 +12,16 @@ import { Project, userOnboardingDataGuard } from '../types';
|
||||||
const userOnboardingDataKey = 'onboarding';
|
const userOnboardingDataKey = 'onboarding';
|
||||||
|
|
||||||
const useUserOnboardingData = () => {
|
const useUserOnboardingData = () => {
|
||||||
const { data, error, isLoading, isLoaded, update: updateMeCustomData } = useMeCustomData();
|
const { customData, error, isLoading, isLoaded, updateCustomData } = useCurrentUser();
|
||||||
const { currentTenantId } = useContext(TenantsContext);
|
const { currentTenantId } = useContext(TenantsContext);
|
||||||
|
|
||||||
const userOnboardingData = useMemo(() => {
|
const userOnboardingData = useMemo(() => {
|
||||||
const parsed = z.object({ [userOnboardingDataKey]: userOnboardingDataGuard }).safeParse(data);
|
const parsed = z
|
||||||
|
.object({ [userOnboardingDataKey]: userOnboardingDataGuard })
|
||||||
|
.safeParse(customData);
|
||||||
|
|
||||||
return parsed.success ? parsed.data[userOnboardingDataKey] : {};
|
return parsed.success ? parsed.data[userOnboardingDataKey] : {};
|
||||||
}, [data]);
|
}, [customData]);
|
||||||
|
|
||||||
const isOnboarding = useMemo(() => {
|
const isOnboarding = useMemo(() => {
|
||||||
if (!isCloud) {
|
if (!isCloud) {
|
||||||
|
@ -47,14 +49,14 @@ const useUserOnboardingData = () => {
|
||||||
|
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
async (data: Partial<UserOnboardingData>) => {
|
async (data: Partial<UserOnboardingData>) => {
|
||||||
await updateMeCustomData({
|
await updateCustomData({
|
||||||
[userOnboardingDataKey]: {
|
[userOnboardingDataKey]: {
|
||||||
...userOnboardingData,
|
...userOnboardingData,
|
||||||
...data,
|
...data,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[updateMeCustomData, userOnboardingData]
|
[updateCustomData, userOnboardingData]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Add table
Reference in a new issue