fix(admin): fix the admin app (#9233)

This commit is contained in:
EYHN
2024-12-23 03:06:31 +00:00
parent ec5bbb442f
commit cddef4c2f6
12 changed files with 253 additions and 63 deletions

View File

@@ -1,20 +1,5 @@
import { Toaster } from '@affine/admin/components/ui/sonner';
import {
configureCloudModule,
DefaultServerService,
} from '@affine/core/modules/cloud';
import { configureLocalStorageStateStorageImpls } from '@affine/core/modules/storage';
import { configureUrlModule } from '@affine/core/modules/url';
import { wrapCreateBrowserRouter } from '@sentry/react';
import {
configureGlobalContextModule,
configureGlobalStorageModule,
configureLifecycleModule,
Framework,
FrameworkRoot,
FrameworkScope,
LifecycleService,
} from '@toeverything/infra';
import { useEffect } from 'react';
import {
createBrowserRouter as reactRouterCreateBrowserRouter,
@@ -124,38 +109,18 @@ export const router = _createBrowserRouter(
}
);
const framework = new Framework();
configureLifecycleModule(framework);
configureLocalStorageStateStorageImpls(framework);
configureGlobalStorageModule(framework);
configureGlobalContextModule(framework);
configureUrlModule(framework);
configureCloudModule(framework);
const frameworkProvider = framework.provider();
// setup application lifecycle events, and emit application start event
window.addEventListener('focus', () => {
frameworkProvider.get(LifecycleService).applicationFocus();
});
frameworkProvider.get(LifecycleService).applicationStart();
const serverService = frameworkProvider.get(DefaultServerService);
export const App = () => {
return (
<FrameworkRoot framework={frameworkProvider}>
<FrameworkScope scope={serverService.server.scope}>
<TooltipProvider>
<SWRConfig
value={{
revalidateOnFocus: false,
revalidateOnMount: false,
}}
>
<RouterProvider router={router} />
</SWRConfig>
<Toaster />
</TooltipProvider>
</FrameworkScope>
</FrameworkRoot>
<TooltipProvider>
<SWRConfig
value={{
revalidateOnFocus: false,
revalidateOnMount: false,
}}
>
<RouterProvider router={router} />
</SWRConfig>
<Toaster />
</TooltipProvider>
);
};

View File

@@ -1,6 +1,6 @@
import { Button } from '@affine/admin/components/ui/button';
import { Input } from '@affine/admin/components/ui/input';
import { useQuery } from '@affine/core/components/hooks/use-query';
import { useQuery } from '@affine/admin/use-query';
import { getUserByEmailQuery } from '@affine/graphql';
import { PlusIcon } from 'lucide-react';
import type { SetStateAction } from 'react';

View File

@@ -1,9 +1,9 @@
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import {
useMutateQueryResource,
useMutation,
} from '@affine/core/components/hooks/use-mutation';
import { useQuery } from '@affine/core/components/hooks/use-query';
} from '@affine/admin/use-mutation';
import { useQuery } from '@affine/admin/use-query';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import {
createChangePasswordUrlMutation,
createUserMutation,

View File

@@ -1,4 +1,4 @@
import { useQuery } from '@affine/core/components/hooks/use-query';
import { useQuery } from '@affine/admin/use-query';
import { listUsersQuery } from '@affine/graphql';
import { useState } from 'react';

View File

@@ -1,9 +1,9 @@
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import {
useMutateQueryResource,
useMutation,
} from '@affine/core/components/hooks/use-mutation';
import { useQuery } from '@affine/core/components/hooks/use-query';
} from '@affine/admin/use-mutation';
import { useQuery } from '@affine/admin/use-query';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { getPromptsQuery, updatePromptMutation } from '@affine/graphql';
import { toast } from 'sonner';

View File

@@ -1,5 +1,3 @@
import { useMutateQueryResource } from '@affine/core/components/hooks/use-mutation';
import { useQuery } from '@affine/core/components/hooks/use-query';
import type { GetCurrentUserFeaturesQuery } from '@affine/graphql';
import {
adminServerConfigQuery,
@@ -7,6 +5,9 @@ import {
getCurrentUserFeaturesQuery,
} from '@affine/graphql';
import { useMutateQueryResource } from '../use-mutation';
import { useQuery } from '../use-query';
export const useServerConfig = () => {
const { data } = useQuery({
query: adminServerConfigQuery,

View File

@@ -1,4 +1,4 @@
import { useQueryImmutable } from '@affine/core/components/hooks/use-query';
import { useQueryImmutable } from '@affine/admin/use-query';
import { getServerServiceConfigsQuery } from '@affine/graphql';
import { useMemo } from 'react';

View File

@@ -1,4 +1,4 @@
import { useQuery } from '@affine/core/components/hooks/use-query';
import { useQuery } from '@affine/admin/use-query';
import { getServerRuntimeConfigQuery } from '@affine/graphql';
import { useMemo } from 'react';

View File

@@ -1,9 +1,9 @@
import { notify } from '@affine/component';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import {
useMutateQueryResource,
useMutation,
} from '@affine/core/components/hooks/use-mutation';
} from '@affine/admin/use-mutation';
import { notify } from '@affine/component';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import {
getServerRuntimeConfigQuery,
updateServerRuntimeConfigsMutation,

View File

@@ -0,0 +1,94 @@
import type {
GraphQLQuery,
MutationOptions,
QueryResponse,
QueryVariables,
RecursiveMaybeFields,
} from '@affine/graphql';
import type { GraphQLError } from 'graphql';
import { useMemo } from 'react';
import type { Key } from 'swr';
import { useSWRConfig } from 'swr';
import type {
SWRMutationConfiguration,
SWRMutationResponse,
} from 'swr/mutation';
import useSWRMutation from 'swr/mutation';
import { gqlFetcher } from './use-query';
/**
* A useSWRMutation wrapper for sending graphql mutations
*
* @example
*
* ```ts
* import { someMutation } from '@affine/graphql'
*
* const { trigger } = useMutation({
* mutation: someMutation,
* })
*
* trigger({ name: 'John Doe' })
*/
export function useMutation<Mutation extends GraphQLQuery, K extends Key = Key>(
options: Omit<MutationOptions<Mutation>, 'variables'>,
config?: Omit<
SWRMutationConfiguration<
QueryResponse<Mutation>,
GraphQLError,
K,
QueryVariables<Mutation>
>,
'fetcher'
>
): SWRMutationResponse<
QueryResponse<Mutation>,
GraphQLError,
K,
QueryVariables<Mutation>
>;
export function useMutation(
options: Omit<MutationOptions<GraphQLQuery>, 'variables'>,
config?: any
) {
return useSWRMutation(
() => ['cloud', options.mutation.id],
(_: unknown[], { arg }: { arg: any }) =>
gqlFetcher({
...options,
query: options.mutation,
variables: arg,
}),
config
);
}
// use this to revalidate all queries that match the filter
export const useMutateQueryResource = () => {
const { mutate } = useSWRConfig();
const revalidateResource = useMemo(
() =>
<Q extends GraphQLQuery>(
query: Q,
varsFilter: (
vars: RecursiveMaybeFields<QueryVariables<Q>>
) => boolean = _vars => true
) => {
return mutate(key => {
const res =
Array.isArray(key) &&
key[0] === 'cloud' &&
key[1] === query.id &&
varsFilter(key[2]);
if (res) {
console.debug('revalidate resource', key);
}
return res;
});
},
[mutate]
);
return revalidateResource;
};

View File

@@ -0,0 +1,131 @@
import {
gqlFetcherFactory,
type GraphQLQuery,
type QueryOptions,
type QueryResponse,
} from '@affine/graphql';
import type { GraphQLError } from 'graphql';
import { useCallback, useMemo } from 'react';
import type { SWRConfiguration, SWRResponse } from 'swr';
import useSWR from 'swr';
import useSWRImmutable from 'swr/immutable';
import useSWRInfinite from 'swr/infinite';
/**
* A `useSWR` wrapper for sending graphql queries
*
* @example
*
* ```ts
* import { someQuery, someQueryWithNoVars } from '@affine/graphql'
*
* const swrResponse1 = useQuery({
* query: workspaceByIdQuery,
* variables: { id: '1' }
* })
*
* const swrResponse2 = useQuery({
* query: someQueryWithNoVars
* })
* ```
*/
type useQueryFn = <Query extends GraphQLQuery>(
options?: QueryOptions<Query>,
config?: Omit<
SWRConfiguration<
QueryResponse<Query>,
GraphQLError,
(options: QueryOptions<Query>) => Promise<QueryResponse<Query>>
>,
'fetcher'
>
) => SWRResponse<
QueryResponse<Query>,
GraphQLError,
{
suspense: true;
}
>;
const createUseQuery =
(immutable: boolean): useQueryFn =>
(options, config) => {
const configWithSuspense: SWRConfiguration = useMemo(
() => ({
suspense: true,
...config,
}),
[config]
);
const useSWRFn = immutable ? useSWRImmutable : useSWR;
return useSWRFn(
options ? () => ['cloud', options.query.id, options.variables] : null,
options ? () => gqlFetcher(options) : null,
configWithSuspense
);
};
export const useQuery = createUseQuery(false);
export const useQueryImmutable = createUseQuery(true);
export const gqlFetcher = gqlFetcherFactory('/graphql', window.fetch);
export function useQueryInfinite<Query extends GraphQLQuery>(
options: Omit<QueryOptions<Query>, 'variables'> & {
getVariables: (
pageIndex: number,
previousPageData: QueryResponse<Query>
) => QueryOptions<Query>['variables'];
},
config?: Omit<
SWRConfiguration<
QueryResponse<Query>,
GraphQLError | GraphQLError[],
(options: QueryOptions<Query>) => Promise<QueryResponse<Query>>
>,
'fetcher'
>
) {
const configWithSuspense: SWRConfiguration = useMemo(
() => ({
suspense: true,
...config,
}),
[config]
);
const { data, setSize, size, error } = useSWRInfinite<
QueryResponse<Query>,
GraphQLError | GraphQLError[]
>(
(pageIndex: number, previousPageData: QueryResponse<Query>) => [
'cloud',
options.query.id,
options.getVariables(pageIndex, previousPageData),
],
async ([_, __, variables]) => {
const params = { ...options, variables } as QueryOptions<Query>;
return gqlFetcher(params);
},
configWithSuspense
);
const loadingMore = size > 0 && data && !data[size - 1];
// TODO(@Peng): find a generic way to know whether or not there are more items to load
const loadMore = useCallback(() => {
if (loadingMore) {
return;
}
setSize(size => size + 1).catch(err => {
console.error(err);
});
}, [loadingMore, setSize]);
return {
data,
error,
loadingMore,
loadMore,
};
}