refactor(infra): migrate to new infra (#5565)

This commit is contained in:
EYHN
2024-01-30 07:16:39 +00:00
parent 1e3499c323
commit 329fc19852
170 changed files with 2007 additions and 4354 deletions
@@ -1,12 +1,12 @@
import {
currentWorkspaceAtom,
workspaceListAtom,
} from '@affine/core/modules/workspace';
import { WorkspaceListService } from '@toeverything/infra';
import { useService } from '@toeverything/infra/di';
import { useLiveData } from '@toeverything/infra/livedata';
import { useAtomValue } from 'jotai/react';
import { useEffect } from 'react';
import { useLocation, useParams } from 'react-router-dom';
import { currentPageIdAtom } from '../../../../atoms/mode';
import { CurrentWorkspaceService } from '../../../../modules/workspace/current-workspace';
export interface DumpInfoProps {
error: any;
@@ -14,8 +14,10 @@ export interface DumpInfoProps {
export const DumpInfo = (_props: DumpInfoProps) => {
const location = useLocation();
const workspaceList = useAtomValue(workspaceListAtom);
const currentWorkspace = useAtomValue(currentWorkspaceAtom);
const workspaceList = useService(WorkspaceListService);
const currentWorkspace = useLiveData(
useService(CurrentWorkspaceService).currentWorkspace
);
const currentPageId = useAtomValue(currentPageIdAtom);
const path = location.pathname;
const query = useParams();
@@ -1,13 +1,16 @@
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
import { useAtomValue } from 'jotai';
import { useService } from '@toeverything/infra/di';
import { useLiveData } from '@toeverything/infra/livedata';
import { Suspense, useEffect } from 'react';
import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status';
import { useCurrentUser } from '../../../hooks/affine/use-current-user';
import { CurrentWorkspaceService } from '../../../modules/workspace/current-workspace';
const SyncAwarenessInnerLoggedIn = () => {
const currentUser = useCurrentUser();
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const currentWorkspace = useLiveData(
useService(CurrentWorkspaceService).currentWorkspace
);
useEffect(() => {
if (currentUser && currentWorkspace) {
@@ -5,18 +5,18 @@ import {
Modal,
} from '@affine/component/ui/modal';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { workspaceManagerAtom } from '@affine/core/modules/workspace';
import { DebugLogger } from '@affine/debug';
import { apis } from '@affine/electron-api';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { _addLocalWorkspace } from '@affine/workspace-impl';
import { WorkspaceManager } from '@toeverything/infra';
import { getCurrentStore } from '@toeverything/infra/atom';
import {
buildShowcaseWorkspace,
initEmptyPage,
} from '@toeverything/infra/blocksuite';
import { useAtomValue } from 'jotai';
import { useService } from '@toeverything/infra/di';
import type { KeyboardEvent } from 'react';
import { useLayoutEffect } from 'react';
import { useCallback, useState } from 'react';
@@ -101,7 +101,7 @@ export const CreateWorkspaceModal = ({
}: ModalProps) => {
const [step, setStep] = useState<CreateWorkspaceStep>();
const t = useAFFiNEI18N();
const workspaceManager = useAtomValue(workspaceManagerAtom);
const workspaceManager = useService(WorkspaceManager);
// todo: maybe refactor using xstate?
useLayoutEffect(() => {
@@ -148,7 +148,7 @@ export const CreateWorkspaceModal = ({
async (name: string) => {
// this will be the last step for web for now
// fix me later
const id = await workspaceManager.createWorkspace(
const { id } = await workspaceManager.createWorkspace(
WorkspaceFlavour.LOCAL,
async workspace => {
workspace.meta.setName(name);
@@ -8,10 +8,10 @@ import {
listHistoryQuery,
recoverDocMutation,
} from '@affine/graphql';
import { globalBlockSuiteSchema } from '@affine/workspace';
import { createAffineCloudBlobStorage } from '@affine/workspace-impl';
import { AffineCloudBlobStorage } from '@affine/workspace-impl';
import { assertEquals } from '@blocksuite/global/utils';
import { Workspace } from '@blocksuite/store';
import { globalBlockSuiteSchema } from '@toeverything/infra';
import { revertUpdate } from '@toeverything/y-indexeddb';
import { useEffect, useMemo } from 'react';
import useSWRImmutable from 'swr/immutable';
@@ -108,7 +108,7 @@ const workspaceMap = new Map<string, Workspace>();
const getOrCreateShellWorkspace = (workspaceId: string) => {
let workspace = workspaceMap.get(workspaceId);
if (!workspace) {
const blobStorage = createAffineCloudBlobStorage(workspaceId);
const blobStorage = new AffineCloudBlobStorage(workspaceId);
workspace = new Workspace({
id: workspaceId,
providerCreators: [],
@@ -7,14 +7,17 @@ import { useIsWorkspaceOwner } from '@affine/core/hooks/affine/use-is-workspace-
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { useBlockSuiteWorkspacePageTitle } from '@affine/core/hooks/use-block-suite-workspace-page-title';
import { useWorkspaceQuota } from '@affine/core/hooks/use-workspace-quota';
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
import { timestampToLocalTime } from '@affine/core/utils';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloseIcon, ToggleCollapseIcon } from '@blocksuite/icons';
import type { Page, Workspace } from '@blocksuite/store';
import {
type Page,
type Workspace as BlockSuiteWorkspace,
} from '@blocksuite/store';
import * as Collapsible from '@radix-ui/react-collapsible';
import type { DialogContentProps } from '@radix-ui/react-dialog';
import { Workspace } from '@toeverything/infra';
import { useService } from '@toeverything/infra/di';
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai';
import {
Fragment,
@@ -29,6 +32,7 @@ import { encodeStateAsUpdate } from 'yjs';
import { currentModeAtom } from '../../../atoms/mode';
import { pageHistoryModalAtom } from '../../../atoms/page-history';
import { timestampToLocalTime } from '../../../utils';
import { BlockSuiteEditor } from '../../blocksuite/block-suite-editor';
import { StyledEditorModeSwitch } from '../../blocksuite/block-suite-mode-switch/style';
import {
@@ -48,7 +52,7 @@ import * as styles from './styles.css';
export interface PageHistoryModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
workspace: Workspace;
workspace: BlockSuiteWorkspace;
pageId: string;
}
@@ -153,13 +157,12 @@ const HistoryEditorPreview = ({
const planPromptClosedAtom = atom(false);
const PlanPrompt = () => {
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const isOwner = useIsWorkspaceOwner(currentWorkspace.meta);
const workspaceQuota = useWorkspaceQuota(currentWorkspace.id);
const workspace = useService(Workspace);
const workspaceQuota = useWorkspaceQuota(workspace.id);
const isProWorkspace = useMemo(() => {
return workspaceQuota?.humanReadable.name.toLowerCase() !== 'free';
}, [workspaceQuota]);
const isOwner = useIsWorkspaceOwner(workspace.meta);
const setSettingModalAtom = useSetAtom(openSettingModalAtom);
const [planPromptClosed, setPlanPromptClosed] = useAtom(planPromptClosedAtom);
@@ -412,7 +415,7 @@ const PageHistoryManager = ({
pageId,
onClose,
}: {
workspace: Workspace;
workspace: BlockSuiteWorkspace;
pageId: string;
onClose: () => void;
}) => {
@@ -536,8 +539,7 @@ export const PageHistoryModal = ({
export const GlobalPageHistoryModal = () => {
const [{ open, pageId }, setState] = useAtom(pageHistoryModalAtom);
const workspace = useAtomValue(waitForCurrentWorkspaceAtom);
const workspace = useService(Workspace);
const handleOpenChange = useCallback(
(open: boolean) => {
setState(prev => ({
@@ -3,15 +3,15 @@ import { openQuotaModalAtom, openSettingModalAtom } from '@affine/core/atoms';
import { useIsWorkspaceOwner } from '@affine/core/hooks/affine/use-is-workspace-owner';
import { useUserQuota } from '@affine/core/hooks/use-quota';
import { useWorkspaceQuota } from '@affine/core/hooks/use-workspace-quota';
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useService, Workspace } from '@toeverything/infra';
import bytes from 'bytes';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import { useAtom, useSetAtom } from 'jotai';
import { useCallback, useEffect, useMemo } from 'react';
export const CloudQuotaModal = () => {
const t = useAFFiNEI18N();
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const currentWorkspace = useService(Workspace);
const [open, setOpen] = useAtom(openQuotaModalAtom);
const workspaceQuota = useWorkspaceQuota(currentWorkspace.id);
const isOwner = useIsWorkspaceOwner(currentWorkspace.meta);
@@ -1,13 +1,13 @@
import { ConfirmModal } from '@affine/component/ui/modal';
import { openQuotaModalAtom } from '@affine/core/atoms';
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useAtom, useAtomValue } from 'jotai';
import { useService, Workspace } from '@toeverything/infra';
import { useAtom } from 'jotai';
import { useCallback, useEffect } from 'react';
export const LocalQuotaModal = () => {
const t = useAFFiNEI18N();
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const currentWorkspace = useService(Workspace);
const [open, setOpen] = useAtom(openQuotaModalAtom);
const onConfirm = useCallback(() => {
@@ -1,8 +1,8 @@
import { WorkspaceDetailSkeleton } from '@affine/component/setting-components';
import { Modal, type ModalProps } from '@affine/component/ui/modal';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { WorkspaceMetadata } from '@affine/workspace/metadata';
import { ContactWithUsIcon } from '@blocksuite/icons';
import type { WorkspaceMetadata } from '@toeverything/infra';
import { debounce } from 'lodash-es';
import { Suspense, useCallback, useLayoutEffect, useRef } from 'react';
@@ -8,17 +8,19 @@ import { useIsWorkspaceOwner } from '@affine/core/hooks/affine/use-is-workspace-
import { useWorkspaceBlobObjectUrl } from '@affine/core/hooks/use-workspace-blob';
import { useWorkspaceAvailableFeatures } from '@affine/core/hooks/use-workspace-features';
import { useWorkspaceInfo } from '@affine/core/hooks/use-workspace-info';
import {
waitForCurrentWorkspaceAtom,
workspaceListAtom,
} from '@affine/core/modules/workspace';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { WorkspaceMetadata } from '@affine/workspace';
import { Logo1Icon } from '@blocksuite/icons';
import {
Workspace,
WorkspaceManager,
type WorkspaceMetadata,
} from '@toeverything/infra';
import { useService } from '@toeverything/infra/di';
import { useLiveData } from '@toeverything/infra/livedata';
import clsx from 'clsx';
import { useAtom, useAtomValue } from 'jotai/react';
import { useAtom } from 'jotai/react';
import { type ReactElement, Suspense, useCallback, useMemo } from 'react';
import { authAtom } from '../../../../atoms';
@@ -188,7 +190,9 @@ export const WorkspaceList = ({
selectedWorkspaceId: string | null;
activeSubTab: WorkspaceSubTab;
}) => {
const workspaces = useAtomValue(workspaceListAtom);
const workspaces = useLiveData(
useService(WorkspaceManager).list.workspaceList
);
return (
<>
{workspaces.map(workspace => {
@@ -236,7 +240,7 @@ const WorkspaceListItem = ({
const information = useWorkspaceInfo(meta);
const avatarUrl = useWorkspaceBlobObjectUrl(meta, information?.avatar);
const name = information?.name ?? UNTITLED_WORKSPACE_NAME;
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const currentWorkspace = useService(Workspace);
const isCurrent = currentWorkspace.id === meta.id;
const t = useAFFiNEI18N();
const isOwner = useIsWorkspaceOwner(meta);
@@ -8,7 +8,7 @@ import {
} from '@affine/core/hooks/use-workspace-features';
import { FeatureType } from '@affine/graphql';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { WorkspaceMetadata } from '@affine/workspace/metadata';
import type { WorkspaceMetadata } from '@toeverything/infra';
import { useAtom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
import { Suspense, useCallback, useState } from 'react';
@@ -1,4 +1,4 @@
import type { WorkspaceMetadata } from '@affine/workspace/metadata';
import type { WorkspaceMetadata } from '@toeverything/infra';
import { useIsWorkspaceOwner } from '../../../../hooks/affine/use-is-workspace-owner';
import { ExperimentalFeatures } from './experimental-features';
@@ -8,7 +8,7 @@ import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { WorkspaceMetadata } from '@affine/workspace/metadata';
import type { WorkspaceMetadata } from '@toeverything/infra';
import { useCallback, useState } from 'react';
import * as styles from './style.css';
@@ -2,15 +2,12 @@ import { pushNotificationAtom } from '@affine/component/notification-center';
import { SettingRow } from '@affine/component/setting-components';
import { ConfirmModal } from '@affine/component/ui/modal';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import {
currentWorkspaceAtom,
workspaceListAtom,
workspaceManagerAtom,
} from '@affine/core/modules/workspace';
import { WorkspaceSubPath } from '@affine/core/shared';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ArrowRightSmallIcon } from '@blocksuite/icons';
import { useAtomValue, useSetAtom } from 'jotai';
import { Workspace, WorkspaceManager } from '@toeverything/infra';
import { useService } from '@toeverything/infra/di';
import { useLiveData } from '@toeverything/infra/livedata';
import { useSetAtom } from 'jotai';
import { useCallback, useState } from 'react';
import { openSettingModalAtom } from '../../../../../../atoms';
@@ -18,6 +15,7 @@ import {
RouteLogic,
useNavigateHelper,
} from '../../../../../../hooks/use-navigate-helper';
import { WorkspaceSubPath } from '../../../../../../shared';
import type { WorkspaceSettingDetailProps } from '../types';
import { WorkspaceDeleteModal } from './delete';
@@ -35,9 +33,9 @@ export const DeleteLeaveWorkspace = ({
const [showLeave, setShowLeave] = useState(false);
const setSettingModal = useSetAtom(openSettingModalAtom);
const workspaceManager = useAtomValue(workspaceManagerAtom);
const workspaceList = useAtomValue(workspaceListAtom);
const currentWorkspace = useAtomValue(currentWorkspaceAtom);
const workspaceManager = useService(WorkspaceManager);
const workspaceList = useLiveData(workspaceManager.list.workspaceList);
const currentWorkspace = useService(Workspace);
const pushNotification = useSetAtom(pushNotificationAtom);
const onLeaveOrDelete = useCallback(() => {
@@ -2,17 +2,17 @@ import { SettingRow } from '@affine/component/setting-components';
import { Button } from '@affine/component/ui/button';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { useWorkspaceInfo } from '@affine/core/hooks/use-workspace-info';
import { workspaceManagerAtom } from '@affine/core/modules/workspace';
import { WorkspaceSubPath } from '@affine/core/shared';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { Workspace } from '@affine/workspace';
import { useAtomValue, useSetAtom } from 'jotai';
import { type Workspace, WorkspaceManager } from '@toeverything/infra';
import { useService } from '@toeverything/infra/di';
import { useSetAtom } from 'jotai';
import { useState } from 'react';
import { openSettingModalAtom } from '../../../../../atoms';
import { useNavigateHelper } from '../../../../../hooks/use-navigate-helper';
import { WorkspaceSubPath } from '../../../../../shared';
import { EnableAffineCloudModal } from '../../../enable-affine-cloud-modal';
import { TmpDisableAffineCloudModal } from '../../../tmp-disable-affine-cloud-modal';
import type { WorkspaceSettingDetailProps } from './types';
@@ -29,7 +29,7 @@ export const EnableCloudPanel = ({
const { openPage } = useNavigateHelper();
const workspaceManager = useAtomValue(workspaceManagerAtom);
const workspaceManager = useService(WorkspaceManager);
const workspaceInfo = useWorkspaceInfo(workspaceMetadata);
const setSettingModal = useSetAtom(openSettingModalAtom);
@@ -4,7 +4,7 @@ import { Button } from '@affine/component/ui/button';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { apis } from '@affine/electron-api';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { Workspace, WorkspaceMetadata } from '@affine/workspace';
import type { Workspace, WorkspaceMetadata } from '@toeverything/infra';
import { useSetAtom } from 'jotai';
import { useState } from 'react';
@@ -9,9 +9,9 @@ import { useWorkspaceStatus } from '@affine/core/hooks/use-workspace-status';
import { validateAndReduceImage } from '@affine/core/utils/reduce-image';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { Workspace } from '@affine/workspace';
import { SyncPeerStep } from '@affine/workspace';
import { CameraIcon } from '@blocksuite/icons';
import type { Workspace } from '@toeverything/infra';
import { SyncPeerStep } from '@toeverything/infra';
import { useSetAtom } from 'jotai';
import {
type KeyboardEvent,
@@ -4,7 +4,7 @@ import { Button } from '@affine/component/ui/button';
import { Tooltip } from '@affine/component/ui/tooltip';
import { apis, events } from '@affine/electron-api';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { WorkspaceMetadata } from '@affine/workspace/metadata';
import type { WorkspaceMetadata } from '@toeverything/infra';
import { useMemo } from 'react';
import { useCallback, useEffect, useState } from 'react';
@@ -1,4 +1,4 @@
import type { WorkspaceMetadata } from '@affine/workspace/metadata';
import type { WorkspaceMetadata } from '@toeverything/infra';
export interface WorkspaceSettingDetailProps {
isOwner: boolean;
@@ -1,9 +1,8 @@
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { workspaceManagerAtom } from '@affine/core/modules/workspace';
import { WorkspaceFlavour } from '@affine/env/workspace';
import type { Workspace } from '@affine/workspace';
import type { Page } from '@blocksuite/store';
import { useAtomValue } from 'jotai';
import { type Workspace, WorkspaceManager } from '@toeverything/infra';
import { useService } from '@toeverything/infra';
import { useState } from 'react';
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
@@ -25,7 +24,7 @@ export const SharePageButton = ({
const { openPage } = useNavigateHelper();
const workspaceManager = useAtomValue(workspaceManagerAtom);
const workspaceManager = useService(WorkspaceManager);
const handleConfirm = useAsyncCallback(async () => {
if (workspace.flavour !== WorkspaceFlavour.LOCAL) {
@@ -3,9 +3,9 @@ import { Divider } from '@affine/component/ui/divider';
import { Menu } from '@affine/component/ui/menu';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { WorkspaceMetadata } from '@affine/workspace';
import { WebIcon } from '@blocksuite/icons';
import type { Page } from '@blocksuite/store';
import type { WorkspaceMetadata } from '@toeverything/infra';
import clsx from 'clsx';
import { useIsSharedPage } from '../../../../hooks/affine/use-is-shared-page';
@@ -1,11 +1,11 @@
import { FavoriteTag } from '@affine/core/components/page-list';
import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-helper';
import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta';
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
import { toast } from '@affine/core/utils';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertExists } from '@blocksuite/global/utils';
import { useAtomValue } from 'jotai';
import { Workspace } from '@toeverything/infra';
import { useService } from '@toeverything/infra/di';
import { useCallback } from 'react';
export interface FavoriteButtonProps {
@@ -14,7 +14,7 @@ export interface FavoriteButtonProps {
export const useFavorite = (pageId: string) => {
const t = useAFFiNEI18N();
const workspace = useAtomValue(waitForCurrentWorkspaceAtom);
const workspace = useService(Workspace);
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
const currentPage = blockSuiteWorkspace.getPage(pageId);
assertExists(currentPage);
@@ -1,3 +1,4 @@
import { toast } from '@affine/component';
import {
Menu,
MenuIcon,
@@ -11,8 +12,6 @@ import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-sui
import { useExportPage } from '@affine/core/hooks/affine/use-export-page';
import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper';
import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta';
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
import { toast } from '@affine/core/utils';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertExists } from '@blocksuite/global/utils';
@@ -26,6 +25,7 @@ import {
ImportIcon,
PageIcon,
} from '@blocksuite/icons';
import { useService, Workspace } from '@toeverything/infra';
import { useAtomValue } from 'jotai';
import { useCallback, useState } from 'react';
@@ -46,8 +46,7 @@ export const PageHeaderMenuButton = ({
}: PageMenuProps) => {
const t = useAFFiNEI18N();
// fixme(himself65): remove these hooks ASAP
const workspace = useAtomValue(waitForCurrentWorkspaceAtom);
const workspace = useService(Workspace);
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
const currentPage = blockSuiteWorkspace.getPage(pageId);
assertExists(currentPage);
@@ -3,57 +3,46 @@
*/
import 'fake-indexeddb/auto';
import type { CollectionService } from '@affine/core/modules/collection';
import type { Collection } from '@affine/env/filter';
import { renderHook } from '@testing-library/react';
import { atom } from 'jotai';
import { atomWithObservable } from 'jotai/utils';
import { LiveData } from '@toeverything/infra';
import { BehaviorSubject } from 'rxjs';
import { expect, test } from 'vitest';
import { createDefaultFilter, vars } from '../filter/vars';
import {
type CollectionsCRUD,
useCollectionManager,
} from '../use-collection-manager';
import { useCollectionManager } from '../use-collection-manager';
const defaultMeta = { tags: { options: [] } };
const collectionsSubject = new BehaviorSubject<Collection[]>([]);
const baseAtom = atomWithObservable<Collection[]>(
() => {
return collectionsSubject;
},
{
initialValue: [],
}
);
const mockAtom = atom(get => {
return {
collections: get(baseAtom),
addCollection: (...collections) => {
const prev = collectionsSubject.value;
collectionsSubject.next([...collections, ...prev]);
},
deleteCollection: (...ids) => {
const prev = collectionsSubject.value;
collectionsSubject.next(prev.filter(v => !ids.includes(v.id)));
},
updateCollection: (id, updater) => {
const prev = collectionsSubject.value;
collectionsSubject.next(
prev.map(v => {
if (v.id === id) {
return updater(v);
}
return v;
})
);
},
} satisfies CollectionsCRUD;
});
const mockWorkspaceCollectionService = {
collections: LiveData.from(collectionsSubject, []),
addCollection: (...collections) => {
const prev = collectionsSubject.value;
collectionsSubject.next([...collections, ...prev]);
},
deleteCollection: (...ids) => {
const prev = collectionsSubject.value;
collectionsSubject.next(prev.filter(v => !ids.includes(v.id)));
},
updateCollection: (id, updater) => {
const prev = collectionsSubject.value;
collectionsSubject.next(
prev.map(v => {
if (v.id === id) {
return updater(v);
}
return v;
})
);
},
} as CollectionService;
test('useAllPageSetting', async () => {
const settingHook = renderHook(() => useCollectionManager(mockAtom));
const settingHook = renderHook(() =>
useCollectionManager(mockWorkspaceCollectionService)
);
const prevCollection = settingHook.result.current.currentCollection;
expect(settingHook.result.current.savedCollections).toEqual([]);
settingHook.result.current.updateCollection({
@@ -1,9 +1,8 @@
import { collectionsCRUDAtom } from '@affine/core/atoms/collections';
import { useDeleteCollectionInfo } from '@affine/core/hooks/affine/use-delete-collection-info';
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
import type { Collection, DeleteCollectionInfo } from '@affine/env/filter';
import { Trans } from '@affine/i18n';
import { useAtomValue } from 'jotai';
import { useService } from '@toeverything/infra';
import { Workspace } from '@toeverything/infra';
import {
type ReactElement,
useCallback,
@@ -12,6 +11,7 @@ import {
useState,
} from 'react';
import { CollectionService } from '../../../modules/collection';
import { ListFloatingToolbar } from '../components/list-floating-toolbar';
import { collectionHeaderColsDef } from '../header-col-def';
import { CollectionOperationCell } from '../operation-cell';
@@ -69,8 +69,8 @@ export const VirtualizedCollectionList = ({
const [selectedCollectionIds, setSelectedCollectionIds] = useState<string[]>(
[]
);
const setting = useCollectionManager(collectionsCRUDAtom);
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const setting = useCollectionManager(useService(CollectionService));
const currentWorkspace = useService(Workspace);
const info = useDeleteCollectionInfo();
const collectionOperations = useCollectionOperationsRenderer({
@@ -1,13 +1,14 @@
import { Button } from '@affine/component';
import { collectionsCRUDAtom } from '@affine/core/atoms/collections';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
import type { Collection, Tag } from '@affine/env/filter';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ViewLayersIcon } from '@blocksuite/icons';
import { useService } from '@toeverything/infra/di';
import { nanoid } from 'nanoid';
import { useCallback, useMemo } from 'react';
import { CollectionService } from '../../../modules/collection';
import { createTagFilter } from '../filter/utils';
import {
createEmptyCollection,
@@ -24,7 +25,7 @@ import { PageListNewPageButton } from './page-list-new-page-button';
export const PageListHeader = ({ workspaceId }: { workspaceId: string }) => {
const t = useAFFiNEI18N();
const setting = useCollectionManager(collectionsCRUDAtom);
const setting = useCollectionManager(useService(CollectionService));
const { jumpToCollections } = useNavigateHelper();
const handleJumpToCollections = useCallback(() => {
@@ -74,14 +75,16 @@ export const CollectionPageListHeader = ({
workspaceId: string;
}) => {
const t = useAFFiNEI18N();
const setting = useCollectionManager(collectionsCRUDAtom);
const setting = useCollectionManager(useService(CollectionService));
const { jumpToCollections } = useNavigateHelper();
const handleJumpToCollections = useCallback(() => {
jumpToCollections(workspaceId);
}, [jumpToCollections, workspaceId]);
const { updateCollection } = useCollectionManager(collectionsCRUDAtom);
const { updateCollection } = useCollectionManager(
useService(CollectionService)
);
const { node, open } = useEditCollection(config);
const handleAddPage = useAsyncCallback(async () => {
@@ -121,7 +124,7 @@ export const TagPageListHeader = ({
}) => {
const t = useAFFiNEI18N();
const { jumpToTags, jumpToCollection } = useNavigateHelper();
const setting = useCollectionManager(collectionsCRUDAtom);
const setting = useCollectionManager(useService(CollectionService));
const { open, node } = useEditCollectionName({
title: t['com.affine.editCollection.saveCollection'](),
showTips: true,
@@ -1,5 +1,5 @@
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
import { useAtomValue } from 'jotai';
import { Workspace } from '@toeverything/infra';
import { useService } from '@toeverything/infra/di';
import type { PropsWithChildren } from 'react';
import { usePageHelper } from '../../blocksuite/block-suite-page-list/utils';
@@ -16,7 +16,7 @@ export const PageListNewPageButton = ({
size?: 'small' | 'default';
testId?: string;
}>) => {
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const currentWorkspace = useService(Workspace);
const { importFile, createEdgeless, createPage } = usePageHelper(
currentWorkspace.blockSuiteWorkspace
);
@@ -2,12 +2,12 @@ import { toast } from '@affine/component';
import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-helper';
import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper';
import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta';
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
import type { Collection } from '@affine/env/filter';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { PageMeta, Tag } from '@blocksuite/store';
import { useAtomValue } from 'jotai';
import { useService } from '@toeverything/infra';
import { Workspace } from '@toeverything/infra';
import { useCallback, useMemo, useRef, useState } from 'react';
import { usePageHelper } from '../../blocksuite/block-suite-page-list/utils';
@@ -27,7 +27,7 @@ import {
} from './page-list-header';
const usePageOperationsRenderer = () => {
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const currentWorkspace = useService(Workspace);
const { setTrashModal } = useTrashModalHelper(
currentWorkspace.blockSuiteWorkspace
);
@@ -89,7 +89,7 @@ export const VirtualizedPageList = ({
const listRef = useRef<ItemListHandle>(null);
const [showFloatingToolbar, setShowFloatingToolbar] = useState(false);
const [selectedPageIds, setSelectedPageIds] = useState<string[]>([]);
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const currentWorkspace = useService(Workspace);
const pageMetas = useBlockSuitePageMeta(currentWorkspace.blockSuiteWorkspace);
const pageOperations = usePageOperationsRenderer();
const { isPreferredEdgeless } = usePageHelper(
@@ -1,7 +1,7 @@
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
import { Trans } from '@affine/i18n';
import type { Tag } from '@blocksuite/store';
import { useAtomValue } from 'jotai';
import { useService } from '@toeverything/infra';
import { Workspace } from '@toeverything/infra';
import { useCallback, useMemo, useRef, useState } from 'react';
import { ListFloatingToolbar } from '../components/list-floating-toolbar';
@@ -26,7 +26,7 @@ export const VirtualizedTagList = ({
const listRef = useRef<ItemListHandle>(null);
const [showFloatingToolbar, setShowFloatingToolbar] = useState(false);
const [selectedTagIds, setSelectedTagIds] = useState<string[]>([]);
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const currentWorkspace = useService(Workspace);
const filteredSelectedTagIds = useMemo(() => {
const ids = tags.map(tag => tag.id);
@@ -1,11 +1,8 @@
import type {
Collection,
DeleteCollectionInfo,
Filter,
VariableMap,
} from '@affine/env/filter';
import type { CollectionService } from '@affine/core/modules/collection';
import type { Collection, Filter, VariableMap } from '@affine/env/filter';
import type { PageMeta } from '@blocksuite/store';
import { type Atom, useAtom, useAtomValue } from 'jotai';
import { useLiveData } from '@toeverything/infra/livedata';
import { useAtom, useAtomValue } from 'jotai';
import { atomWithReset } from 'jotai/utils';
import { useCallback } from 'react';
import { NIL } from 'uuid';
@@ -32,47 +29,28 @@ export const currentCollectionAtom = atomWithReset<string>(NIL);
export type Updater<T> = (value: T) => T;
export type CollectionUpdater = Updater<Collection>;
export type CollectionsCRUD = {
addCollection: (...collections: Collection[]) => void;
collections: Collection[];
updateCollection: (id: string, updater: CollectionUpdater) => void;
deleteCollection: (info: DeleteCollectionInfo, ...ids: string[]) => void;
};
export type CollectionsCRUDAtom = Atom<
Promise<CollectionsCRUD> | CollectionsCRUD
>;
export const useSavedCollections = (collectionAtom: CollectionsCRUDAtom) => {
const [{ collections, addCollection, deleteCollection, updateCollection }] =
useAtom(collectionAtom);
export const useSavedCollections = (collectionService: CollectionService) => {
const addPage = useCallback(
(collectionId: string, pageId: string) => {
updateCollection(collectionId, old => {
collectionService.updateCollection(collectionId, old => {
return {
...old,
allowList: [pageId, ...(old.allowList ?? [])],
};
});
},
[updateCollection]
[collectionService]
);
return {
collections,
addCollection,
updateCollection,
deleteCollection,
collectionService,
addPage,
};
};
export const useCollectionManager = (collectionsAtom: CollectionsCRUDAtom) => {
const {
collections,
updateCollection,
addCollection,
deleteCollection,
addPage,
} = useSavedCollections(collectionsAtom);
export const useCollectionManager = (collectionService: CollectionService) => {
const collections = useLiveData(collectionService.collections);
const { addPage } = useSavedCollections(collectionService);
const currentCollectionId = useAtomValue(currentCollectionAtom);
const [defaultCollection, updateDefaultCollection] = useAtom(
defaultCollectionAtom
@@ -82,10 +60,10 @@ export const useCollectionManager = (collectionsAtom: CollectionsCRUDAtom) => {
if (collection.id === NIL) {
updateDefaultCollection(collection);
} else {
updateCollection(collection.id, () => collection);
collectionService.updateCollection(collection.id, () => collection);
}
},
[updateDefaultCollection, updateCollection]
[updateDefaultCollection, collectionService]
);
const setTemporaryFilter = useCallback(
(filterList: Filter[]) => {
@@ -108,9 +86,10 @@ export const useCollectionManager = (collectionsAtom: CollectionsCRUDAtom) => {
isDefault: currentCollectionId === NIL,
// actions
createCollection: addCollection,
createCollection: collectionService.addCollection.bind(collectionService),
updateCollection: update,
deleteCollection,
deleteCollection:
collectionService.deleteCollection.bind(collectionService),
addPage,
setTemporaryFilter,
};
@@ -1,8 +1,9 @@
import { allPageModeSelectAtom } from '@affine/core/atoms';
import { collectionsCRUDAtom } from '@affine/core/atoms/collections';
import { usePageHelper } from '@affine/core/components/blocksuite/block-suite-page-list/utils';
import { CollectionService } from '@affine/core/modules/collection';
import type { BlockSuiteWorkspace } from '@affine/core/shared';
import type { PageMeta } from '@blocksuite/store';
import { useService } from '@toeverything/infra/di';
import { useAtomValue } from 'jotai';
import { useMemo } from 'react';
@@ -19,8 +20,9 @@ export const useFilteredPageMetas = (
) => {
const { isPreferredEdgeless } = usePageHelper(workspace);
const pageMode = useAtomValue(allPageModeSelectAtom);
const { currentCollection, isDefault } =
useCollectionManager(collectionsCRUDAtom);
const { currentCollection, isDefault } = useCollectionManager(
useService(CollectionService)
);
const filteredPageMetas = useMemo(
() =>
@@ -1,4 +1,5 @@
import { Button, Tooltip } from '@affine/component';
import type { CollectionService } from '@affine/core/modules/collection';
import type { DeleteCollectionInfo, PropertiesMeta } from '@affine/env/filter';
import type { GetPageInfoById } from '@affine/env/page-info';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
@@ -6,10 +7,7 @@ import { ViewLayersIcon } from '@blocksuite/icons';
import clsx from 'clsx';
import { useState } from 'react';
import {
type CollectionsCRUDAtom,
useCollectionManager,
} from '../use-collection-manager';
import { useCollectionManager } from '../use-collection-manager';
import * as styles from './collection-bar.css';
import {
type AllPageListConfig,
@@ -20,16 +18,16 @@ import { useActions } from './use-action';
interface CollectionBarProps {
getPageInfo: GetPageInfoById;
propertiesMeta: PropertiesMeta;
collectionsAtom: CollectionsCRUDAtom;
collectionService: CollectionService;
backToAll: () => void;
allPageListConfig: AllPageListConfig;
info: DeleteCollectionInfo;
}
export const CollectionBar = (props: CollectionBarProps) => {
const { collectionsAtom } = props;
const { collectionService } = props;
const t = useAFFiNEI18N();
const setting = useCollectionManager(collectionsAtom);
const setting = useCollectionManager(collectionService);
const collection = setting.currentCollection;
const [open, setOpen] = useState(false);
const actions = useActions({
@@ -3,15 +3,11 @@ import {
useBlockSuitePageMeta,
usePageMetaHelper,
} from '@affine/core/hooks/use-block-suite-page-meta';
import {
currentWorkspaceAtom,
waitForCurrentWorkspaceAtom,
} from '@affine/core/modules/workspace';
import { WorkspaceSubPath } from '@affine/core/shared';
import type { Collection } from '@affine/env/filter';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { EdgelessIcon, PageIcon, ViewLayersIcon } from '@blocksuite/icons';
import type { Page, PageMeta } from '@blocksuite/store';
import type { PageMeta } from '@blocksuite/store';
import { Workspace } from '@toeverything/infra';
import { getCurrentStore } from '@toeverything/infra/atom';
import {
type AffineCommand,
@@ -19,19 +15,17 @@ import {
type CommandCategory,
PreconditionStrategy,
} from '@toeverything/infra/command';
import { useService } from '@toeverything/infra/di';
import { commandScore } from 'cmdk';
import { atom, useAtomValue } from 'jotai';
import { groupBy } from 'lodash-es';
import { useCallback, useEffect, useMemo, useState } from 'react';
import {
openQuickSearchModalAtom,
pageSettingsAtom,
recentPageIdsBaseAtom,
} from '../../../atoms';
import { collectionsCRUDAtom } from '../../../atoms/collections';
import { pageSettingsAtom, recentPageIdsBaseAtom } from '../../../atoms';
import { currentPageIdAtom } from '../../../atoms/mode';
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
import { CollectionService } from '../../../modules/collection';
import { WorkspaceSubPath } from '../../../shared';
import { usePageHelper } from '../../blocksuite/block-suite-page-list/utils';
import type { CMDKCommand, CommandContext } from './types';
@@ -47,41 +41,6 @@ export function removeDoubleQuotes(str?: string): string | undefined {
export const cmdkQueryAtom = atom('');
export const cmdkValueAtom = atom('');
// like currentWorkspaceAtom, but not throw error
const safeCurrentPageAtom = atom<Promise<Page | undefined>>(async get => {
const currentWorkspace = get(currentWorkspaceAtom);
if (!currentWorkspace) {
return;
}
const currentPageId = get(currentPageIdAtom);
if (!currentPageId) {
return;
}
const page = currentWorkspace.blockSuiteWorkspace.getPage(currentPageId);
if (!page) {
return;
}
if (!page.loaded) {
await page.waitForLoaded();
}
return page;
});
export const commandContextAtom = atom<Promise<CommandContext>>(async get => {
const currentPage = await get(safeCurrentPageAtom);
const pageSettings = get(pageSettingsAtom);
return {
currentPage,
pageMode: currentPage ? pageSettings[currentPage.id]?.mode : undefined,
};
});
function filterCommandByContext(
command: AffineCommand,
context: CommandContext
@@ -96,7 +55,7 @@ function filterCommandByContext(
return context.pageMode === 'page';
}
if (command.preconditionStrategy === PreconditionStrategy.InPaperOrEdgeless) {
return !!context.currentPage;
return !!context.pageMode;
}
if (command.preconditionStrategy === PreconditionStrategy.Never) {
return false;
@@ -107,27 +66,16 @@ function filterCommandByContext(
return true;
}
let quickSearchOpenCounter = 0;
const openCountAtom = atom(get => {
if (get(openQuickSearchModalAtom)) {
quickSearchOpenCounter++;
}
return quickSearchOpenCounter;
});
export const filteredAffineCommands = atom(async get => {
const context = await get(commandContextAtom);
// reset when modal open
get(openCountAtom);
function getAllCommand(context: CommandContext) {
const commands = AffineCommandRegistry.getAll();
return commands.filter(command => {
return filterCommandByContext(command, context);
});
});
}
const useWorkspacePages = () => {
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const pages = useBlockSuitePageMeta(currentWorkspace.blockSuiteWorkspace);
const workspace = useService(Workspace);
const pages = useBlockSuitePageMeta(workspace.blockSuiteWorkspace);
return pages;
};
@@ -153,6 +101,7 @@ export const pageToCommand = (
store: ReturnType<typeof getCurrentStore>,
navigationHelper: ReturnType<typeof useNavigateHelper>,
t: ReturnType<typeof useAFFiNEI18N>,
workspace: Workspace,
label?: {
title: string;
subTitle?: string;
@@ -160,7 +109,6 @@ export const pageToCommand = (
blockId?: string
): CMDKCommand => {
const pageMode = store.get(pageSettingsAtom)?.[page.id]?.mode;
const currentWorkspace = store.get(currentWorkspaceAtom);
const title = page.title || t['Untitled']();
const commandLabel = label || {
@@ -186,18 +134,14 @@ export const pageToCommand = (
originalValue: title,
category: category,
run: () => {
if (!currentWorkspace) {
if (!workspace) {
console.error('current workspace not found');
return;
}
if (blockId) {
return navigationHelper.jumpToPageBlock(
currentWorkspace.id,
page.id,
blockId
);
return navigationHelper.jumpToPageBlock(workspace.id, page.id, blockId);
}
return navigationHelper.jumpToPage(currentWorkspace.id, page.id);
return navigationHelper.jumpToPage(workspace.id, page.id);
},
icon: pageMode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />,
timestamp: page.updatedDate,
@@ -212,7 +156,7 @@ export const usePageCommands = () => {
const recentPages = useRecentPages();
const pages = useWorkspacePages();
const store = getCurrentStore();
const workspace = useAtomValue(waitForCurrentWorkspaceAtom);
const workspace = useService(Workspace);
const pageHelper = usePageHelper(workspace.blockSuiteWorkspace);
const pageMetaHelper = usePageMetaHelper(workspace.blockSuiteWorkspace);
const query = useAtomValue(cmdkQueryAtom);
@@ -241,7 +185,14 @@ export const usePageCommands = () => {
let results: CMDKCommand[] = [];
if (query.trim() === '') {
results = recentPages.map(page => {
return pageToCommand('affine:recent', page, store, navigationHelper, t);
return pageToCommand(
'affine:recent',
page,
store,
navigationHelper,
t,
workspace
);
});
} else {
// queried pages that has matched contents
@@ -283,6 +234,7 @@ export const usePageCommands = () => {
store,
navigationHelper,
t,
workspace,
label,
blockId
);
@@ -334,27 +286,26 @@ export const usePageCommands = () => {
}
return results;
}, [
pageHelper,
pageMetaHelper,
navigationHelper,
pages,
searchTime,
query,
recentPages,
store,
navigationHelper,
t,
workspace.blockSuiteWorkspace,
searchTime,
workspace,
pages,
pageHelper,
pageMetaHelper,
]);
};
export const collectionToCommand = (
collection: Collection,
store: ReturnType<typeof getCurrentStore>,
navigationHelper: ReturnType<typeof useNavigateHelper>,
selectCollection: (id: string) => void,
t: ReturnType<typeof useAFFiNEI18N>
t: ReturnType<typeof useAFFiNEI18N>,
workspace: Workspace
): CMDKCommand => {
const currentWorkspace = store.get(currentWorkspaceAtom);
const label = collection.name || t['Untitled']();
const category = 'affine:collections';
return {
@@ -372,11 +323,7 @@ export const collectionToCommand = (
originalValue: label,
category: category,
run: () => {
if (!currentWorkspace) {
console.error('current workspace not found');
return;
}
navigationHelper.jumpToSubPath(currentWorkspace.id, WorkspaceSubPath.ALL);
navigationHelper.jumpToSubPath(workspace.id, WorkspaceSubPath.ALL);
selectCollection(collection.id);
},
icon: <ViewLayersIcon />,
@@ -385,12 +332,13 @@ export const collectionToCommand = (
export const useCollectionsCommands = () => {
// todo: considering collections for searching pages
const { savedCollections } = useCollectionManager(collectionsCRUDAtom);
const store = getCurrentStore();
const { savedCollections } = useCollectionManager(
useService(CollectionService)
);
const query = useAtomValue(cmdkQueryAtom);
const navigationHelper = useNavigateHelper();
const t = useAFFiNEI18N();
const workspace = useAtomValue(waitForCurrentWorkspaceAtom);
const workspace = useService(Workspace);
const selectCollection = useCallback(
(id: string) => {
navigationHelper.jumpToCollection(workspace.id, id);
@@ -405,22 +353,39 @@ export const useCollectionsCommands = () => {
results = savedCollections.map(collection => {
const command = collectionToCommand(
collection,
store,
navigationHelper,
selectCollection,
t
t,
workspace
);
return command;
});
return results;
}
}, [query, savedCollections, store, navigationHelper, selectCollection, t]);
}, [
query,
savedCollections,
navigationHelper,
selectCollection,
t,
workspace,
]);
};
export const useCMDKCommandGroups = () => {
const pageCommands = usePageCommands();
const collectionCommands = useCollectionsCommands();
const affineCommands = useAtomValue(filteredAffineCommands);
const currentPageId = useAtomValue(currentPageIdAtom);
const pageSettings = useAtomValue(pageSettingsAtom);
const currentPageMode = currentPageId
? pageSettings[currentPageId]?.mode
: undefined;
const affineCommands = useMemo(() => {
return getAllCommand({
pageMode: currentPageMode,
});
}, [currentPageMode]);
return useMemo(() => {
const commands = [
@@ -1,8 +1,6 @@
import type { Page } from '@blocksuite/store';
import type { CommandCategory } from '@toeverything/infra/command';
export interface CommandContext {
currentPage: Page | undefined;
pageMode: 'page' | 'edgeless' | undefined;
}
@@ -2,22 +2,25 @@ import { Button } from '@affine/component/ui/button';
import { ConfirmModal } from '@affine/component/ui/modal';
import { Tooltip } from '@affine/component/ui/tooltip';
import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta';
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
import { WorkspaceSubPath } from '@affine/core/shared';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertExists } from '@blocksuite/global/utils';
import { DeleteIcon, ResetIcon } from '@blocksuite/icons';
import { useAtomValue } from 'jotai';
import { useService } from '@toeverything/infra/di';
import { useLiveData } from '@toeverything/infra/livedata';
import { useCallback, useState } from 'react';
import { useAppSettingHelper } from '../../../hooks/affine/use-app-setting-helper';
import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper';
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
import { CurrentWorkspaceService } from '../../../modules/workspace/current-workspace';
import { WorkspaceSubPath } from '../../../shared';
import { toast } from '../../../utils';
import * as styles from './styles.css';
export const TrashPageFooter = ({ pageId }: { pageId: string }) => {
const workspace = useAtomValue(waitForCurrentWorkspaceAtom);
const workspace = useLiveData(
useService(CurrentWorkspaceService).currentWorkspace
);
assertExists(workspace);
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
const pageMeta = useBlockSuitePageMeta(blockSuiteWorkspace).find(
@@ -7,21 +7,22 @@ import {
filterPage,
stopPropagation,
useCollectionManager,
useSavedCollections,
} from '@affine/core/components/page-list';
import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta';
import { CollectionService } from '@affine/core/modules/collection';
import type { Collection, DeleteCollectionInfo } from '@affine/env/filter';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { MoreHorizontalIcon, ViewLayersIcon } from '@blocksuite/icons';
import type { PageMeta, Workspace } from '@blocksuite/store';
import { useDroppable } from '@dnd-kit/core';
import * as Collapsible from '@radix-ui/react-collapsible';
import { useService } from '@toeverything/infra';
import { useLiveData } from '@toeverything/infra/livedata';
import { useCallback, useMemo, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { collectionsCRUDAtom } from '../../../../atoms/collections';
import { useAllPageListConfig } from '../../../../hooks/affine/use-all-page-list-config';
import { getDropItemId } from '../../../../hooks/affine/use-sidebar-drag';
import { useBlockSuitePageMeta } from '../../../../hooks/use-block-suite-page-meta';
import type { CollectionsListProps } from '../index';
import { Page } from './page';
import * as styles from './styles.css';
@@ -39,7 +40,7 @@ const CollectionRenderer = ({
}) => {
const [collapsed, setCollapsed] = useState(true);
const [open, setOpen] = useState(false);
const setting = useCollectionManager(collectionsCRUDAtom);
const setting = useCollectionManager(useService(CollectionService));
const t = useAFFiNEI18N();
const dragItemId = getDropItemId('collections', collection.id);
@@ -168,7 +169,7 @@ export const CollectionsList = ({
onCreate,
}: CollectionsListProps) => {
const metas = useBlockSuitePageMeta(workspace);
const { collections } = useSavedCollections(collectionsCRUDAtom);
const collections = useLiveData(useService(CollectionService).collections);
const t = useAFFiNEI18N();
if (collections.length === 0) {
return (
@@ -1,13 +1,12 @@
import { Divider } from '@affine/component/ui/divider';
import { MenuItem } from '@affine/component/ui/menu';
import {
workspaceListAtom,
workspaceManagerAtom,
} from '@affine/core/modules/workspace';
import { Unreachable } from '@affine/env/constant';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Logo1Icon } from '@blocksuite/icons';
import { useAtomValue, useSetAtom } from 'jotai';
import { WorkspaceManager } from '@toeverything/infra';
import { useService } from '@toeverything/infra/di';
import { useLiveData } from '@toeverything/infra/livedata';
import { useSetAtom } from 'jotai';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { useSession } from 'next-auth/react';
import { useCallback, useEffect, useMemo } from 'react';
@@ -85,9 +84,8 @@ export const UserWithWorkspaceList = ({
onEventEnd?.();
}, [onEventEnd, setOpenCreateWorkspaceModal]);
const workspaces = useAtomValue(workspaceListAtom);
const workspaceManager = useAtomValue(workspaceManagerAtom);
const workspaceManager = useService(WorkspaceManager);
const workspaces = useLiveData(workspaceManager.list.workspaceList);
// revalidate workspace list when mounted
useEffect(() => {
@@ -5,16 +5,13 @@ import {
useWorkspaceAvatar,
useWorkspaceName,
} from '@affine/core/hooks/use-workspace-info';
import {
currentWorkspaceAtom,
workspaceListAtom,
} from '@affine/core/modules/workspace';
import { WorkspaceSubPath } from '@affine/core/shared';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { WorkspaceMetadata } from '@affine/workspace';
import type { DragEndEvent } from '@dnd-kit/core';
import { useAtomValue, useSetAtom } from 'jotai';
import { WorkspaceManager, type WorkspaceMetadata } from '@toeverything/infra';
import { useService } from '@toeverything/infra/di';
import { useLiveData } from '@toeverything/infra/livedata';
import { useSetAtom } from 'jotai';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { useSession } from 'next-auth/react';
import { useCallback, useMemo } from 'react';
@@ -23,6 +20,8 @@ import {
openCreateWorkspaceModalAtom,
openSettingModalAtom,
} from '../../../../../atoms';
import { CurrentWorkspaceService } from '../../../../../modules/workspace/current-workspace';
import { WorkspaceSubPath } from '../../../../../shared';
import { useIsWorkspaceOwner } from '../.././../../../hooks/affine/use-is-workspace-owner';
import { useNavigateHelper } from '../.././../../../hooks/use-navigate-helper';
import * as styles from './index.css';
@@ -106,13 +105,17 @@ export const AFFiNEWorkspaceList = ({
}: {
onEventEnd?: () => void;
}) => {
const workspaces = useAtomValue(workspaceListAtom);
const workspaces = useLiveData(
useService(WorkspaceManager).list.workspaceList
);
const setOpenCreateWorkspaceModal = useSetAtom(openCreateWorkspaceModalAtom);
const { jumpToSubPath } = useNavigateHelper();
const currentWorkspace = useAtomValue(currentWorkspaceAtom);
const currentWorkspace = useLiveData(
useService(CurrentWorkspaceService).currentWorkspace
);
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
@@ -6,11 +6,9 @@ import { openSettingModalAtom } from '@affine/core/atoms';
import { useIsWorkspaceOwner } from '@affine/core/hooks/affine/use-is-workspace-owner';
import { useWorkspaceBlobObjectUrl } from '@affine/core/hooks/use-workspace-blob';
import { useWorkspaceInfo } from '@affine/core/hooks/use-workspace-info';
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { type SyncEngineStatus, SyncEngineStep } from '@affine/workspace';
import {
CloudWorkspaceIcon,
InformationFillDuotoneIcon,
@@ -18,7 +16,13 @@ import {
NoNetworkIcon,
UnsyncIcon,
} from '@blocksuite/icons';
import { useAtomValue, useSetAtom } from 'jotai';
import {
type SyncEngineStatus,
SyncEngineStep,
Workspace,
} from '@toeverything/infra';
import { useService } from '@toeverything/infra/di';
import { useSetAtom } from 'jotai';
import { debounce, mean } from 'lodash-es';
import {
forwardRef,
@@ -97,7 +101,7 @@ const useSyncEngineSyncProgress = () => {
useState<SyncEngineStatus | null>(null);
const [isOverCapacity, setIsOverCapacity] = useState(false);
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const currentWorkspace = useService(Workspace);
const isOwner = useIsWorkspaceOwner(currentWorkspace.meta);
const setSettingModalAtom = useSetAtom(openSettingModalAtom);
@@ -250,7 +254,7 @@ export const WorkspaceCard = forwardRef<
HTMLDivElement,
HTMLAttributes<HTMLDivElement>
>(({ ...props }, ref) => {
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const currentWorkspace = useService(Workspace);
const information = useWorkspaceInfo(currentWorkspace.meta);
@@ -12,15 +12,14 @@ import {
SidebarScrollableContainer,
} from '@affine/component/app-sidebar';
import { Menu } from '@affine/component/ui/menu';
import { collectionsCRUDAtom } from '@affine/core/atoms/collections';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { WorkspaceSubPath } from '@affine/core/shared';
import { CollectionService } from '@affine/core/modules/collection';
import { apis, events } from '@affine/electron-api';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { Workspace } from '@affine/workspace';
import { FolderIcon, SettingsIcon } from '@blocksuite/icons';
import { type Page } from '@blocksuite/store';
import { useDroppable } from '@dnd-kit/core';
import { useService, type Workspace } from '@toeverything/infra';
import { useAtom, useAtomValue } from 'jotai';
import { nanoid } from 'nanoid';
import type { HTMLAttributes, ReactElement } from 'react';
@@ -35,6 +34,7 @@ import { getDropItemId } from '../../hooks/affine/use-sidebar-drag';
import { useTrashModalHelper } from '../../hooks/affine/use-trash-modal-helper';
import { useRegisterBrowserHistoryCommands } from '../../hooks/use-browser-history-commands';
import { useNavigateHelper } from '../../hooks/use-navigate-helper';
import { WorkspaceSubPath } from '../../shared';
import {
createEmptyCollection,
MoveToTrash,
@@ -177,7 +177,7 @@ export const RootAppSidebar = ({
useRegisterBrowserHistoryCommands(router.back, router.forward);
const userInfo = useDeleteCollectionInfo();
const setting = useCollectionManager(collectionsCRUDAtom);
const setting = useCollectionManager(useService(CollectionService));
const { node, open } = useEditCollectionName({
title: t['com.affine.editCollection.createCollection'](),
showTips: true,
@@ -1,18 +1,18 @@
import { BrowserWarning } from '@affine/component/affine-banner';
import { LocalDemoTips } from '@affine/component/affine-banner';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { workspaceManagerAtom } from '@affine/core/modules/workspace';
import { WorkspaceSubPath } from '@affine/core/shared';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { Workspace } from '@affine/workspace';
import { useAtomValue, useSetAtom } from 'jotai';
import { type Workspace, WorkspaceManager } from '@toeverything/infra';
import { useService } from '@toeverything/infra';
import { useSetAtom } from 'jotai';
import { useCallback, useState } from 'react';
import { authAtom } from '../atoms';
import { useCurrentLoginStatus } from '../hooks/affine/use-current-login-status';
import { useNavigateHelper } from '../hooks/use-navigate-helper';
import { WorkspaceSubPath } from '../shared';
import { EnableAffineCloudModal } from './affine/enable-affine-cloud-modal';
const minimumChromeVersion = 106;
@@ -77,7 +77,7 @@ export const TopTip = ({
}, [setAuthModal]);
const { openPage } = useNavigateHelper();
const workspaceManager = useAtomValue(workspaceManagerAtom);
const workspaceManager = useService(WorkspaceManager);
const handleConfirm = useAsyncCallback(async () => {
if (workspace.flavour !== WorkspaceFlavour.LOCAL) {
return;
@@ -3,15 +3,12 @@ import { AffineShapeIcon } from '@affine/core/components/page-list'; // TODO: im
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
import { useWorkspaceStatus } from '@affine/core/hooks/use-workspace-status';
import {
waitForCurrentWorkspaceAtom,
workspaceManagerAtom,
} from '@affine/core/modules/workspace';
import { WorkspaceSubPath } from '@affine/core/shared';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useAtomValue } from 'jotai';
import { Workspace, WorkspaceManager } from '@toeverything/infra';
import { useService } from '@toeverything/infra/di';
import { useState } from 'react';
import { WorkspaceSubPath } from '../../shared';
import * as styles from './upgrade.css';
import { ArrowCircleIcon, HeartBreakIcon } from './upgrade-icon';
@@ -20,8 +17,8 @@ import { ArrowCircleIcon, HeartBreakIcon } from './upgrade-icon';
*/
export const WorkspaceUpgrade = function WorkspaceUpgrade() {
const [error, setError] = useState<string | null>(null);
const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom);
const workspaceManager = useAtomValue(workspaceManagerAtom);
const currentWorkspace = useService(Workspace);
const workspaceManager = useService(WorkspaceManager);
const upgradeStatus = useWorkspaceStatus(currentWorkspace, s => s.upgrade);
const { openPage } = useNavigateHelper();
const t = useAFFiNEI18N();
@@ -32,10 +29,10 @@ export const WorkspaceUpgrade = function WorkspaceUpgrade() {
}
try {
const newWorkspaceId =
const newWorkspace =
await currentWorkspace.upgrade.upgrade(workspaceManager);
if (newWorkspaceId) {
openPage(newWorkspaceId, WorkspaceSubPath.ALL);
if (newWorkspace) {
openPage(newWorkspace.id, WorkspaceSubPath.ALL);
} else {
// blocksuite may enter an incorrect state, reload to reset it.
location.reload();