mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-04 19:11:57 +08:00
refactor(core): initial multiple servers infra (#8745)
This is the initial refactoring of affine to support multiple servers, but many more changes are needed to make multi-server actually work.
This commit is contained in:
+3
-3
@@ -1,7 +1,7 @@
|
||||
import { Button, ErrorMessage, Skeleton } from '@affine/component';
|
||||
import { SettingRow } from '@affine/component/setting-components';
|
||||
import {
|
||||
ServerConfigService,
|
||||
ServerService,
|
||||
SubscriptionService,
|
||||
UserCopilotQuotaService,
|
||||
} from '@affine/core/modules/cloud';
|
||||
@@ -22,9 +22,9 @@ export const AIUsagePanel = ({
|
||||
onChangeSettingState?: (settingState: SettingState) => void;
|
||||
}) => {
|
||||
const t = useI18n();
|
||||
const serverConfigService = useService(ServerConfigService);
|
||||
const serverService = useService(ServerService);
|
||||
const hasPaymentFeature = useLiveData(
|
||||
serverConfigService.serverConfig.features$.map(f => f?.payment)
|
||||
serverService.server.features$.map(f => f?.payment)
|
||||
);
|
||||
const subscriptionService = useService(SubscriptionService);
|
||||
const aiSubscription = useLiveData(subscriptionService.subscription.ai$);
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { AuthService, ServerConfigService } from '../../../../modules/cloud';
|
||||
import { AuthService, ServerService } from '../../../../modules/cloud';
|
||||
import type { SettingState } from '../types';
|
||||
import { AIUsagePanel } from './ai-usage-panel';
|
||||
import { StorageProgress } from './storage-progress';
|
||||
@@ -178,13 +178,11 @@ export const AccountSetting = ({
|
||||
}: {
|
||||
onChangeSettingState?: (settingState: SettingState) => void;
|
||||
}) => {
|
||||
const { authService, serverConfigService } = useServices({
|
||||
const { authService, serverService } = useServices({
|
||||
AuthService,
|
||||
ServerConfigService,
|
||||
ServerService,
|
||||
});
|
||||
const serverFeatures = useLiveData(
|
||||
serverConfigService.serverConfig.features$
|
||||
);
|
||||
const serverFeatures = useLiveData(serverService.server.features$);
|
||||
const t = useI18n();
|
||||
const session = authService.session;
|
||||
useEffect(() => {
|
||||
|
||||
+3
-3
@@ -5,7 +5,7 @@ import { cssVar } from '@toeverything/theme';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
|
||||
import {
|
||||
ServerConfigService,
|
||||
ServerService,
|
||||
SubscriptionService,
|
||||
UserQuotaService,
|
||||
} from '../../../../modules/cloud';
|
||||
@@ -34,9 +34,9 @@ export const StorageProgress = ({ onUpgrade }: StorageProgressProgress) => {
|
||||
const maxFormatted = useLiveData(quota.maxFormatted$);
|
||||
const percent = useLiveData(quota.percent$);
|
||||
|
||||
const serverConfigService = useService(ServerConfigService);
|
||||
const serverService = useService(ServerService);
|
||||
const hasPaymentFeature = useLiveData(
|
||||
serverConfigService.serverConfig.features$.map(f => f?.payment)
|
||||
serverService.server.features$.map(f => f?.payment)
|
||||
);
|
||||
const subscription = useService(SubscriptionService).subscription;
|
||||
useEffect(() => {
|
||||
|
||||
+4
-6
@@ -16,7 +16,7 @@ import {
|
||||
SettingWrapper,
|
||||
} from '@affine/component/setting-components';
|
||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||
import { ServerConfigService } from '@affine/core/modules/cloud';
|
||||
import { ServerService } from '@affine/core/modules/cloud';
|
||||
import { DesktopApiService } from '@affine/core/modules/desktop-api';
|
||||
import {
|
||||
EditorSettingService,
|
||||
@@ -358,13 +358,11 @@ const NewDocDefaultModeSettings = () => {
|
||||
const AISettings = () => {
|
||||
const t = useI18n();
|
||||
const { openConfirmModal } = useConfirmModal();
|
||||
const { featureFlagService, serverConfigService } = useServices({
|
||||
const { featureFlagService, serverService } = useServices({
|
||||
FeatureFlagService,
|
||||
ServerConfigService,
|
||||
ServerService,
|
||||
});
|
||||
const serverFeatures = useLiveData(
|
||||
serverConfigService.serverConfig.features$
|
||||
);
|
||||
const serverFeatures = useLiveData(serverService.server.features$);
|
||||
const enableAI = useLiveData(featureFlagService.flags.enable_ai.$);
|
||||
|
||||
const onAIChange = useCallback(
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
import type { ReactElement, SVGProps } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { AuthService, ServerConfigService } from '../../../../modules/cloud';
|
||||
import { AuthService, ServerService } from '../../../../modules/cloud';
|
||||
import type { SettingState } from '../types';
|
||||
import { AboutAffine } from './about';
|
||||
import { AppearanceSettings } from './appearance';
|
||||
@@ -38,20 +38,16 @@ export type GeneralSettingList = GeneralSettingListItem[];
|
||||
|
||||
export const useGeneralSettingList = (): GeneralSettingList => {
|
||||
const t = useI18n();
|
||||
const {
|
||||
authService,
|
||||
serverConfigService,
|
||||
userFeatureService,
|
||||
featureFlagService,
|
||||
} = useServices({
|
||||
AuthService,
|
||||
ServerConfigService,
|
||||
UserFeatureService,
|
||||
FeatureFlagService,
|
||||
});
|
||||
const { authService, serverService, userFeatureService, featureFlagService } =
|
||||
useServices({
|
||||
AuthService,
|
||||
ServerService,
|
||||
UserFeatureService,
|
||||
FeatureFlagService,
|
||||
});
|
||||
const status = useLiveData(authService.session.status$);
|
||||
const hasPaymentFeature = useLiveData(
|
||||
serverConfigService.serverConfig.features$.map(f => f?.payment)
|
||||
serverService.server.features$.map(f => f?.payment)
|
||||
);
|
||||
const enableEditorSettings = useLiveData(
|
||||
featureFlagService.flags.enable_editor_settings.$
|
||||
|
||||
+3
-3
@@ -40,7 +40,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
type AuthAccountInfo,
|
||||
AuthService,
|
||||
ServerConfigService,
|
||||
ServerService,
|
||||
SubscriptionService,
|
||||
} from '../../../../../modules/cloud';
|
||||
import type { SettingState } from '../../types';
|
||||
@@ -65,9 +65,9 @@ export const CloudWorkspaceMembersPanel = ({
|
||||
}: {
|
||||
onChangeSettingState: (settingState: SettingState) => void;
|
||||
}) => {
|
||||
const serverConfig = useService(ServerConfigService).serverConfig;
|
||||
const serverService = useService(ServerService);
|
||||
const hasPaymentFeature = useLiveData(
|
||||
serverConfig.features$.map(f => f?.payment)
|
||||
serverService.server.features$.map(f => f?.payment)
|
||||
);
|
||||
const workspace = useService(WorkspaceService).workspace;
|
||||
|
||||
|
||||
@@ -2,19 +2,14 @@ import { notify } from '@affine/component';
|
||||
import {
|
||||
ChangeEmailPage,
|
||||
ChangePasswordPage,
|
||||
ConfirmChangeEmail,
|
||||
ConfirmVerifiedEmail,
|
||||
OnboardingPage,
|
||||
SetPasswordPage,
|
||||
SignInSuccessPage,
|
||||
SignUpPage,
|
||||
} from '@affine/component/auth-components';
|
||||
import {
|
||||
changeEmailMutation,
|
||||
changePasswordMutation,
|
||||
fetcher,
|
||||
sendVerifyChangeEmailMutation,
|
||||
verifyEmailMutation,
|
||||
} from '@affine/graphql';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
@@ -28,7 +23,10 @@ import {
|
||||
RouteLogic,
|
||||
useNavigateHelper,
|
||||
} from '../../../components/hooks/use-navigate-helper';
|
||||
import { AuthService, ServerConfigService } from '../../../modules/cloud';
|
||||
import { AuthService, ServerService } from '../../../modules/cloud';
|
||||
import { AppContainer } from '../../components/app-container';
|
||||
import { ConfirmChangeEmail } from './confirm-change-email';
|
||||
import { ConfirmVerifiedEmail } from './email-verified-email';
|
||||
|
||||
const authTypeSchema = z.enum([
|
||||
'onboarding',
|
||||
@@ -46,9 +44,9 @@ export const Component = () => {
|
||||
const authService = useService(AuthService);
|
||||
const account = useLiveData(authService.session.account$);
|
||||
const t = useI18n();
|
||||
const serverConfig = useService(ServerConfigService).serverConfig;
|
||||
const serverService = useService(ServerService);
|
||||
const passwordLimits = useLiveData(
|
||||
serverConfig.credentialsRequirement$.map(r => r?.password)
|
||||
serverService.server.credentialsRequirement$.map(r => r?.password)
|
||||
);
|
||||
|
||||
const { authType } = useParams();
|
||||
@@ -103,8 +101,7 @@ export const Component = () => {
|
||||
}, [jumpToIndex]);
|
||||
|
||||
if (!passwordLimits) {
|
||||
// TODO(@eyhn): loading UI
|
||||
return null;
|
||||
return <AppContainer fallback />;
|
||||
}
|
||||
|
||||
switch (authType) {
|
||||
@@ -171,36 +168,5 @@ export const loader: LoaderFunction = async args => {
|
||||
return redirect('/404');
|
||||
}
|
||||
|
||||
if (args.params.authType === 'confirm-change-email') {
|
||||
const url = new URL(args.request.url);
|
||||
const searchParams = url.searchParams;
|
||||
const token = searchParams.get('token') ?? '';
|
||||
const email = decodeURIComponent(searchParams.get('email') ?? '');
|
||||
const res = await fetcher({
|
||||
query: changeEmailMutation,
|
||||
variables: {
|
||||
token: token,
|
||||
email: email,
|
||||
},
|
||||
}).catch(console.error);
|
||||
// TODO(@eyhn): Add error handling
|
||||
if (!res?.changeEmail) {
|
||||
return redirect('/expired');
|
||||
}
|
||||
} else if (args.params.authType === 'verify-email') {
|
||||
const url = new URL(args.request.url);
|
||||
const searchParams = url.searchParams;
|
||||
const token = searchParams.get('token') ?? '';
|
||||
const res = await fetcher({
|
||||
query: verifyEmailMutation,
|
||||
variables: {
|
||||
token: token,
|
||||
},
|
||||
}).catch(console.error);
|
||||
|
||||
if (!res?.verifyEmail) {
|
||||
return redirect('/expired');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Button } from '@affine/component';
|
||||
import { AuthPageContainer } from '@affine/component/auth-components';
|
||||
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
||||
import { BackendError, GraphQLService } from '@affine/core/modules/cloud';
|
||||
import { changeEmailMutation, ErrorNames } from '@affine/graphql';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { useService } from '@toeverything/infra';
|
||||
import { type FC, useEffect, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { AppContainer } from '../../components/app-container';
|
||||
|
||||
export const ConfirmChangeEmail: FC<{
|
||||
onOpenAffine: () => void;
|
||||
}> = ({ onOpenAffine }) => {
|
||||
const t = useI18n();
|
||||
const [searchParams] = useSearchParams();
|
||||
const navigateHelper = useNavigateHelper();
|
||||
const graphqlService = useService(GraphQLService);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const token = searchParams.get('token') ?? '';
|
||||
const email = decodeURIComponent(searchParams.get('email') ?? '');
|
||||
setIsLoading(true);
|
||||
await graphqlService
|
||||
.gql({
|
||||
query: changeEmailMutation,
|
||||
variables: {
|
||||
token: token,
|
||||
email: email,
|
||||
},
|
||||
})
|
||||
.catch(err => {
|
||||
if (err instanceof BackendError) {
|
||||
const userFriendlyError = err.originError;
|
||||
if (userFriendlyError.name === ErrorNames.INVALID_EMAIL_TOKEN) {
|
||||
return navigateHelper.jumpToExpired();
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
})().catch(err => {
|
||||
// TODO(@eyhn): Add error handling
|
||||
console.error(err);
|
||||
});
|
||||
}, [graphqlService, navigateHelper, searchParams]);
|
||||
|
||||
if (isLoading) {
|
||||
return <AppContainer fallback />;
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthPageContainer
|
||||
title={t['com.affine.auth.change.email.page.success.title']()}
|
||||
subtitle={t['com.affine.auth.change.email.page.success.subtitle']()}
|
||||
>
|
||||
<Button variant="primary" size="large" onClick={onOpenAffine}>
|
||||
{t['com.affine.auth.open.affine']()}
|
||||
</Button>
|
||||
</AuthPageContainer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Button } from '@affine/component';
|
||||
import { AuthPageContainer } from '@affine/component/auth-components';
|
||||
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
||||
import { GraphQLService } from '@affine/core/modules/cloud';
|
||||
import {
|
||||
ErrorNames,
|
||||
UserFriendlyError,
|
||||
verifyEmailMutation,
|
||||
} from '@affine/graphql';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { useService } from '@toeverything/infra';
|
||||
import { type FC, useEffect, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { AppContainer } from '../../components/app-container';
|
||||
|
||||
export const ConfirmVerifiedEmail: FC<{
|
||||
onOpenAffine: () => void;
|
||||
}> = ({ onOpenAffine }) => {
|
||||
const t = useI18n();
|
||||
const graphqlService = useService(GraphQLService);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [searchParams] = useSearchParams();
|
||||
const navigateHelper = useNavigateHelper();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const token = searchParams.get('token') ?? '';
|
||||
setIsLoading(true);
|
||||
await graphqlService
|
||||
.gql({
|
||||
query: verifyEmailMutation,
|
||||
variables: {
|
||||
token: token,
|
||||
},
|
||||
})
|
||||
.catch(error => {
|
||||
const userFriendlyError = UserFriendlyError.fromAnyError(error);
|
||||
if (userFriendlyError.name === ErrorNames.INVALID_EMAIL_TOKEN) {
|
||||
return navigateHelper.jumpToExpired();
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
})().catch(err => {
|
||||
// TODO(@eyhn): Add error handling
|
||||
console.error(err);
|
||||
});
|
||||
}, [graphqlService, navigateHelper, searchParams]);
|
||||
|
||||
if (isLoading) {
|
||||
return <AppContainer fallback />;
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthPageContainer
|
||||
title={t['com.affine.auth.change.email.page.success.title']()}
|
||||
subtitle={t['com.affine.auth.change.email.page.success.subtitle']()}
|
||||
>
|
||||
<Button variant="primary" size="large" onClick={onOpenAffine}>
|
||||
{t['com.affine.auth.open.affine']()}
|
||||
</Button>
|
||||
</AuthPageContainer>
|
||||
);
|
||||
};
|
||||
@@ -2,56 +2,29 @@ import { AcceptInvitePage } from '@affine/component/member-components';
|
||||
import type { GetInviteInfoQuery } from '@affine/graphql';
|
||||
import {
|
||||
acceptInviteByInviteIdMutation,
|
||||
fetcher,
|
||||
getInviteInfoQuery,
|
||||
} from '@affine/graphql';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import type { LoaderFunction } from 'react-router-dom';
|
||||
import { redirect, useLoaderData } from 'react-router-dom';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
RouteLogic,
|
||||
useNavigateHelper,
|
||||
} from '../../../components/hooks/use-navigate-helper';
|
||||
import { AuthService } from '../../../modules/cloud';
|
||||
import { AuthService, GraphQLService } from '../../../modules/cloud';
|
||||
import { AppContainer } from '../../components/app-container';
|
||||
|
||||
/**
|
||||
* /invite/:inviteId page
|
||||
*
|
||||
* only for web
|
||||
*/
|
||||
export const loader: LoaderFunction = async args => {
|
||||
const inviteId = args.params.inviteId || '';
|
||||
const res = await fetcher({
|
||||
query: getInviteInfoQuery,
|
||||
variables: {
|
||||
inviteId,
|
||||
},
|
||||
}).catch(console.error);
|
||||
|
||||
// If the inviteId is invalid, redirect to 404 page
|
||||
if (!res || !res?.getInviteInfo) {
|
||||
return redirect('/404');
|
||||
}
|
||||
|
||||
// No mater sign in or not, we need to accept the invite
|
||||
await fetcher({
|
||||
query: acceptInviteByInviteIdMutation,
|
||||
variables: {
|
||||
workspaceId: res.getInviteInfo.workspace.id,
|
||||
inviteId,
|
||||
sendAcceptMail: true,
|
||||
},
|
||||
}).catch(console.error);
|
||||
|
||||
return {
|
||||
inviteId,
|
||||
inviteInfo: res.getInviteInfo,
|
||||
};
|
||||
};
|
||||
|
||||
export const Component = () => {
|
||||
const AcceptInvite = ({
|
||||
inviteInfo,
|
||||
}: {
|
||||
inviteInfo: GetInviteInfoQuery['getInviteInfo'];
|
||||
}) => {
|
||||
const authService = useService(AuthService);
|
||||
const isRevalidating = useLiveData(authService.session.isRevalidating$);
|
||||
const loginStatus = useLiveData(authService.session.status$);
|
||||
@@ -63,11 +36,6 @@ export const Component = () => {
|
||||
const { jumpToSignIn } = useNavigateHelper();
|
||||
const { jumpToPage } = useNavigateHelper();
|
||||
|
||||
const { inviteInfo } = useLoaderData() as {
|
||||
inviteId: string;
|
||||
inviteInfo: GetInviteInfoQuery['getInviteInfo'];
|
||||
};
|
||||
|
||||
const openWorkspace = useCallback(() => {
|
||||
jumpToPage(inviteInfo.workspace.id, 'all', RouteLogic.REPLACE);
|
||||
}, [inviteInfo.workspace.id, jumpToPage]);
|
||||
@@ -99,3 +67,57 @@ export const Component = () => {
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const Component = () => {
|
||||
const graphqlService = useService(GraphQLService);
|
||||
const params = useParams<{ inviteId: string }>();
|
||||
const navigateHelper = useNavigateHelper();
|
||||
|
||||
const [data, setData] = useState<{
|
||||
inviteId: string;
|
||||
inviteInfo: GetInviteInfoQuery['getInviteInfo'];
|
||||
} | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setData(null);
|
||||
const inviteId = params.inviteId || '';
|
||||
const res = await graphqlService.gql({
|
||||
query: getInviteInfoQuery,
|
||||
variables: {
|
||||
inviteId,
|
||||
},
|
||||
});
|
||||
|
||||
// If the inviteId is invalid, redirect to 404 page
|
||||
if (!res || !res?.getInviteInfo) {
|
||||
return navigateHelper.jumpTo404();
|
||||
}
|
||||
|
||||
// No mater sign in or not, we need to accept the invite
|
||||
await graphqlService.gql({
|
||||
query: acceptInviteByInviteIdMutation,
|
||||
variables: {
|
||||
workspaceId: res.getInviteInfo.workspace.id,
|
||||
inviteId,
|
||||
sendAcceptMail: true,
|
||||
},
|
||||
});
|
||||
|
||||
setData({
|
||||
inviteId,
|
||||
inviteInfo: res.getInviteInfo,
|
||||
});
|
||||
return;
|
||||
})().catch(err => {
|
||||
// TODO: handle error
|
||||
console.error(err);
|
||||
});
|
||||
}, [graphqlService, navigateHelper, params.inviteId]);
|
||||
|
||||
if (!data) {
|
||||
return <AppContainer fallback />;
|
||||
}
|
||||
|
||||
return <AcceptInvite inviteInfo={data.inviteInfo} />;
|
||||
};
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { GraphQLService } from '@affine/core/modules/cloud';
|
||||
import { OpenInAppPage } from '@affine/core/modules/open-in-app/views/open-in-app-page';
|
||||
import { appSchemes, channelToScheme } from '@affine/core/utils/channel';
|
||||
import type { GetCurrentUserQuery } from '@affine/graphql';
|
||||
import { fetcher, getCurrentUserQuery } from '@affine/graphql';
|
||||
import type { LoaderFunction } from 'react-router-dom';
|
||||
import { useLoaderData, useSearchParams } from 'react-router-dom';
|
||||
import { getCurrentUserQuery } from '@affine/graphql';
|
||||
import { useService } from '@toeverything/infra';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
|
||||
interface LoaderData {
|
||||
action: 'url' | 'signin-redirect';
|
||||
currentUser?: GetCurrentUserQuery['currentUser'];
|
||||
}
|
||||
import { AppContainer } from '../../components/app-container';
|
||||
|
||||
const OpenUrl = () => {
|
||||
const [params] = useSearchParams();
|
||||
@@ -33,8 +32,11 @@ const OpenUrl = () => {
|
||||
* @deprecated
|
||||
*/
|
||||
const OpenOAuthJwt = () => {
|
||||
const { currentUser } = useLoaderData() as LoaderData;
|
||||
const [currentUser, setCurrentUser] = useState<
|
||||
GetCurrentUserQuery['currentUser'] | null
|
||||
>(null);
|
||||
const [params] = useSearchParams();
|
||||
const graphqlService = useService(GraphQLService);
|
||||
|
||||
const maybeScheme = appSchemes.safeParse(params.get('scheme'));
|
||||
const scheme = maybeScheme.success
|
||||
@@ -42,8 +44,19 @@ const OpenOAuthJwt = () => {
|
||||
: channelToScheme[BUILD_CONFIG.appBuildType];
|
||||
const next = params.get('next');
|
||||
|
||||
useEffect(() => {
|
||||
graphqlService
|
||||
.gql({
|
||||
query: getCurrentUserQuery,
|
||||
})
|
||||
.then(res => {
|
||||
setCurrentUser(res?.currentUser || null);
|
||||
})
|
||||
.catch(console.error);
|
||||
}, [graphqlService]);
|
||||
|
||||
if (!currentUser || !currentUser?.token?.sessionToken) {
|
||||
return null;
|
||||
return <AppContainer fallback />;
|
||||
}
|
||||
|
||||
const urlToOpen = `${scheme}://signin-redirect?token=${
|
||||
@@ -54,7 +67,8 @@ const OpenOAuthJwt = () => {
|
||||
};
|
||||
|
||||
export const Component = () => {
|
||||
const { action } = useLoaderData() as LoaderData;
|
||||
const params = useParams<{ action: string }>();
|
||||
const action = params.action || '';
|
||||
|
||||
if (action === 'url') {
|
||||
return <OpenUrl />;
|
||||
@@ -63,22 +77,3 @@ export const Component = () => {
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader: LoaderFunction = async args => {
|
||||
const action = args.params.action || '';
|
||||
|
||||
if (action === 'signin-redirect') {
|
||||
const res = await fetcher({
|
||||
query: getCurrentUserQuery,
|
||||
}).catch(console.error);
|
||||
|
||||
return {
|
||||
action,
|
||||
currentUser: res?.currentUser || null,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
action,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { NotificationCenter } from '@affine/component';
|
||||
import { DefaultServerService } from '@affine/core/modules/cloud';
|
||||
import { FrameworkScope, useService } from '@toeverything/infra';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
import { GlobalDialogs } from '../../dialogs';
|
||||
@@ -6,13 +9,34 @@ import { CustomThemeModifier } from './custom-theme';
|
||||
import { FindInPageModal } from './find-in-page/find-in-page-modal';
|
||||
|
||||
export const RootWrapper = () => {
|
||||
const defaultServerService = useService(DefaultServerService);
|
||||
const [isServerReady, setIsServerReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (isServerReady) {
|
||||
return;
|
||||
}
|
||||
const abortController = new AbortController();
|
||||
defaultServerService.server
|
||||
.waitForConfigRevalidation(abortController.signal)
|
||||
.then(() => {
|
||||
setIsServerReady(true);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
return () => {
|
||||
abortController.abort();
|
||||
};
|
||||
}, [defaultServerService, isServerReady]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FrameworkScope scope={defaultServerService.server.scope}>
|
||||
<GlobalDialogs />
|
||||
<NotificationCenter />
|
||||
<Outlet />
|
||||
<CustomThemeModifier />
|
||||
{BUILD_CONFIG.isElectron && <FindInPageModal />}
|
||||
</>
|
||||
</FrameworkScope>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user