mirror of
https://github.com/toeverything/AFFiNE.git
synced 2025-01-03 09:02:45 +03:00
refactor: add workspace events (#1838)
This commit is contained in:
parent
b6bdf257e4
commit
5ac36b6f0a
@ -16,9 +16,9 @@ import { assertEquals, assertExists } from '@blocksuite/store';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { affineAuth } from '../../../../hooks/affine/use-affine-log-in';
|
||||
import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace';
|
||||
import { useTransformWorkspace } from '../../../../hooks/use-transform-workspace';
|
||||
import { affineAuth } from '../../../../plugins/affine';
|
||||
import type { AffineOfficialWorkspace } from '../../../../shared';
|
||||
import { TransformWorkspaceToAffineModal } from '../../../affine/transform-workspace-to-affine-modal';
|
||||
|
||||
|
@ -36,6 +36,7 @@ export const Footer: React.FC<FooterProps> = ({ user, onLogin, onLogout }) => {
|
||||
</FlexWrapper>
|
||||
<Tooltip content={t('Sign out')} disablePortal={true}>
|
||||
<IconButton
|
||||
data-testid="workspace-list-modal-sign-out"
|
||||
onClick={() => {
|
||||
onLogout();
|
||||
}}
|
||||
|
@ -3,8 +3,8 @@ import { setLoginStorage, SignMethod } from '@affine/workspace/affine/login';
|
||||
import type React from 'react';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
|
||||
import { affineAuth } from '../../../hooks/affine/use-affine-log-in';
|
||||
import { useAffineLogOut } from '../../../hooks/affine/use-affine-log-out';
|
||||
import { affineAuth } from '../../../plugins/affine';
|
||||
import { toast } from '../../../utils';
|
||||
|
||||
declare global {
|
||||
|
@ -1,30 +1,16 @@
|
||||
import { currentAffineUserAtom } from '@affine/workspace/affine/atom';
|
||||
import {
|
||||
createAffineAuth,
|
||||
parseIdToken,
|
||||
setLoginStorage,
|
||||
SignMethod,
|
||||
} from '@affine/workspace/affine/login';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { WorkspaceFlavour } from '@affine/workspace/type';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { toast } from '../../utils';
|
||||
|
||||
export const affineAuth = createAffineAuth();
|
||||
import { WorkspacePlugins } from '../../plugins';
|
||||
|
||||
export function useAffineLogIn() {
|
||||
const router = useRouter();
|
||||
const setUser = useSetAtom(currentAffineUserAtom);
|
||||
return useCallback(async () => {
|
||||
const response = await affineAuth.generateToken(SignMethod.Google);
|
||||
if (response) {
|
||||
setLoginStorage(response);
|
||||
const user = parseIdToken(response.token);
|
||||
setUser(user);
|
||||
await WorkspacePlugins[WorkspaceFlavour.AFFINE].Events[
|
||||
'workspace:access'
|
||||
]?.();
|
||||
// todo: remove reload page requirement
|
||||
router.reload();
|
||||
} else {
|
||||
toast('Login failed');
|
||||
}
|
||||
}, [router, setUser]);
|
||||
}, [router]);
|
||||
}
|
||||
|
@ -1,26 +1,15 @@
|
||||
import { currentAffineUserAtom } from '@affine/workspace/affine/atom';
|
||||
import { clearLoginStorage } from '@affine/workspace/affine/login';
|
||||
import { jotaiWorkspacesAtom } from '@affine/workspace/atom';
|
||||
import { WorkspaceFlavour } from '@affine/workspace/type';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { WorkspacePlugins } from '../../plugins';
|
||||
|
||||
export function useAffineLogOut() {
|
||||
const set = useSetAtom(jotaiWorkspacesAtom);
|
||||
const router = useRouter();
|
||||
const setCurrentUser = useSetAtom(currentAffineUserAtom);
|
||||
return useCallback(() => {
|
||||
set(workspaces =>
|
||||
workspaces.filter(
|
||||
workspace => workspace.flavour !== WorkspaceFlavour.AFFINE
|
||||
)
|
||||
);
|
||||
WorkspacePlugins[WorkspaceFlavour.AFFINE].cleanup?.();
|
||||
clearLoginStorage();
|
||||
setCurrentUser(null);
|
||||
return useCallback(async () => {
|
||||
await WorkspacePlugins[WorkspaceFlavour.AFFINE].Events[
|
||||
'workspace:revoke'
|
||||
]?.();
|
||||
router.reload();
|
||||
}, [router, set, setCurrentUser]);
|
||||
}, [router]);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
} from '@affine/workspace/affine/login';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import { affineAuth } from './use-affine-log-in';
|
||||
import { affineAuth } from '../../plugins/affine';
|
||||
|
||||
const logger = new DebugLogger('auth-token');
|
||||
|
||||
|
@ -1,14 +1,7 @@
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import { DEFAULT_WORKSPACE_NAME } from '@affine/env';
|
||||
import { jotaiStore, jotaiWorkspacesAtom } from '@affine/workspace/atom';
|
||||
import { WorkspaceFlavour } from '@affine/workspace/type';
|
||||
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
|
||||
import { assertEquals, assertExists, nanoid } from '@blocksuite/store';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { LocalPlugin } from '../plugins/local';
|
||||
|
||||
const logger = new DebugLogger('use-create-first-workspace');
|
||||
import { WorkspacePlugins } from '../plugins';
|
||||
|
||||
export function useCreateFirstWorkspace() {
|
||||
// may not need use effect at all, right?
|
||||
@ -24,22 +17,13 @@ export function useCreateFirstWorkspace() {
|
||||
* Create a first workspace, only just once for a browser
|
||||
*/
|
||||
async function createFirst() {
|
||||
const blockSuiteWorkspace = createEmptyBlockSuiteWorkspace(
|
||||
nanoid(),
|
||||
(_: string) => undefined
|
||||
const Plugins = Object.values(WorkspacePlugins).sort(
|
||||
(a, b) => a.loadPriority - b.loadPriority
|
||||
);
|
||||
blockSuiteWorkspace.meta.setName(DEFAULT_WORKSPACE_NAME);
|
||||
const id = await LocalPlugin.CRUD.create(blockSuiteWorkspace);
|
||||
const workspace = await LocalPlugin.CRUD.get(id);
|
||||
assertExists(workspace);
|
||||
assertEquals(workspace.id, id);
|
||||
jotaiStore.set(jotaiWorkspacesAtom, [
|
||||
{
|
||||
id: workspace.id,
|
||||
flavour: WorkspaceFlavour.LOCAL,
|
||||
},
|
||||
]);
|
||||
logger.info('created local workspace', id);
|
||||
|
||||
for (const Plugin of Plugins) {
|
||||
await Plugin.Events['app:first-init']?.();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
@ -23,13 +23,13 @@ import React, { useCallback, useEffect } from 'react';
|
||||
import { Unreachable } from '../../../components/affine/affine-error-eoundary';
|
||||
import { PageLoading } from '../../../components/pure/loading';
|
||||
import { WorkspaceTitle } from '../../../components/pure/workspace-title';
|
||||
import { affineAuth } from '../../../hooks/affine/use-affine-log-in';
|
||||
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
|
||||
import { useSyncRouterWithCurrentWorkspace } from '../../../hooks/use-sync-router-with-current-workspace';
|
||||
import { useTransformWorkspace } from '../../../hooks/use-transform-workspace';
|
||||
import { useWorkspacesHelper } from '../../../hooks/use-workspaces';
|
||||
import { WorkspaceLayout } from '../../../layouts';
|
||||
import { WorkspacePlugins } from '../../../plugins';
|
||||
import { affineAuth } from '../../../plugins/affine';
|
||||
import type { NextPageWithLayout } from '../../../shared';
|
||||
|
||||
const settingPanelAtom = atomWithSyncStorage<SettingPanel>(
|
||||
|
@ -1,4 +1,13 @@
|
||||
import { getLoginStorage } from '@affine/workspace/affine/login';
|
||||
import { currentAffineUserAtom } from '@affine/workspace/affine/atom';
|
||||
import {
|
||||
clearLoginStorage,
|
||||
createAffineAuth,
|
||||
getLoginStorage,
|
||||
parseIdToken,
|
||||
setLoginStorage,
|
||||
SignMethod,
|
||||
} from '@affine/workspace/affine/login';
|
||||
import { jotaiStore, jotaiWorkspacesAtom } from '@affine/workspace/atom';
|
||||
import type { AffineWorkspace } from '@affine/workspace/type';
|
||||
import { LoadPriority, WorkspaceFlavour } from '@affine/workspace/type';
|
||||
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
|
||||
@ -15,7 +24,7 @@ import { PageDetailEditor } from '../../components/page-detail-editor';
|
||||
import { AffineSWRConfigProvider } from '../../providers/AffineSWRConfigProvider';
|
||||
import { BlockSuiteWorkspace } from '../../shared';
|
||||
import { affineApis } from '../../shared/apis';
|
||||
import { initPage } from '../../utils';
|
||||
import { initPage, toast } from '../../utils';
|
||||
import type { WorkspacePlugin } from '..';
|
||||
import { QueryKey } from './fetcher';
|
||||
|
||||
@ -56,11 +65,32 @@ const getPersistenceAllWorkspace = () => {
|
||||
return allWorkspaces;
|
||||
};
|
||||
|
||||
export const affineAuth = createAffineAuth();
|
||||
|
||||
export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
||||
flavour: WorkspaceFlavour.AFFINE,
|
||||
loadPriority: LoadPriority.HIGH,
|
||||
cleanup: () => {
|
||||
Events: {
|
||||
'workspace:access': async () => {
|
||||
const response = await affineAuth.generateToken(SignMethod.Google);
|
||||
if (response) {
|
||||
setLoginStorage(response);
|
||||
const user = parseIdToken(response.token);
|
||||
jotaiStore.set(currentAffineUserAtom, user);
|
||||
} else {
|
||||
toast('Login failed');
|
||||
}
|
||||
},
|
||||
'workspace:revoke': async () => {
|
||||
jotaiStore.set(jotaiWorkspacesAtom, workspaces =>
|
||||
workspaces.filter(
|
||||
workspace => workspace.flavour !== WorkspaceFlavour.AFFINE
|
||||
)
|
||||
);
|
||||
storage.removeItem(kAffineLocal);
|
||||
clearLoginStorage();
|
||||
jotaiStore.set(currentAffineUserAtom, null);
|
||||
},
|
||||
},
|
||||
CRUD: {
|
||||
create: async blockSuiteWorkspace => {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type {
|
||||
AppEvents,
|
||||
LoadPriority,
|
||||
WorkspaceCRUD,
|
||||
WorkspaceUISchema,
|
||||
@ -12,8 +13,7 @@ export interface WorkspacePlugin<Flavour extends WorkspaceFlavour> {
|
||||
flavour: Flavour;
|
||||
// Plugin will be loaded according to the priority
|
||||
loadPriority: LoadPriority;
|
||||
// fixme: this is a hack
|
||||
cleanup?: () => void;
|
||||
Events: Partial<AppEvents>;
|
||||
// Fetch necessary data for the first render
|
||||
CRUD: WorkspaceCRUD<Flavour>;
|
||||
UI: WorkspaceUISchema<Flavour>;
|
||||
|
@ -1,5 +1,10 @@
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import { DEFAULT_WORKSPACE_NAME } from '@affine/env';
|
||||
import { jotaiStore, jotaiWorkspacesAtom } from '@affine/workspace/atom';
|
||||
import { CRUD } from '@affine/workspace/local/crud';
|
||||
import { LoadPriority, WorkspaceFlavour } from '@affine/workspace/type';
|
||||
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
|
||||
import { assertEquals, assertExists, nanoid } from '@blocksuite/store';
|
||||
import React from 'react';
|
||||
|
||||
import { PageNotFoundError } from '../../components/affine/affine-error-eoundary';
|
||||
@ -9,9 +14,33 @@ import { PageDetailEditor } from '../../components/page-detail-editor';
|
||||
import { initPage } from '../../utils';
|
||||
import type { WorkspacePlugin } from '..';
|
||||
|
||||
const logger = new DebugLogger('use-create-first-workspace');
|
||||
|
||||
export const LocalPlugin: WorkspacePlugin<WorkspaceFlavour.LOCAL> = {
|
||||
flavour: WorkspaceFlavour.LOCAL,
|
||||
loadPriority: LoadPriority.LOW,
|
||||
Events: {
|
||||
'app:first-init': async () => {
|
||||
const blockSuiteWorkspace = createEmptyBlockSuiteWorkspace(
|
||||
nanoid(),
|
||||
(_: string) => undefined
|
||||
);
|
||||
blockSuiteWorkspace.meta.setName(DEFAULT_WORKSPACE_NAME);
|
||||
const id = await LocalPlugin.CRUD.create(blockSuiteWorkspace);
|
||||
const workspace = await LocalPlugin.CRUD.get(id);
|
||||
assertExists(workspace);
|
||||
assertEquals(workspace.id, id);
|
||||
// todo: use a better way to set initial workspace
|
||||
jotaiStore.set(jotaiWorkspacesAtom, ws => [
|
||||
...ws,
|
||||
{
|
||||
id: workspace.id,
|
||||
flavour: WorkspaceFlavour.LOCAL,
|
||||
},
|
||||
]);
|
||||
logger.debug('create first workspace', workspace);
|
||||
},
|
||||
},
|
||||
CRUD,
|
||||
UI: {
|
||||
Provider: ({ children }) => {
|
||||
|
@ -128,3 +128,13 @@ export interface WorkspaceUISchema<Flavour extends keyof WorkspaceRegistry> {
|
||||
SettingsDetail: FC<SettingProps<Flavour>>;
|
||||
Provider: FC<PropsWithChildren>;
|
||||
}
|
||||
|
||||
export interface AppEvents {
|
||||
// event when app is first initialized
|
||||
// usually used to initialize workspace plugin
|
||||
'app:first-init': () => Promise<void>;
|
||||
// request to gain access to workspace plugin
|
||||
'workspace:access': () => Promise<void>;
|
||||
// request to revoke access to workspace plugin
|
||||
'workspace:revoke': () => Promise<void>;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import { createFakeUser, loginUser, openHomePage } from '../../libs/utils';
|
||||
import {
|
||||
assertCurrentWorkspaceFlavour,
|
||||
createWorkspace,
|
||||
openWorkspaceListModal,
|
||||
} from '../../libs/workspace';
|
||||
|
||||
test.describe('affine workspace', () => {
|
||||
@ -55,5 +56,9 @@ test.describe('affine workspace', () => {
|
||||
delay: 50,
|
||||
});
|
||||
await assertCurrentWorkspaceFlavour('affine', page);
|
||||
await openWorkspaceListModal(page);
|
||||
await page.getByTestId('workspace-list-modal-sign-out').click();
|
||||
await page.waitForTimeout(1000);
|
||||
await assertCurrentWorkspaceFlavour('local', page);
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user