mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-24 18:02:47 +08:00
@@ -38,7 +38,6 @@ export function registerAffineHelpCommands({
|
||||
track.$.cmdk.help.contactUs();
|
||||
globalDialogService.open('setting', {
|
||||
activeTab: 'about',
|
||||
workspaceMetadata: null,
|
||||
});
|
||||
},
|
||||
})
|
||||
|
||||
@@ -86,9 +86,8 @@ export const AFFiNESharePage = (props: ShareMenuProps) => {
|
||||
const onOpenWorkspaceSettings = useCallback(() => {
|
||||
globalDialogService.open('setting', {
|
||||
activeTab: 'workspace:preference',
|
||||
workspaceMetadata: props.workspaceMetadata,
|
||||
});
|
||||
}, [globalDialogService, props.workspaceMetadata]);
|
||||
}, [globalDialogService]);
|
||||
|
||||
const onClickAnyoneReadOnlyShare = useAsyncCallback(async () => {
|
||||
if (isSharedPage) {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { useWorkspace } from '@affine/core/components/hooks/use-workspace';
|
||||
import { GlobalContextService } from '@affine/core/modules/global-context';
|
||||
import { WorkspacesService } from '@affine/core/modules/workspace';
|
||||
import { FrameworkScope, useLiveData, useService } from '@toeverything/infra';
|
||||
|
||||
export const CurrentWorkspaceScopeProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const globalContext = useService(GlobalContextService).globalContext;
|
||||
const workspacesService = useService(WorkspacesService);
|
||||
const workspaceMeta = useLiveData(workspacesService.list.workspaces$).find(
|
||||
workspace => workspace.id === globalContext.workspaceId.get()
|
||||
);
|
||||
const workspace = useWorkspace(workspaceMeta);
|
||||
if (!workspace) {
|
||||
// todo(@pengx17): render a loading/error component here if not found?
|
||||
return null;
|
||||
}
|
||||
return <FrameworkScope scope={workspace.scope}>{children}</FrameworkScope>;
|
||||
};
|
||||
@@ -148,11 +148,7 @@ export const RootAppSidebar = memo((): ReactElement => {
|
||||
<SidebarContainer>
|
||||
<div className={workspaceAndUserWrapper}>
|
||||
<div className={workspaceWrapper}>
|
||||
<WorkspaceNavigator
|
||||
showEnableCloudButton
|
||||
showSettingsButton
|
||||
showSyncStatus
|
||||
/>
|
||||
<WorkspaceNavigator showEnableCloudButton showSyncStatus />
|
||||
</div>
|
||||
<UserInfo />
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,6 @@ interface WorkspaceSelectorProps {
|
||||
metadata: WorkspaceMetadata;
|
||||
defaultDocId?: string;
|
||||
}) => void;
|
||||
showSettingsButton?: boolean;
|
||||
showEnableCloudButton?: boolean;
|
||||
showArrowDownIcon?: boolean;
|
||||
showSyncStatus?: boolean;
|
||||
@@ -38,7 +37,6 @@ export const WorkspaceSelector = ({
|
||||
workspaceMetadata: outerWorkspaceMetadata,
|
||||
onSelectWorkspace,
|
||||
onCreatedWorkspace,
|
||||
showSettingsButton,
|
||||
showArrowDownIcon,
|
||||
disable,
|
||||
open: outerOpen,
|
||||
@@ -89,7 +87,6 @@ export const WorkspaceSelector = ({
|
||||
onClickWorkspace={onSelectWorkspace}
|
||||
onCreatedWorkspace={onCreatedWorkspace}
|
||||
showEnableCloudButton={showEnableCloudButton}
|
||||
showSettingsButton={showSettingsButton}
|
||||
/>
|
||||
}
|
||||
contentOptions={{
|
||||
|
||||
@@ -55,7 +55,6 @@ interface UserWithWorkspaceListProps {
|
||||
metadata: WorkspaceMetadata;
|
||||
defaultDocId?: string;
|
||||
}) => void;
|
||||
showSettingsButton?: boolean;
|
||||
showEnableCloudButton?: boolean;
|
||||
}
|
||||
|
||||
@@ -63,7 +62,6 @@ const UserWithWorkspaceListInner = ({
|
||||
onEventEnd,
|
||||
onClickWorkspace,
|
||||
onCreatedWorkspace,
|
||||
showSettingsButton,
|
||||
showEnableCloudButton,
|
||||
}: UserWithWorkspaceListProps) => {
|
||||
const globalDialogService = useService(GlobalDialogService);
|
||||
@@ -121,7 +119,6 @@ const UserWithWorkspaceListInner = ({
|
||||
onEventEnd={onEventEnd}
|
||||
onClickWorkspace={onClickWorkspace}
|
||||
showEnableCloudButton={showEnableCloudButton}
|
||||
showSettingsButton={showSettingsButton}
|
||||
/>
|
||||
<AddWorkspace
|
||||
onAddWorkspace={onAddWorkspace}
|
||||
|
||||
@@ -50,13 +50,11 @@ const CloudWorkSpaceList = ({
|
||||
server,
|
||||
workspaces,
|
||||
onClickWorkspace,
|
||||
onClickWorkspaceSetting,
|
||||
onClickEnableCloud,
|
||||
}: {
|
||||
server: Server;
|
||||
workspaces: WorkspaceMetadata[];
|
||||
onClickWorkspace: (workspaceMetadata: WorkspaceMetadata) => void;
|
||||
onClickWorkspaceSetting?: (workspaceMetadata: WorkspaceMetadata) => void;
|
||||
onClickEnableCloud?: (meta: WorkspaceMetadata) => void;
|
||||
}) => {
|
||||
const t = useI18n();
|
||||
@@ -161,7 +159,6 @@ const CloudWorkSpaceList = ({
|
||||
<WorkspaceList
|
||||
items={workspaces}
|
||||
onClick={onClickWorkspace}
|
||||
onSettingClick={onClickWorkspaceSetting}
|
||||
onEnableCloudClick={onClickEnableCloud}
|
||||
/>
|
||||
<MenuItem
|
||||
@@ -215,16 +212,13 @@ export const AFFiNEWorkspaceList = ({
|
||||
onEventEnd,
|
||||
onClickWorkspace,
|
||||
showEnableCloudButton,
|
||||
showSettingsButton,
|
||||
}: {
|
||||
onClickWorkspace?: (workspaceMetadata: WorkspaceMetadata) => void;
|
||||
onEventEnd?: () => void;
|
||||
showSettingsButton?: boolean;
|
||||
showEnableCloudButton?: boolean;
|
||||
}) => {
|
||||
const workspacesService = useService(WorkspacesService);
|
||||
const workspaces = useLiveData(workspacesService.list.workspaces$);
|
||||
const globalDialogService = useService(GlobalDialogService);
|
||||
|
||||
const confirmEnableCloud = useEnableCloud();
|
||||
|
||||
@@ -247,17 +241,6 @@ export const AFFiNEWorkspaceList = ({
|
||||
[workspaces]
|
||||
);
|
||||
|
||||
const onClickWorkspaceSetting = useCallback(
|
||||
(workspaceMetadata: WorkspaceMetadata) => {
|
||||
globalDialogService.open('setting', {
|
||||
activeTab: 'workspace:preference',
|
||||
workspaceMetadata,
|
||||
});
|
||||
onEventEnd?.();
|
||||
},
|
||||
[globalDialogService, onEventEnd]
|
||||
);
|
||||
|
||||
const onClickEnableCloud = useCallback(
|
||||
(meta: WorkspaceMetadata) => {
|
||||
const { workspace, dispose } = workspacesService.open({ metadata: meta });
|
||||
@@ -292,9 +275,6 @@ export const AFFiNEWorkspaceList = ({
|
||||
({ flavour }) => flavour === server.id
|
||||
)}
|
||||
onClickWorkspace={handleClickWorkspace}
|
||||
onClickWorkspaceSetting={
|
||||
showSettingsButton ? onClickWorkspaceSetting : undefined
|
||||
}
|
||||
/>
|
||||
<Divider size="thinner" />
|
||||
</FrameworkScope>
|
||||
@@ -303,9 +283,6 @@ export const AFFiNEWorkspaceList = ({
|
||||
<LocalWorkspaces
|
||||
workspaces={localWorkspaces}
|
||||
onClickWorkspace={handleClickWorkspace}
|
||||
onClickWorkspaceSetting={
|
||||
showSettingsButton ? onClickWorkspaceSetting : undefined
|
||||
}
|
||||
onClickEnableCloud={
|
||||
showEnableCloudButton ? onClickEnableCloud : undefined
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
PenIcon,
|
||||
} from '@blocksuite/icons/rc';
|
||||
import { useLiveData, useServices } from '@toeverything/infra';
|
||||
import type { ReactElement, SVGProps } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { AuthService, ServerService } from '../../../../modules/cloud';
|
||||
@@ -27,7 +27,7 @@ import { Shortcuts } from './shortcuts';
|
||||
interface GeneralSettingListItem {
|
||||
key: SettingTab;
|
||||
title: string;
|
||||
icon: (props: SVGProps<SVGSVGElement>) => ReactElement;
|
||||
icon: ReactNode;
|
||||
testId: string;
|
||||
}
|
||||
|
||||
@@ -58,19 +58,19 @@ export const useGeneralSettingList = (): GeneralSettingList => {
|
||||
{
|
||||
key: 'appearance',
|
||||
title: t['com.affine.settings.appearance'](),
|
||||
icon: AppearanceIcon,
|
||||
icon: <AppearanceIcon />,
|
||||
testId: 'appearance-panel-trigger',
|
||||
},
|
||||
{
|
||||
key: 'shortcuts',
|
||||
title: t['com.affine.keyboardShortcuts.title'](),
|
||||
icon: KeyboardIcon,
|
||||
icon: <KeyboardIcon />,
|
||||
testId: 'shortcuts-panel-trigger',
|
||||
},
|
||||
{
|
||||
key: 'about',
|
||||
title: t['com.affine.aboutAFFiNE.title'](),
|
||||
icon: InformationIcon,
|
||||
icon: <InformationIcon />,
|
||||
testId: 'about-panel-trigger',
|
||||
},
|
||||
];
|
||||
@@ -79,7 +79,7 @@ export const useGeneralSettingList = (): GeneralSettingList => {
|
||||
settings.splice(1, 0, {
|
||||
key: 'editor',
|
||||
title: t['com.affine.settings.editorSettings'](),
|
||||
icon: PenIcon,
|
||||
icon: <PenIcon />,
|
||||
testId: 'editor-panel-trigger',
|
||||
});
|
||||
}
|
||||
@@ -88,14 +88,14 @@ export const useGeneralSettingList = (): GeneralSettingList => {
|
||||
settings.splice(3, 0, {
|
||||
key: 'plans',
|
||||
title: t['com.affine.payment.title'](),
|
||||
icon: UpgradeIcon,
|
||||
icon: <UpgradeIcon />,
|
||||
testId: 'plans-panel-trigger',
|
||||
});
|
||||
if (status === 'authenticated') {
|
||||
settings.splice(3, 0, {
|
||||
key: 'billing',
|
||||
title: t['com.affine.payment.billing-setting.title'](),
|
||||
icon: PaymentIcon,
|
||||
icon: <PaymentIcon />,
|
||||
testId: 'billing-panel-trigger',
|
||||
});
|
||||
}
|
||||
@@ -104,7 +104,7 @@ export const useGeneralSettingList = (): GeneralSettingList => {
|
||||
settings.push({
|
||||
key: 'experimental-features',
|
||||
title: t['com.affine.settings.workspace.experimental-features'](),
|
||||
icon: ExperimentIcon,
|
||||
icon: <ExperimentIcon />,
|
||||
testId: 'experimental-features-trigger',
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import type {
|
||||
} from '@affine/core/modules/dialogs';
|
||||
import type { SettingTab } from '@affine/core/modules/dialogs/constant';
|
||||
import { GlobalContextService } from '@affine/core/modules/global-context';
|
||||
import type { WorkspaceMetadata } from '@affine/core/modules/workspace';
|
||||
import { Trans } from '@affine/i18n';
|
||||
import { ContactWithUsIcon } from '@blocksuite/icons/rc';
|
||||
import { FrameworkScope, useLiveData, useService } from '@toeverything/infra';
|
||||
@@ -37,7 +36,6 @@ import { WorkspaceSetting } from './workspace-setting';
|
||||
|
||||
interface SettingProps extends ModalProps {
|
||||
activeTab?: SettingTab;
|
||||
workspaceMetadata?: WorkspaceMetadata | null;
|
||||
onCloseSetting: () => void;
|
||||
}
|
||||
|
||||
@@ -54,12 +52,10 @@ const CenteredLoading = () => {
|
||||
|
||||
const SettingModalInner = ({
|
||||
activeTab: initialActiveTab = 'appearance',
|
||||
workspaceMetadata: initialWorkspaceMetadata = null,
|
||||
onCloseSetting,
|
||||
}: SettingProps) => {
|
||||
const [settingState, setSettingState] = useState<SettingState>({
|
||||
activeTab: initialActiveTab,
|
||||
activeWorkspaceMetadata: initialWorkspaceMetadata,
|
||||
scrollAnchor: undefined,
|
||||
});
|
||||
const globalContextService = useService(GlobalContextService);
|
||||
@@ -122,8 +118,8 @@ const SettingModalInner = ({
|
||||
}, []);
|
||||
|
||||
const onTabChange = useCallback(
|
||||
(key: SettingTab, meta: WorkspaceMetadata | null) => {
|
||||
setSettingState({ activeTab: key, activeWorkspaceMetadata: meta });
|
||||
(key: SettingTab) => {
|
||||
setSettingState({ activeTab: key });
|
||||
},
|
||||
[setSettingState]
|
||||
);
|
||||
@@ -143,7 +139,6 @@ const SettingModalInner = ({
|
||||
<SettingSidebar
|
||||
activeTab={settingState.activeTab}
|
||||
onTabChange={onTabChange}
|
||||
selectedWorkspaceId={settingState.activeWorkspaceMetadata?.id ?? null}
|
||||
/>
|
||||
<Scrollable.Root>
|
||||
<Scrollable.Viewport
|
||||
@@ -158,11 +153,9 @@ const SettingModalInner = ({
|
||||
{settingState.activeTab === 'account' &&
|
||||
loginStatus === 'authenticated' ? (
|
||||
<AccountSetting onChangeSettingState={setSettingState} />
|
||||
) : isWorkspaceSetting(settingState.activeTab) &&
|
||||
settingState.activeWorkspaceMetadata ? (
|
||||
) : isWorkspaceSetting(settingState.activeTab) ? (
|
||||
<WorkspaceSetting
|
||||
activeTab={settingState.activeTab}
|
||||
workspaceMetadata={settingState.activeWorkspaceMetadata}
|
||||
onCloseSetting={onCloseSetting}
|
||||
onChangeSettingState={setSettingState}
|
||||
/>
|
||||
@@ -214,7 +207,6 @@ const SettingModalInner = ({
|
||||
export const SettingDialog = ({
|
||||
close,
|
||||
activeTab,
|
||||
workspaceMetadata,
|
||||
}: DialogComponentProps<GLOBAL_DIALOG_SCHEMA['setting']>) => {
|
||||
return (
|
||||
<Modal
|
||||
@@ -234,11 +226,7 @@ export const SettingDialog = ({
|
||||
onOpenChange={() => close()}
|
||||
>
|
||||
<Suspense fallback={<CenteredLoading />}>
|
||||
<SettingModalInner
|
||||
activeTab={activeTab}
|
||||
workspaceMetadata={workspaceMetadata}
|
||||
onCloseSetting={close}
|
||||
/>
|
||||
<SettingModalInner activeTab={activeTab} onCloseSetting={close} />
|
||||
</Suspense>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
import {
|
||||
WorkspaceListItemSkeleton,
|
||||
WorkspaceListSkeleton,
|
||||
} from '@affine/component/setting-components';
|
||||
import { WorkspaceListSkeleton } from '@affine/component/setting-components';
|
||||
import { Avatar } from '@affine/component/ui/avatar';
|
||||
import { Tooltip } from '@affine/component/ui/tooltip';
|
||||
import { UserPlanButton } from '@affine/core/components/affine/auth/user-plan-button';
|
||||
import { useCatchEventCallback } from '@affine/core/components/hooks/use-catch-event-hook';
|
||||
import { useWorkspaceInfo } from '@affine/core/components/hooks/use-workspace-info';
|
||||
import { WorkspaceAvatar } from '@affine/core/components/workspace-avatar';
|
||||
import { CurrentWorkspaceScopeProvider } from '@affine/core/components/providers/current-workspace-scope';
|
||||
import { AuthService } from '@affine/core/modules/cloud';
|
||||
import { UserFeatureService } from '@affine/core/modules/cloud/services/user-feature';
|
||||
import { GlobalDialogService } from '@affine/core/modules/dialogs';
|
||||
import type { SettingTab } from '@affine/core/modules/dialogs/constant';
|
||||
import { GlobalContextService } from '@affine/core/modules/global-context';
|
||||
import {
|
||||
type WorkspaceMetadata,
|
||||
WorkspacesService,
|
||||
WorkspaceService,
|
||||
} from '@affine/core/modules/workspace';
|
||||
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { track } from '@affine/track';
|
||||
import { Logo1Icon } from '@blocksuite/icons/rc';
|
||||
import {
|
||||
Logo1Icon,
|
||||
PaymentIcon,
|
||||
PropertyIcon,
|
||||
SettingsIcon,
|
||||
} from '@blocksuite/icons/rc';
|
||||
import { useLiveData, useService, useServices } from '@toeverything/infra';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
type HTMLAttributes,
|
||||
type MouseEvent,
|
||||
type ReactNode,
|
||||
Suspense,
|
||||
useCallback,
|
||||
useEffect,
|
||||
@@ -117,17 +118,36 @@ export const SignInButton = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const SettingSidebarItem = ({
|
||||
isActive,
|
||||
icon,
|
||||
label,
|
||||
...props
|
||||
}: {
|
||||
isActive: boolean;
|
||||
label: string;
|
||||
icon: ReactNode;
|
||||
} & HTMLAttributes<HTMLDivElement>) => {
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
title={label}
|
||||
className={clsx(style.sidebarSelectItem, {
|
||||
active: isActive,
|
||||
})}
|
||||
>
|
||||
<div className={style.sidebarSelectItemIcon}>{icon}</div>
|
||||
<div className={style.sidebarSelectItemName}>{label}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const SettingSidebar = ({
|
||||
activeTab,
|
||||
onTabChange,
|
||||
selectedWorkspaceId,
|
||||
}: {
|
||||
activeTab: SettingTab;
|
||||
onTabChange: (
|
||||
key: SettingTab,
|
||||
workspaceMetadata: WorkspaceMetadata | null
|
||||
) => void;
|
||||
selectedWorkspaceId: string | null;
|
||||
onTabChange: (key: SettingTab) => void;
|
||||
}) => {
|
||||
const t = useI18n();
|
||||
const loginStatus = useLiveData(useService(AuthService).session.status$);
|
||||
@@ -137,21 +157,21 @@ export const SettingSidebar = ({
|
||||
const tab = e.currentTarget.dataset.eventArg;
|
||||
if (!tab) return;
|
||||
track.$.settingsPanel.menu.openSettings({ to: tab });
|
||||
onTabChange(tab as SettingTab, null);
|
||||
onTabChange(tab as SettingTab);
|
||||
},
|
||||
[onTabChange]
|
||||
);
|
||||
const onAccountSettingClick = useCallback(() => {
|
||||
track.$.settingsPanel.menu.openSettings({ to: 'account' });
|
||||
onTabChange('account', null);
|
||||
onTabChange('account');
|
||||
}, [onTabChange]);
|
||||
const onWorkspaceSettingClick = useCallback(
|
||||
(tab: SettingTab, workspaceMetadata: WorkspaceMetadata) => {
|
||||
(tab: SettingTab) => {
|
||||
track.$.settingsPanel.menu.openSettings({
|
||||
to: 'workspace',
|
||||
control: tab,
|
||||
});
|
||||
onTabChange(tab, workspaceMetadata);
|
||||
onTabChange(tab);
|
||||
},
|
||||
[onTabChange]
|
||||
);
|
||||
@@ -161,195 +181,129 @@ export const SettingSidebar = ({
|
||||
<div className={style.sidebarTitle}>
|
||||
{t['com.affine.settingSidebar.title']()}
|
||||
</div>
|
||||
<div className={style.sidebarSubtitle}>
|
||||
{t['com.affine.settingSidebar.settings.general']()}
|
||||
</div>
|
||||
<div className={style.sidebarItemsWrapper}>
|
||||
{generalList.map(({ title, icon, key, testId }) => {
|
||||
return (
|
||||
<div
|
||||
className={clsx(style.sidebarSelectItem, {
|
||||
active: key === activeTab,
|
||||
})}
|
||||
key={key}
|
||||
title={title}
|
||||
data-event-arg={key}
|
||||
onClick={gotoTab}
|
||||
data-testid={testId}
|
||||
>
|
||||
{icon({ className: 'icon' })}
|
||||
<span className="setting-name">{title}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className={style.sidebarSubtitle}>
|
||||
{t['com.affine.settingSidebar.settings.workspace']()}
|
||||
</div>
|
||||
<div className={clsx(style.sidebarItemsWrapper, 'scroll')}>
|
||||
<Suspense fallback={<WorkspaceListSkeleton />}>
|
||||
<WorkspaceList
|
||||
onWorkspaceSettingClick={onWorkspaceSettingClick}
|
||||
selectedWorkspaceId={selectedWorkspaceId}
|
||||
activeTab={activeTab}
|
||||
{loginStatus === 'unauthenticated' ? <SignInButton /> : null}
|
||||
{loginStatus === 'authenticated' ? (
|
||||
<Suspense>
|
||||
<UserInfo
|
||||
onAccountSettingClick={onAccountSettingClick}
|
||||
active={activeTab === 'account'}
|
||||
onTabChange={onTabChange}
|
||||
/>
|
||||
</Suspense>
|
||||
) : null}
|
||||
|
||||
<div className={style.sidebarGroup}>
|
||||
<div className={style.sidebarSubtitle}>
|
||||
{t['com.affine.settingSidebar.settings.general']()}
|
||||
</div>
|
||||
<div className={style.sidebarItemsWrapper}>
|
||||
{generalList.map(({ title, icon, key, testId }) => {
|
||||
return (
|
||||
<SettingSidebarItem
|
||||
isActive={key === activeTab}
|
||||
key={key}
|
||||
label={title}
|
||||
data-event-arg={key}
|
||||
onClick={gotoTab}
|
||||
data-testid={testId}
|
||||
icon={icon}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={style.sidebarFooter}>
|
||||
{loginStatus === 'unauthenticated' ? <SignInButton /> : null}
|
||||
{loginStatus === 'authenticated' ? (
|
||||
<Suspense>
|
||||
<UserInfo
|
||||
onAccountSettingClick={onAccountSettingClick}
|
||||
active={activeTab === 'account'}
|
||||
onTabChange={onTabChange}
|
||||
/>
|
||||
<div className={style.sidebarGroup}>
|
||||
<div className={style.sidebarSubtitle}>
|
||||
{t['com.affine.settingSidebar.settings.workspace']()}
|
||||
</div>
|
||||
<div className={style.sidebarItemsWrapper}>
|
||||
<Suspense fallback={<WorkspaceListSkeleton />}>
|
||||
<CurrentWorkspaceScopeProvider>
|
||||
<WorkspaceSettingItems
|
||||
onWorkspaceSettingClick={onWorkspaceSettingClick}
|
||||
activeTab={activeTab}
|
||||
/>
|
||||
</CurrentWorkspaceScopeProvider>
|
||||
</Suspense>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const WorkspaceList = ({
|
||||
const WorkspaceSettingItems = ({
|
||||
onWorkspaceSettingClick,
|
||||
selectedWorkspaceId,
|
||||
activeTab,
|
||||
}: {
|
||||
onWorkspaceSettingClick: (
|
||||
activeTab: SettingTab,
|
||||
workspaceMetadata: WorkspaceMetadata
|
||||
) => void;
|
||||
selectedWorkspaceId: string | null;
|
||||
onWorkspaceSettingClick: (activeTab: SettingTab) => void;
|
||||
activeTab: SettingTab;
|
||||
}) => {
|
||||
const workspaces = useLiveData(
|
||||
useService(WorkspacesService).list.workspaces$
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{workspaces.map(workspace => {
|
||||
return (
|
||||
<Suspense key={workspace.id} fallback={<WorkspaceListItemSkeleton />}>
|
||||
<WorkspaceListItem
|
||||
meta={workspace}
|
||||
onClick={subTab => {
|
||||
onWorkspaceSettingClick(subTab, workspace);
|
||||
}}
|
||||
activeTab={
|
||||
workspace.id === selectedWorkspaceId ? activeTab : undefined
|
||||
}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const WorkspaceListItem = ({
|
||||
activeTab,
|
||||
meta,
|
||||
onClick,
|
||||
}: {
|
||||
meta: WorkspaceMetadata;
|
||||
activeTab?: SettingTab;
|
||||
onClick: (activeTab: SettingTab) => void;
|
||||
}) => {
|
||||
const { globalContextService, userFeatureService } = useServices({
|
||||
GlobalContextService,
|
||||
const { userFeatureService } = useServices({
|
||||
UserFeatureService,
|
||||
});
|
||||
const information = useWorkspaceInfo(meta);
|
||||
const name = information?.name ?? UNTITLED_WORKSPACE_NAME;
|
||||
const currentWorkspaceId = useLiveData(
|
||||
globalContextService.globalContext.workspaceId.$
|
||||
);
|
||||
const isCurrent = currentWorkspaceId === meta.id;
|
||||
|
||||
const workspaceService = useService(WorkspaceService);
|
||||
const information = useWorkspaceInfo(workspaceService.workspace);
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
useEffect(() => {
|
||||
userFeatureService.userFeature.revalidate();
|
||||
}, [userFeatureService]);
|
||||
|
||||
const onClickPreference = useCallback(() => {
|
||||
onClick('workspace:preference');
|
||||
}, [onClick]);
|
||||
|
||||
const showBilling = information?.isTeam && information?.isOwner;
|
||||
const subTabs = useMemo(() => {
|
||||
const subTabConfigs = [
|
||||
{
|
||||
key: 'workspace:preference',
|
||||
title: 'com.affine.settings.workspace.preferences',
|
||||
icon: <SettingsIcon />,
|
||||
},
|
||||
{
|
||||
key: 'workspace:properties',
|
||||
title: 'com.affine.settings.workspace.properties',
|
||||
icon: <PropertyIcon />,
|
||||
},
|
||||
...(showBilling
|
||||
? [
|
||||
{
|
||||
key: 'workspace:billing' as SettingTab,
|
||||
title: 'com.affine.settings.workspace.billing',
|
||||
icon: <PaymentIcon />,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
] satisfies {
|
||||
key: SettingTab;
|
||||
title: keyof ReturnType<typeof useI18n>;
|
||||
icon: ReactNode;
|
||||
}[];
|
||||
|
||||
return subTabConfigs.map(({ key, title }) => {
|
||||
return subTabConfigs.map(({ key, title, icon }) => {
|
||||
return (
|
||||
<div
|
||||
<SettingSidebarItem
|
||||
isActive={activeTab === key}
|
||||
label={t[title]()}
|
||||
icon={icon}
|
||||
data-testid={`workspace-list-item-${key}`}
|
||||
onClick={() => {
|
||||
onClick(key);
|
||||
onWorkspaceSettingClick(key);
|
||||
}}
|
||||
className={clsx(style.sidebarSelectSubItem, {
|
||||
className={clsx(style.sidebarSelectItem, {
|
||||
active: activeTab === key,
|
||||
})}
|
||||
key={key}
|
||||
>
|
||||
{t[title]()}
|
||||
</div>
|
||||
/>
|
||||
);
|
||||
});
|
||||
}, [activeTab, onClick, showBilling, t]);
|
||||
}, [activeTab, onWorkspaceSettingClick, showBilling, t]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={clsx(style.sidebarSelectItem, { active: !!activeTab })}
|
||||
title={name}
|
||||
onClick={onClickPreference}
|
||||
data-testid="workspace-list-item"
|
||||
>
|
||||
<WorkspaceAvatar
|
||||
key={meta.id}
|
||||
meta={meta}
|
||||
size={16}
|
||||
name={name}
|
||||
colorfulFallback
|
||||
style={{
|
||||
marginRight: '10px',
|
||||
}}
|
||||
rounded={2}
|
||||
/>
|
||||
<span className="setting-name">{name}</span>
|
||||
{isCurrent ? (
|
||||
<Tooltip content="Current" side="top">
|
||||
<div
|
||||
className={style.currentWorkspaceLabel}
|
||||
data-testid="current-workspace-label"
|
||||
></div>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</div>
|
||||
{activeTab && subTabs.length > 1 ? subTabs : null}
|
||||
</>
|
||||
<div className={style.sidebarItemsWrapper}>
|
||||
{/* TODO: remove the suspense? */}
|
||||
<Suspense fallback={<WorkspaceListSkeleton />}>{subTabs}</Suspense>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { globalStyle, style } from '@vanilla-extract/css';
|
||||
export const settingSlideBar = style({
|
||||
width: '25%',
|
||||
maxWidth: '242px',
|
||||
background: cssVar('backgroundSecondaryColor'),
|
||||
padding: '20px 0px',
|
||||
padding: '20px 12px',
|
||||
height: '100%',
|
||||
flexShrink: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '16px',
|
||||
overflowY: 'auto',
|
||||
});
|
||||
export const sidebarTitle = style({
|
||||
fontSize: cssVar('fontH6'),
|
||||
fontWeight: '600',
|
||||
lineHeight: cssVar('lineHeight'),
|
||||
padding: '0px 16px 0px 24px',
|
||||
padding: '0 8px',
|
||||
});
|
||||
export const sidebarSubtitle = style({
|
||||
fontSize: cssVar('fontSm'),
|
||||
lineHeight: cssVar('lineHeight'),
|
||||
color: cssVar('textSecondaryColor'),
|
||||
padding: '0px 16px 0px 24px',
|
||||
marginTop: '20px',
|
||||
marginBottom: '4px',
|
||||
padding: '4px 8px',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
@@ -31,18 +32,11 @@ export const sidebarItemsWrapper = style({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 4,
|
||||
selectors: {
|
||||
'&.scroll': {
|
||||
flexGrow: 1,
|
||||
overflowY: 'auto',
|
||||
},
|
||||
},
|
||||
});
|
||||
export const sidebarSelectItem = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
margin: '0px 16px',
|
||||
padding: '0px 8px',
|
||||
padding: '4px 8px',
|
||||
height: '30px',
|
||||
flexShrink: 0,
|
||||
fontSize: cssVar('fontSm'),
|
||||
@@ -76,19 +70,23 @@ export const sidebarSelectSubItem = style({
|
||||
},
|
||||
},
|
||||
});
|
||||
globalStyle(`${settingSlideBar} .icon`, {
|
||||
|
||||
export const sidebarSelectItemIcon = style({
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
marginRight: '10px',
|
||||
flexShrink: 0,
|
||||
color: cssVarV2('icon/primary'),
|
||||
});
|
||||
globalStyle(`${settingSlideBar} .setting-name`, {
|
||||
|
||||
export const sidebarSelectItemName = style({
|
||||
minWidth: 0,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
flexGrow: 1,
|
||||
});
|
||||
|
||||
export const currentWorkspaceLabel = style({
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
@@ -105,9 +103,13 @@ export const currentWorkspaceLabel = style({
|
||||
},
|
||||
},
|
||||
});
|
||||
export const sidebarFooter = style({
|
||||
padding: '0 16px',
|
||||
|
||||
export const sidebarGroup = style({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '4px',
|
||||
});
|
||||
|
||||
export const accountButton = style({
|
||||
padding: '4px 8px',
|
||||
borderRadius: '8px',
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import type { SettingTab } from '@affine/core/modules/dialogs/constant';
|
||||
import type { WorkspaceMetadata } from '@affine/core/modules/workspace';
|
||||
|
||||
export interface SettingState {
|
||||
activeTab: SettingTab;
|
||||
activeWorkspaceMetadata?: WorkspaceMetadata | null;
|
||||
scrollAnchor?: string;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
import { getUpgradeQuestionnaireLink } from '@affine/core/components/hooks/affine/use-subscription-notify';
|
||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||
import { useMutation } from '@affine/core/components/hooks/use-mutation';
|
||||
import { useWorkspace } from '@affine/core/components/hooks/use-workspace';
|
||||
import {
|
||||
AuthService,
|
||||
SubscriptionService,
|
||||
@@ -17,7 +16,7 @@ import {
|
||||
} from '@affine/core/modules/cloud';
|
||||
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
||||
import { UrlService } from '@affine/core/modules/url';
|
||||
import type { WorkspaceMetadata } from '@affine/core/modules/workspace';
|
||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||
import {
|
||||
createCustomerPortalMutation,
|
||||
type InvoicesQuery,
|
||||
@@ -27,7 +26,7 @@ import {
|
||||
UserFriendlyError,
|
||||
} from '@affine/graphql';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { FrameworkScope, useLiveData, useService } from '@toeverything/infra';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
@@ -37,14 +36,8 @@ import {
|
||||
} from '../../general-setting/plans/actions';
|
||||
import * as styles from './styles.css';
|
||||
|
||||
export const WorkspaceSettingBilling = ({
|
||||
workspaceMetadata,
|
||||
}: {
|
||||
workspaceMetadata: WorkspaceMetadata;
|
||||
}) => {
|
||||
// useWorkspace hook is a vary heavy operation here, but we need syncing name and avatar changes here,
|
||||
// we don't have a better way to do this now
|
||||
const workspace = useWorkspace(workspaceMetadata);
|
||||
export const WorkspaceSettingBilling = () => {
|
||||
const workspace = useService(WorkspaceService).workspace;
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
@@ -68,7 +61,7 @@ export const WorkspaceSettingBilling = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<FrameworkScope scope={workspace.scope}>
|
||||
<>
|
||||
<SettingHeader
|
||||
title={t['com.affine.payment.billing-setting.title']()}
|
||||
subtitle={t['com.affine.payment.billing-setting.subtitle']()}
|
||||
@@ -87,7 +80,7 @@ export const WorkspaceSettingBilling = ({
|
||||
<SettingWrapper title={t['com.affine.payment.billing-setting.history']()}>
|
||||
<BillingHistory />
|
||||
</SettingWrapper>
|
||||
</FrameworkScope>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CurrentWorkspaceScopeProvider } from '@affine/core/components/providers/current-workspace-scope';
|
||||
import type { SettingTab } from '@affine/core/modules/dialogs/constant';
|
||||
import type { WorkspaceMetadata } from '@affine/core/modules/workspace';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import type { SettingState } from '../types';
|
||||
import { WorkspaceSettingBilling } from './billing';
|
||||
@@ -7,31 +8,32 @@ import { WorkspaceSettingDetail } from './new-workspace-setting-detail';
|
||||
import { WorkspaceSettingProperties } from './properties';
|
||||
|
||||
export const WorkspaceSetting = ({
|
||||
workspaceMetadata,
|
||||
activeTab,
|
||||
onCloseSetting,
|
||||
onChangeSettingState,
|
||||
}: {
|
||||
workspaceMetadata: WorkspaceMetadata;
|
||||
activeTab: SettingTab;
|
||||
onCloseSetting: () => void;
|
||||
onChangeSettingState: (settingState: SettingState) => void;
|
||||
}) => {
|
||||
switch (activeTab) {
|
||||
case 'workspace:preference':
|
||||
return (
|
||||
<WorkspaceSettingDetail
|
||||
onCloseSetting={onCloseSetting}
|
||||
onChangeSettingState={onChangeSettingState}
|
||||
workspaceMetadata={workspaceMetadata}
|
||||
/>
|
||||
);
|
||||
case 'workspace:properties':
|
||||
return (
|
||||
<WorkspaceSettingProperties workspaceMetadata={workspaceMetadata} />
|
||||
);
|
||||
case 'workspace:billing':
|
||||
return <WorkspaceSettingBilling workspaceMetadata={workspaceMetadata} />;
|
||||
}
|
||||
return null;
|
||||
const element = useMemo(() => {
|
||||
switch (activeTab) {
|
||||
case 'workspace:preference':
|
||||
return (
|
||||
<WorkspaceSettingDetail
|
||||
onCloseSetting={onCloseSetting}
|
||||
onChangeSettingState={onChangeSettingState}
|
||||
/>
|
||||
);
|
||||
case 'workspace:properties':
|
||||
return <WorkspaceSettingProperties />;
|
||||
case 'workspace:billing':
|
||||
return <WorkspaceSettingBilling />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}, [activeTab, onCloseSetting, onChangeSettingState]);
|
||||
return (
|
||||
<CurrentWorkspaceScopeProvider>{element}</CurrentWorkspaceScopeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,25 +5,18 @@ import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hoo
|
||||
import { useSystemOnline } from '@affine/core/components/hooks/use-system-online';
|
||||
import { DesktopApiService } from '@affine/core/modules/desktop-api';
|
||||
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
||||
import type {
|
||||
Workspace,
|
||||
WorkspaceMetadata,
|
||||
} from '@affine/core/modules/workspace';
|
||||
import type { Workspace } from '@affine/core/modules/workspace';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import track from '@affine/track';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface ExportPanelProps {
|
||||
workspaceMetadata: WorkspaceMetadata;
|
||||
workspace: Workspace | null;
|
||||
workspace: Workspace;
|
||||
}
|
||||
|
||||
export const DesktopExportPanel = ({
|
||||
workspaceMetadata,
|
||||
workspace,
|
||||
}: ExportPanelProps) => {
|
||||
const workspaceId = workspaceMetadata.id;
|
||||
export const DesktopExportPanel = ({ workspace }: ExportPanelProps) => {
|
||||
const workspaceId = workspace.id;
|
||||
const workspacePermissionService = useService(
|
||||
WorkspacePermissionService
|
||||
).permission;
|
||||
|
||||
@@ -3,13 +3,13 @@ import {
|
||||
SettingRow,
|
||||
SettingWrapper,
|
||||
} from '@affine/component/setting-components';
|
||||
import { useWorkspace } from '@affine/core/components/hooks/use-workspace';
|
||||
import { useWorkspaceInfo } from '@affine/core/components/hooks/use-workspace-info';
|
||||
import { WorkspaceServerService } from '@affine/core/modules/cloud';
|
||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { ArrowRightSmallIcon } from '@blocksuite/icons/rc';
|
||||
import { FrameworkScope } from '@toeverything/infra';
|
||||
import { FrameworkScope, useService } from '@toeverything/infra';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { DeleteLeaveWorkspace } from './delete-leave-workspace';
|
||||
@@ -24,18 +24,15 @@ import type { WorkspaceSettingDetailProps } from './types';
|
||||
import { WorkspaceQuotaPanel } from './workspace-quota';
|
||||
|
||||
export const WorkspaceSettingDetail = ({
|
||||
workspaceMetadata,
|
||||
onCloseSetting,
|
||||
onChangeSettingState,
|
||||
}: WorkspaceSettingDetailProps) => {
|
||||
const t = useI18n();
|
||||
|
||||
// useWorkspace hook is a vary heavy operation here, but we need syncing name and avatar changes here,
|
||||
// we don't have a better way to do this now
|
||||
const workspace = useWorkspace(workspaceMetadata);
|
||||
const workspace = useService(WorkspaceService).workspace;
|
||||
const server = workspace?.scope.get(WorkspaceServerService).server;
|
||||
|
||||
const workspaceInfo = useWorkspaceInfo(workspaceMetadata);
|
||||
const workspaceInfo = useWorkspaceInfo(workspace);
|
||||
|
||||
const handleResetSyncStatus = useCallback(() => {
|
||||
workspace?.engine.doc
|
||||
@@ -80,10 +77,7 @@ export const WorkspaceSettingDetail = ({
|
||||
<SharingPanel />
|
||||
{BUILD_CONFIG.isElectron && (
|
||||
<SettingWrapper title={t['Storage and Export']()}>
|
||||
<DesktopExportPanel
|
||||
workspace={workspace}
|
||||
workspaceMetadata={workspaceMetadata}
|
||||
/>
|
||||
<DesktopExportPanel workspace={workspace} />
|
||||
</SettingWrapper>
|
||||
)}
|
||||
<SettingWrapper>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import type { WorkspaceMetadata } from '@affine/core/modules/workspace';
|
||||
|
||||
import type { SettingState } from '../../types';
|
||||
|
||||
export interface WorkspaceSettingDetailProps {
|
||||
workspaceMetadata: WorkspaceMetadata;
|
||||
onCloseSetting: () => void;
|
||||
onChangeSettingState: (settingState: SettingState) => void;
|
||||
}
|
||||
|
||||
@@ -4,13 +4,12 @@ import { DocPropertyManager } from '@affine/core/components/doc-properties/manag
|
||||
import { CreatePropertyMenuItems } from '@affine/core/components/doc-properties/menu/create-doc-property';
|
||||
import { useWorkspaceInfo } from '@affine/core/components/hooks/use-workspace-info';
|
||||
import type { DocCustomPropertyInfo } from '@affine/core/modules/db';
|
||||
import type { WorkspaceMetadata } from '@affine/core/modules/workspace';
|
||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||
import { Trans, useI18n } from '@affine/i18n';
|
||||
import track from '@affine/track';
|
||||
import { FrameworkScope } from '@toeverything/infra';
|
||||
import { FrameworkScope, useService } from '@toeverything/infra';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useWorkspace } from '../../../../../components/hooks/use-workspace';
|
||||
import * as styles from './styles.css';
|
||||
|
||||
const WorkspaceSettingPropertiesMain = () => {
|
||||
@@ -47,14 +46,10 @@ const WorkspaceSettingPropertiesMain = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const WorkspaceSettingProperties = ({
|
||||
workspaceMetadata,
|
||||
}: {
|
||||
workspaceMetadata: WorkspaceMetadata;
|
||||
}) => {
|
||||
export const WorkspaceSettingProperties = () => {
|
||||
const t = useI18n();
|
||||
const workspace = useWorkspace(workspaceMetadata);
|
||||
const workspaceInfo = useWorkspaceInfo(workspaceMetadata);
|
||||
const workspace = useService(WorkspaceService).workspace;
|
||||
const workspaceInfo = useWorkspaceInfo(workspace);
|
||||
const title = workspaceInfo?.name || 'untitled';
|
||||
|
||||
if (workspace === null) {
|
||||
|
||||
@@ -265,11 +265,7 @@ export function FallbackHeaderWithWorkspaceNavigator() {
|
||||
return (
|
||||
<div className={styles.fallbackHeader}>
|
||||
{currentWorkspace && navigate ? (
|
||||
<WorkspaceNavigator
|
||||
showSettingsButton
|
||||
showSyncStatus
|
||||
showEnableCloudButton
|
||||
/>
|
||||
<WorkspaceNavigator showSyncStatus showEnableCloudButton />
|
||||
) : (
|
||||
<FallbackHeaderSkeleton />
|
||||
)}
|
||||
|
||||
@@ -35,7 +35,8 @@ export type { ServerConfig } from './types';
|
||||
|
||||
import { type Framework } from '@toeverything/infra';
|
||||
|
||||
import { DocScope, DocService } from '../doc';
|
||||
import { DocScope } from '../doc/scopes/doc';
|
||||
import { DocService } from '../doc/services/doc';
|
||||
import { GlobalCache, GlobalState, GlobalStateService } from '../storage';
|
||||
import { UrlService } from '../url';
|
||||
import { WorkspaceScope, WorkspaceService } from '../workspace';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Framework } from '@toeverything/infra';
|
||||
|
||||
import { WorkspaceServerService } from '../cloud';
|
||||
import { WorkspaceServerService } from '../cloud/services/workspace-server';
|
||||
import { WorkspaceScope, WorkspaceService } from '../workspace';
|
||||
import { WorkspaceDB } from './entities/db';
|
||||
import { WorkspaceDBTable } from './entities/table';
|
||||
|
||||
@@ -26,11 +26,7 @@ export type GLOBAL_DIALOG_SCHEMA = {
|
||||
templateMode: DocMode;
|
||||
snapshotUrl: string;
|
||||
}) => void;
|
||||
setting: (props: {
|
||||
activeTab?: SettingTab;
|
||||
workspaceMetadata?: WorkspaceMetadata | null;
|
||||
scrollAnchor?: string;
|
||||
}) => void;
|
||||
setting: (props: { activeTab?: SettingTab; scrollAnchor?: string }) => void;
|
||||
'sign-in': (props: { server?: string; step?: string }) => void;
|
||||
'change-password': (props: { server?: string }) => void;
|
||||
'verify-email': (props: { server?: string; changeEmail?: boolean }) => void;
|
||||
|
||||
@@ -8,7 +8,7 @@ export { DocsService } from './services/docs';
|
||||
|
||||
import type { Framework } from '@toeverything/infra';
|
||||
|
||||
import { WorkspaceDBService } from '../db';
|
||||
import { WorkspaceDBService } from '../db/services/db';
|
||||
import { WorkspaceScope, WorkspaceService } from '../workspace';
|
||||
import { Doc } from './entities/doc';
|
||||
import { DocPropertyList } from './entities/property-list';
|
||||
|
||||
Reference in New Issue
Block a user