fix: circular dependencies (#4307)

This commit is contained in:
Peng Xiao
2023-09-12 01:00:48 +08:00
committed by GitHub
parent 892cae5599
commit ea2196b039
21 changed files with 150 additions and 120 deletions

View File

@@ -6,7 +6,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ArrowRightSmallIcon } from '@blocksuite/icons'; import { ArrowRightSmallIcon } from '@blocksuite/icons';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import type { WorkspaceSettingDetailProps } from '../index'; import type { WorkspaceSettingDetailProps } from '../types';
import { WorkspaceDeleteModal } from './delete'; import { WorkspaceDeleteModal } from './delete';
export interface DeleteLeaveWorkspaceProps extends WorkspaceSettingDetailProps { export interface DeleteLeaveWorkspaceProps extends WorkspaceSettingDetailProps {

View File

@@ -3,10 +3,6 @@ import {
SettingRow, SettingRow,
SettingWrapper, SettingWrapper,
} from '@affine/component/setting-components'; } from '@affine/component/setting-components';
import type {
WorkspaceFlavour,
WorkspaceRegistry,
} from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name'; import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import { useMemo } from 'react'; import { useMemo } from 'react';
@@ -19,22 +15,7 @@ import { MembersPanel } from './members';
import { ProfilePanel } from './profile'; import { ProfilePanel } from './profile';
import { PublishPanel } from './publish'; import { PublishPanel } from './publish';
import { StoragePanel } from './storage'; import { StoragePanel } from './storage';
import type { WorkspaceSettingDetailProps } from './types';
export interface WorkspaceSettingDetailProps {
workspaceId: string;
isOwner: boolean;
onDeleteLocalWorkspace: () => void;
onDeleteCloudWorkspace: () => void;
onLeaveWorkspace: () => void;
onTransferWorkspace: <
From extends WorkspaceFlavour,
To extends WorkspaceFlavour,
>(
from: From,
to: To,
workspace: WorkspaceRegistry[From]
) => void;
}
export const WorkspaceSettingDetail = (props: WorkspaceSettingDetailProps) => { export const WorkspaceSettingDetail = (props: WorkspaceSettingDetailProps) => {
const { workspaceId } = props; const { workspaceId } = props;

View File

@@ -1,8 +1,8 @@
import type { AffineOfficialWorkspace } from '@affine/env/workspace'; import type { AffineOfficialWorkspace } from '@affine/env/workspace';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { type WorkspaceSettingDetailProps } from './index';
import * as style from './style.css'; import * as style from './style.css';
import type { WorkspaceSettingDetailProps } from './types';
export interface LabelsPanelProps extends WorkspaceSettingDetailProps { export interface LabelsPanelProps extends WorkspaceSettingDetailProps {
workspace: AffineOfficialWorkspace; workspace: AffineOfficialWorkspace;

View File

@@ -25,8 +25,8 @@ import { useInviteMember } from '../../../hooks/affine/use-invite-member';
import { type Member, useMembers } from '../../../hooks/affine/use-members'; import { type Member, useMembers } from '../../../hooks/affine/use-members';
import { useRevokeMemberPermission } from '../../../hooks/affine/use-revoke-member-permission'; import { useRevokeMemberPermission } from '../../../hooks/affine/use-revoke-member-permission';
import { AnyErrorBoundary } from '../any-error-boundary'; import { AnyErrorBoundary } from '../any-error-boundary';
import { type WorkspaceSettingDetailProps } from './index';
import * as style from './style.css'; import * as style from './style.css';
import type { WorkspaceSettingDetailProps } from './types';
export interface MembersPanelProps extends WorkspaceSettingDetailProps { export interface MembersPanelProps extends WorkspaceSettingDetailProps {
workspace: AffineOfficialWorkspace; workspace: AffineOfficialWorkspace;

View File

@@ -15,8 +15,8 @@ import {
} from 'react'; } from 'react';
import { Upload } from '../../pure/file-upload'; import { Upload } from '../../pure/file-upload';
import { type WorkspaceSettingDetailProps } from './index';
import * as style from './style.css'; import * as style from './style.css';
import type { WorkspaceSettingDetailProps } from './types';
export interface ProfilePanelProps extends WorkspaceSettingDetailProps { export interface ProfilePanelProps extends WorkspaceSettingDetailProps {
workspace: AffineOfficialWorkspace; workspace: AffineOfficialWorkspace;

View File

@@ -17,8 +17,8 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { toast } from '../../../utils'; import { toast } from '../../../utils';
import { EnableAffineCloudModal } from '../enable-affine-cloud-modal'; import { EnableAffineCloudModal } from '../enable-affine-cloud-modal';
import { TmpDisableAffineCloudModal } from '../tmp-disable-affine-cloud-modal'; import { TmpDisableAffineCloudModal } from '../tmp-disable-affine-cloud-modal';
import type { WorkspaceSettingDetailProps } from './index';
import * as style from './style.css'; import * as style from './style.css';
import type { WorkspaceSettingDetailProps } from './types';
export interface PublishPanelProps export interface PublishPanelProps
extends Omit<WorkspaceSettingDetailProps, 'workspaceId'> { extends Omit<WorkspaceSettingDetailProps, 'workspaceId'> {

View File

@@ -0,0 +1,20 @@
import type {
WorkspaceFlavour,
WorkspaceRegistry,
} from '@affine/env/workspace';
export interface WorkspaceSettingDetailProps {
workspaceId: string;
isOwner: boolean;
onDeleteLocalWorkspace: () => void;
onDeleteCloudWorkspace: () => void;
onLeaveWorkspace: () => void;
onTransferWorkspace: <
From extends WorkspaceFlavour,
To extends WorkspaceFlavour,
>(
from: From,
to: To,
workspace: WorkspaceRegistry[From]
) => void;
}

View File

@@ -25,7 +25,8 @@ import {
} from 'rxjs/operators'; } from 'rxjs/operators';
import { logger } from '../logger'; import { logger } from '../logger';
import { getWorkspaceMeta, workspaceSubjects } from '../workspace'; import { getWorkspaceMeta } from '../workspace/meta';
import { workspaceSubjects } from '../workspace/subjects';
import { SecondaryWorkspaceSQLiteDB } from './secondary-db'; import { SecondaryWorkspaceSQLiteDB } from './secondary-db';
import type { WorkspaceSQLiteDB } from './workspace-db-adapter'; import type { WorkspaceSQLiteDB } from './workspace-db-adapter';
import { openWorkspaceDatabase } from './workspace-db-adapter'; import { openWorkspaceDatabase } from './workspace-db-adapter';

View File

@@ -6,7 +6,7 @@ import { applyUpdate, Doc as YDoc } from 'yjs';
import { logger } from '../logger'; import { logger } from '../logger';
import type { YOrigin } from '../type'; import type { YOrigin } from '../type';
import { getWorkspaceMeta } from '../workspace'; import { getWorkspaceMeta } from '../workspace/meta';
import { BaseSQLiteAdapter } from './base-db-adapter'; import { BaseSQLiteAdapter } from './base-db-adapter';
import type { WorkspaceSQLiteDB } from './workspace-db-adapter'; import type { WorkspaceSQLiteDB } from './workspace-db-adapter';

View File

@@ -5,7 +5,7 @@ import { applyUpdate, Doc as YDoc, encodeStateAsUpdate } from 'yjs';
import { logger } from '../logger'; import { logger } from '../logger';
import type { YOrigin } from '../type'; import type { YOrigin } from '../type';
import { getWorkspaceMeta } from '../workspace'; import { getWorkspaceMeta } from '../workspace/meta';
import { BaseSQLiteAdapter } from './base-db-adapter'; import { BaseSQLiteAdapter } from './base-db-adapter';
import { dbSubjects } from './subjects'; import { dbSubjects } from './subjects';

View File

@@ -21,13 +21,12 @@ import {
import type { WorkspaceSQLiteDB } from '../db/workspace-db-adapter'; import type { WorkspaceSQLiteDB } from '../db/workspace-db-adapter';
import { logger } from '../logger'; import { logger } from '../logger';
import { mainRPC } from '../main-rpc'; import { mainRPC } from '../main-rpc';
import { listWorkspaces, storeWorkspaceMeta } from '../workspace';
import { import {
getWorkspaceDBPath, getWorkspaceDBPath,
getWorkspaceMeta, getWorkspaceMeta,
getWorkspacesBasePath, getWorkspacesBasePath,
listWorkspaces, } from '../workspace/meta';
storeWorkspaceMeta,
} from '../workspace';
// NOTE: // NOTE:
// we are using native dialogs because HTML dialogs do not give full file paths // we are using native dialogs because HTML dialogs do not give full file paths

View File

@@ -6,6 +6,7 @@ import type {
import { dbEvents, dbHandlers } from './db'; import { dbEvents, dbHandlers } from './db';
import { dialogHandlers } from './dialog'; import { dialogHandlers } from './dialog';
import { provideExposed } from './provide';
import { workspaceEvents, workspaceHandlers } from './workspace'; import { workspaceEvents, workspaceHandlers } from './workspace';
type AllHandlers = { type AllHandlers = {
@@ -25,7 +26,7 @@ export const events = {
workspace: workspaceEvents, workspace: workspaceEvents,
}; };
export const getExposedMeta = () => { const getExposedMeta = () => {
const handlersMeta = Object.entries(handlers).map( const handlersMeta = Object.entries(handlers).map(
([namespace, namespaceHandlers]) => { ([namespace, namespaceHandlers]) => {
return [namespace, Object.keys(namespaceHandlers)] as [string, string[]]; return [namespace, Object.keys(namespaceHandlers)] as [string, string[]];
@@ -43,3 +44,5 @@ export const getExposedMeta = () => {
events: eventsMeta, events: eventsMeta,
}; };
}; };
provideExposed(getExposedMeta());

View File

@@ -1,13 +1,17 @@
import { assertExists } from '@blocksuite/global/utils';
import type { import type {
HelperToMain, HelperToMain,
MainToHelper, MainToHelper,
} from '@toeverything/infra/preload/electron'; } from '@toeverything/infra/preload/electron';
import { AsyncCall } from 'async-call-rpc'; import { AsyncCall } from 'async-call-rpc';
import { getExposedMeta } from './exposed'; import { exposed } from './provide';
const helperToMainServer: HelperToMain = { const helperToMainServer: HelperToMain = {
getMeta: () => getExposedMeta(), getMeta: () => {
assertExists(exposed);
return exposed;
},
}; };
export const mainRPC = AsyncCall<MainToHelper>(helperToMainServer, { export const mainRPC = AsyncCall<MainToHelper>(helperToMainServer, {

View File

@@ -0,0 +1,11 @@
import type { ExposedMeta } from '@toeverything/infra/preload/electron';
/**
* A naive DI implementation to get rid of circular dependency.
*/
export let exposed: ExposedMeta | undefined;
export const provideExposed = (exposedMeta: ExposedMeta) => {
exposed = exposedMeta;
};

View File

@@ -74,7 +74,7 @@ describe('delete workspace', () => {
describe('getWorkspaceMeta', () => { describe('getWorkspaceMeta', () => {
test('can get meta', async () => { test('can get meta', async () => {
const { getWorkspaceMeta } = await import('../handlers'); const { getWorkspaceMeta } = await import('../meta');
const workspaceId = v4(); const workspaceId = v4();
const workspacePath = path.join(appDataPath, 'workspaces', workspaceId); const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
const meta = { const meta = {
@@ -86,7 +86,7 @@ describe('getWorkspaceMeta', () => {
}); });
test('can create meta if not exists', async () => { test('can create meta if not exists', async () => {
const { getWorkspaceMeta } = await import('../handlers'); const { getWorkspaceMeta } = await import('../meta');
const workspaceId = v4(); const workspaceId = v4();
const workspacePath = path.join(appDataPath, 'workspaces', workspaceId); const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
await fs.ensureDir(workspacePath); await fs.ensureDir(workspacePath);
@@ -100,7 +100,7 @@ describe('getWorkspaceMeta', () => {
}); });
test('can migrate meta if db file is a link', async () => { test('can migrate meta if db file is a link', async () => {
const { getWorkspaceMeta } = await import('../handlers'); const { getWorkspaceMeta } = await import('../meta');
const workspaceId = v4(); const workspaceId = v4();
const workspacePath = path.join(appDataPath, 'workspaces', workspaceId); const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
await fs.ensureDir(workspacePath); await fs.ensureDir(workspacePath);

View File

@@ -4,20 +4,15 @@ import fs from 'fs-extra';
import { ensureSQLiteDB } from '../db/ensure-db'; import { ensureSQLiteDB } from '../db/ensure-db';
import { logger } from '../logger'; import { logger } from '../logger';
import { mainRPC } from '../main-rpc';
import type { WorkspaceMeta } from '../type'; import type { WorkspaceMeta } from '../type';
import {
getDeletedWorkspacesBasePath,
getWorkspaceBasePath,
getWorkspaceMeta,
getWorkspacesBasePath,
} from './meta';
import { workspaceSubjects } from './subjects'; import { workspaceSubjects } from './subjects';
let _appDataPath = '';
async function getAppDataPath() {
if (_appDataPath) {
return _appDataPath;
}
_appDataPath = await mainRPC.getPath('sessionData');
return _appDataPath;
}
export async function listWorkspaces(): Promise< export async function listWorkspaces(): Promise<
[workspaceId: string, meta: WorkspaceMeta][] [workspaceId: string, meta: WorkspaceMeta][]
> { > {
@@ -58,67 +53,6 @@ export async function deleteWorkspace(id: string) {
} }
} }
export async function getWorkspacesBasePath() {
return path.join(await getAppDataPath(), 'workspaces');
}
export async function getWorkspaceBasePath(workspaceId: string) {
return path.join(await getAppDataPath(), 'workspaces', workspaceId);
}
async function getDeletedWorkspacesBasePath() {
return path.join(await getAppDataPath(), 'deleted-workspaces');
}
export async function getWorkspaceDBPath(workspaceId: string) {
return path.join(await getWorkspaceBasePath(workspaceId), 'storage.db');
}
export async function getWorkspaceMetaPath(workspaceId: string) {
return path.join(await getWorkspaceBasePath(workspaceId), 'meta.json');
}
/**
* Get workspace meta, create one if not exists
* This function will also migrate the workspace if needed
*/
export async function getWorkspaceMeta(
workspaceId: string
): Promise<WorkspaceMeta> {
try {
const basePath = await getWorkspaceBasePath(workspaceId);
const metaPath = await getWorkspaceMetaPath(workspaceId);
if (!(await fs.exists(metaPath))) {
// since not meta is found, we will migrate symlinked db file if needed
await fs.ensureDir(basePath);
const dbPath = await getWorkspaceDBPath(workspaceId);
// todo: remove this after migration (in stable version)
const realDBPath = (await fs.exists(dbPath))
? await fs.realpath(dbPath)
: dbPath;
const isLink = realDBPath !== dbPath;
if (isLink) {
await fs.copy(realDBPath, dbPath);
}
// create one if not exists
const meta = {
id: workspaceId,
mainDBPath: dbPath,
secondaryDBPath: isLink ? realDBPath : undefined,
};
await fs.writeJSON(metaPath, meta);
return meta;
} else {
const meta = await fs.readJSON(metaPath);
return meta;
}
} catch (err) {
logger.error('getWorkspaceMeta failed', err);
throw err;
}
}
export async function storeWorkspaceMeta( export async function storeWorkspaceMeta(
workspaceId: string, workspaceId: string,
meta: Partial<WorkspaceMeta> meta: Partial<WorkspaceMeta>

View File

@@ -1,5 +1,6 @@
import type { MainEventRegister, WorkspaceMeta } from '../type'; import type { MainEventRegister, WorkspaceMeta } from '../type';
import { deleteWorkspace, getWorkspaceMeta, listWorkspaces } from './handlers'; import { deleteWorkspace, listWorkspaces } from './handlers';
import { getWorkspaceMeta } from './meta';
import { workspaceSubjects } from './subjects'; import { workspaceSubjects } from './subjects';
export * from './handlers'; export * from './handlers';

View File

@@ -0,0 +1,78 @@
import path from 'node:path';
import fs from 'fs-extra';
import { logger } from '../logger';
import { mainRPC } from '../main-rpc';
import type { WorkspaceMeta } from '../type';
let _appDataPath = '';
export async function getAppDataPath() {
if (_appDataPath) {
return _appDataPath;
}
_appDataPath = await mainRPC.getPath('sessionData');
return _appDataPath;
}
export async function getWorkspacesBasePath() {
return path.join(await getAppDataPath(), 'workspaces');
}
export async function getWorkspaceBasePath(workspaceId: string) {
return path.join(await getAppDataPath(), 'workspaces', workspaceId);
}
export async function getDeletedWorkspacesBasePath() {
return path.join(await getAppDataPath(), 'deleted-workspaces');
}
export async function getWorkspaceDBPath(workspaceId: string) {
return path.join(await getWorkspaceBasePath(workspaceId), 'storage.db');
}
export async function getWorkspaceMetaPath(workspaceId: string) {
return path.join(await getWorkspaceBasePath(workspaceId), 'meta.json');
}
/**
* Get workspace meta, create one if not exists
* This function will also migrate the workspace if needed
*/
export async function getWorkspaceMeta(
workspaceId: string
): Promise<WorkspaceMeta> {
try {
const basePath = await getWorkspaceBasePath(workspaceId);
const metaPath = await getWorkspaceMetaPath(workspaceId);
if (!(await fs.exists(metaPath))) {
// since not meta is found, we will migrate symlinked db file if needed
await fs.ensureDir(basePath);
const dbPath = await getWorkspaceDBPath(workspaceId);
// todo: remove this after migration (in stable version)
const realDBPath = (await fs.exists(dbPath))
? await fs.realpath(dbPath)
: dbPath;
const isLink = realDBPath !== dbPath;
if (isLink) {
await fs.copy(realDBPath, dbPath);
}
// create one if not exists
const meta = {
id: workspaceId,
mainDBPath: dbPath,
secondaryDBPath: isLink ? realDBPath : undefined,
};
await fs.writeJSON(metaPath, meta);
return meta;
} else {
const meta = await fs.readJSON(metaPath);
return meta;
}
} catch (err) {
logger.error('getWorkspaceMeta failed', err);
throw err;
}
}

View File

@@ -1,7 +1,3 @@
import {
CollectionBar,
type CollectionsAtom,
} from '@affine/component/page-list';
import { DEFAULT_SORT_KEY } from '@affine/env/constant'; import { DEFAULT_SORT_KEY } from '@affine/env/constant';
import type { PropertiesMeta } from '@affine/env/filter'; import type { PropertiesMeta } from '@affine/env/filter';
import type { GetPageInfoById } from '@affine/env/page-info'; import type { GetPageInfoById } from '@affine/env/page-info';
@@ -28,8 +24,10 @@ import { AllPageListMobileView, TrashListMobileView } from './mobile';
import { TrashOperationCell } from './operation-cell'; import { TrashOperationCell } from './operation-cell';
import { StyledTableContainer } from './styles'; import { StyledTableContainer } from './styles';
import type { ListData, PageListProps, TrashListData } from './type'; import type { ListData, PageListProps, TrashListData } from './type';
import type { CollectionsAtom } from './use-collection-manager';
import { useSorter } from './use-sorter'; import { useSorter } from './use-sorter';
import { formatDate, useIsSmallDevices } from './utils'; import { formatDate, useIsSmallDevices } from './utils';
import { CollectionBar } from './view/collection-bar';
interface AllPagesHeadProps { interface AllPagesHeadProps {
isPublicWorkspace: boolean; isPublicWorkspace: boolean;

View File

@@ -1,7 +1,3 @@
import {
type CollectionsAtom,
EditCollectionModel,
} from '@affine/component/page-list';
import type { PropertiesMeta } from '@affine/env/filter'; import type { PropertiesMeta } from '@affine/env/filter';
import type { GetPageInfoById } from '@affine/env/page-info'; import type { GetPageInfoById } from '@affine/env/page-info';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
@@ -11,8 +7,12 @@ import { Tooltip } from '@toeverything/components/tooltip';
import clsx from 'clsx'; import clsx from 'clsx';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { useCollectionManager } from '../use-collection-manager'; import {
type CollectionsAtom,
useCollectionManager,
} from '../use-collection-manager';
import * as styles from './collection-bar.css'; import * as styles from './collection-bar.css';
import { EditCollectionModel } from './create-collection';
import { useActions } from './use-action'; import { useActions } from './use-action';
interface CollectionBarProps { interface CollectionBarProps {

View File

@@ -1,5 +1,4 @@
import { FlexWrapper } from '@affine/component'; import { FlexWrapper } from '@affine/component';
import { EditCollectionModel } from '@affine/component/page-list';
import type { Collection, Filter } from '@affine/env/filter'; import type { Collection, Filter } from '@affine/env/filter';
import type { PropertiesMeta } from '@affine/env/filter'; import type { PropertiesMeta } from '@affine/env/filter';
import type { GetPageInfoById } from '@affine/env/page-info'; import type { GetPageInfoById } from '@affine/env/page-info';
@@ -15,6 +14,7 @@ import { useCallback, useRef, useState } from 'react';
import { CreateFilterMenu } from '../filter/vars'; import { CreateFilterMenu } from '../filter/vars';
import type { useCollectionManager } from '../use-collection-manager'; import type { useCollectionManager } from '../use-collection-manager';
import * as styles from './collection-list.css'; import * as styles from './collection-list.css';
import { EditCollectionModel } from './create-collection';
import { useActions } from './use-action'; import { useActions } from './use-action';
const CollectionOption = ({ const CollectionOption = ({