From 5ac36b6f0acf964be6bc1b653085e3782f454d74 Mon Sep 17 00:00:00 2001 From: Himself65 Date: Thu, 6 Apr 2023 16:14:23 -0500 Subject: [PATCH] refactor: add workspace events (#1838) --- .../header/header-right-items/SyncUser.tsx | 2 +- apps/web/src/components/pure/footer/index.tsx | 1 + .../components/pure/message-center/index.tsx | 2 +- .../web/src/hooks/affine/use-affine-log-in.ts | 30 ++++----------- .../src/hooks/affine/use-affine-log-out.ts | 21 +++------- .../affine/use-affine-refresh-auth-token.ts | 2 +- .../src/hooks/use-create-first-workspace.ts | 30 ++++----------- .../pages/workspace/[workspaceId]/setting.tsx | 2 +- apps/web/src/plugins/affine/index.tsx | 38 +++++++++++++++++-- apps/web/src/plugins/index.tsx | 4 +- apps/web/src/plugins/local/index.tsx | 29 ++++++++++++++ packages/workspace/src/type.ts | 10 +++++ .../parallels/affine/affine-workspace.spec.ts | 5 +++ 13 files changed, 105 insertions(+), 71 deletions(-) diff --git a/apps/web/src/components/blocksuite/header/header-right-items/SyncUser.tsx b/apps/web/src/components/blocksuite/header/header-right-items/SyncUser.tsx index 9cf06621d3..55ee14df47 100644 --- a/apps/web/src/components/blocksuite/header/header-right-items/SyncUser.tsx +++ b/apps/web/src/components/blocksuite/header/header-right-items/SyncUser.tsx @@ -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'; diff --git a/apps/web/src/components/pure/footer/index.tsx b/apps/web/src/components/pure/footer/index.tsx index 0b03107de4..edf0287054 100644 --- a/apps/web/src/components/pure/footer/index.tsx +++ b/apps/web/src/components/pure/footer/index.tsx @@ -36,6 +36,7 @@ export const Footer: React.FC = ({ user, onLogin, onLogout }) => { { onLogout(); }} diff --git a/apps/web/src/components/pure/message-center/index.tsx b/apps/web/src/components/pure/message-center/index.tsx index 9f6e9f1e3d..586c973acc 100644 --- a/apps/web/src/components/pure/message-center/index.tsx +++ b/apps/web/src/components/pure/message-center/index.tsx @@ -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 { diff --git a/apps/web/src/hooks/affine/use-affine-log-in.ts b/apps/web/src/hooks/affine/use-affine-log-in.ts index 00f23bbf60..f687743741 100644 --- a/apps/web/src/hooks/affine/use-affine-log-in.ts +++ b/apps/web/src/hooks/affine/use-affine-log-in.ts @@ -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]); } diff --git a/apps/web/src/hooks/affine/use-affine-log-out.ts b/apps/web/src/hooks/affine/use-affine-log-out.ts index c119aec6db..e18f1066fa 100644 --- a/apps/web/src/hooks/affine/use-affine-log-out.ts +++ b/apps/web/src/hooks/affine/use-affine-log-out.ts @@ -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]); } diff --git a/apps/web/src/hooks/affine/use-affine-refresh-auth-token.ts b/apps/web/src/hooks/affine/use-affine-refresh-auth-token.ts index 7d005e37e3..5b362b8cd2 100644 --- a/apps/web/src/hooks/affine/use-affine-refresh-auth-token.ts +++ b/apps/web/src/hooks/affine/use-affine-refresh-auth-token.ts @@ -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'); diff --git a/apps/web/src/hooks/use-create-first-workspace.ts b/apps/web/src/hooks/use-create-first-workspace.ts index 4388532853..052bd493f2 100644 --- a/apps/web/src/hooks/use-create-first-workspace.ts +++ b/apps/web/src/hooks/use-create-first-workspace.ts @@ -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']?.(); + } } }); }, []); diff --git a/apps/web/src/pages/workspace/[workspaceId]/setting.tsx b/apps/web/src/pages/workspace/[workspaceId]/setting.tsx index 963b162dc7..cdb69048eb 100644 --- a/apps/web/src/pages/workspace/[workspaceId]/setting.tsx +++ b/apps/web/src/pages/workspace/[workspaceId]/setting.tsx @@ -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( diff --git a/apps/web/src/plugins/affine/index.tsx b/apps/web/src/plugins/affine/index.tsx index 387cf02789..56b932d195 100644 --- a/apps/web/src/plugins/affine/index.tsx +++ b/apps/web/src/plugins/affine/index.tsx @@ -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 = { 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 => { diff --git a/apps/web/src/plugins/index.tsx b/apps/web/src/plugins/index.tsx index c914d51bc7..7eec0a95c4 100644 --- a/apps/web/src/plugins/index.tsx +++ b/apps/web/src/plugins/index.tsx @@ -1,4 +1,5 @@ import type { + AppEvents, LoadPriority, WorkspaceCRUD, WorkspaceUISchema, @@ -12,8 +13,7 @@ export interface WorkspacePlugin { flavour: Flavour; // Plugin will be loaded according to the priority loadPriority: LoadPriority; - // fixme: this is a hack - cleanup?: () => void; + Events: Partial; // Fetch necessary data for the first render CRUD: WorkspaceCRUD; UI: WorkspaceUISchema; diff --git a/apps/web/src/plugins/local/index.tsx b/apps/web/src/plugins/local/index.tsx index c3b02c80e3..bb46df937d 100644 --- a/apps/web/src/plugins/local/index.tsx +++ b/apps/web/src/plugins/local/index.tsx @@ -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 = { 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 }) => { diff --git a/packages/workspace/src/type.ts b/packages/workspace/src/type.ts index a3e7a32d6c..d3645af239 100644 --- a/packages/workspace/src/type.ts +++ b/packages/workspace/src/type.ts @@ -128,3 +128,13 @@ export interface WorkspaceUISchema { SettingsDetail: FC>; Provider: FC; } + +export interface AppEvents { + // event when app is first initialized + // usually used to initialize workspace plugin + 'app:first-init': () => Promise; + // request to gain access to workspace plugin + 'workspace:access': () => Promise; + // request to revoke access to workspace plugin + 'workspace:revoke': () => Promise; +} diff --git a/tests/parallels/affine/affine-workspace.spec.ts b/tests/parallels/affine/affine-workspace.spec.ts index 143329dcd9..67e9258ec2 100644 --- a/tests/parallels/affine/affine-workspace.spec.ts +++ b/tests/parallels/affine/affine-workspace.spec.ts @@ -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); }); });