refactor: add workspace events (#1838)

This commit is contained in:
Himself65 2023-04-06 16:14:23 -05:00 committed by GitHub
parent b6bdf257e4
commit 5ac36b6f0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 105 additions and 71 deletions

View File

@ -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';

View File

@ -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();
}}

View File

@ -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 {

View File

@ -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);
router.reload();
} else {
toast('Login failed');
}
}, [router, setUser]);
await WorkspacePlugins[WorkspaceFlavour.AFFINE].Events[
'workspace:access'
]?.();
// todo: remove reload page requirement
router.reload();
}, [router]);
}

View File

@ -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]);
}

View File

@ -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');

View File

@ -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']?.();
}
}
});
}, []);

View File

@ -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>(

View File

@ -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: () => {
storage.removeItem(kAffineLocal);
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 => {

View File

@ -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>;

View File

@ -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 }) => {

View File

@ -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>;
}

View File

@ -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);
});
});