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