feat(core): remove workspace selectors in settings (#9705)

fix AF-2119
This commit is contained in:
pengx17
2025-01-15 07:37:24 +00:00
parent c31a0d28cf
commit 0bce5c6730
33 changed files with 223 additions and 349 deletions

View File

@@ -12,7 +12,7 @@ function setupRendererConnection(rendererPort: Electron.MessagePortMain) {
try { try {
const start = performance.now(); const start = performance.now();
const result = await handler(...args); const result = await handler(...args);
logger.debug( logger.info(
'[async-api]', '[async-api]',
`${namespace}.${name}`, `${namespace}.${name}`,
args.filter( args.filter(

View File

@@ -38,7 +38,6 @@ export function registerAffineHelpCommands({
track.$.cmdk.help.contactUs(); track.$.cmdk.help.contactUs();
globalDialogService.open('setting', { globalDialogService.open('setting', {
activeTab: 'about', activeTab: 'about',
workspaceMetadata: null,
}); });
}, },
}) })

View File

@@ -86,9 +86,8 @@ export const AFFiNESharePage = (props: ShareMenuProps) => {
const onOpenWorkspaceSettings = useCallback(() => { const onOpenWorkspaceSettings = useCallback(() => {
globalDialogService.open('setting', { globalDialogService.open('setting', {
activeTab: 'workspace:preference', activeTab: 'workspace:preference',
workspaceMetadata: props.workspaceMetadata,
}); });
}, [globalDialogService, props.workspaceMetadata]); }, [globalDialogService]);
const onClickAnyoneReadOnlyShare = useAsyncCallback(async () => { const onClickAnyoneReadOnlyShare = useAsyncCallback(async () => {
if (isSharedPage) { if (isSharedPage) {

View File

@@ -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>;
};

View File

@@ -148,11 +148,7 @@ export const RootAppSidebar = memo((): ReactElement => {
<SidebarContainer> <SidebarContainer>
<div className={workspaceAndUserWrapper}> <div className={workspaceAndUserWrapper}>
<div className={workspaceWrapper}> <div className={workspaceWrapper}>
<WorkspaceNavigator <WorkspaceNavigator showEnableCloudButton showSyncStatus />
showEnableCloudButton
showSettingsButton
showSyncStatus
/>
</div> </div>
<UserInfo /> <UserInfo />
</div> </div>

View File

@@ -25,7 +25,6 @@ interface WorkspaceSelectorProps {
metadata: WorkspaceMetadata; metadata: WorkspaceMetadata;
defaultDocId?: string; defaultDocId?: string;
}) => void; }) => void;
showSettingsButton?: boolean;
showEnableCloudButton?: boolean; showEnableCloudButton?: boolean;
showArrowDownIcon?: boolean; showArrowDownIcon?: boolean;
showSyncStatus?: boolean; showSyncStatus?: boolean;
@@ -38,7 +37,6 @@ export const WorkspaceSelector = ({
workspaceMetadata: outerWorkspaceMetadata, workspaceMetadata: outerWorkspaceMetadata,
onSelectWorkspace, onSelectWorkspace,
onCreatedWorkspace, onCreatedWorkspace,
showSettingsButton,
showArrowDownIcon, showArrowDownIcon,
disable, disable,
open: outerOpen, open: outerOpen,
@@ -89,7 +87,6 @@ export const WorkspaceSelector = ({
onClickWorkspace={onSelectWorkspace} onClickWorkspace={onSelectWorkspace}
onCreatedWorkspace={onCreatedWorkspace} onCreatedWorkspace={onCreatedWorkspace}
showEnableCloudButton={showEnableCloudButton} showEnableCloudButton={showEnableCloudButton}
showSettingsButton={showSettingsButton}
/> />
} }
contentOptions={{ contentOptions={{

View File

@@ -55,7 +55,6 @@ interface UserWithWorkspaceListProps {
metadata: WorkspaceMetadata; metadata: WorkspaceMetadata;
defaultDocId?: string; defaultDocId?: string;
}) => void; }) => void;
showSettingsButton?: boolean;
showEnableCloudButton?: boolean; showEnableCloudButton?: boolean;
} }
@@ -63,7 +62,6 @@ const UserWithWorkspaceListInner = ({
onEventEnd, onEventEnd,
onClickWorkspace, onClickWorkspace,
onCreatedWorkspace, onCreatedWorkspace,
showSettingsButton,
showEnableCloudButton, showEnableCloudButton,
}: UserWithWorkspaceListProps) => { }: UserWithWorkspaceListProps) => {
const globalDialogService = useService(GlobalDialogService); const globalDialogService = useService(GlobalDialogService);
@@ -121,7 +119,6 @@ const UserWithWorkspaceListInner = ({
onEventEnd={onEventEnd} onEventEnd={onEventEnd}
onClickWorkspace={onClickWorkspace} onClickWorkspace={onClickWorkspace}
showEnableCloudButton={showEnableCloudButton} showEnableCloudButton={showEnableCloudButton}
showSettingsButton={showSettingsButton}
/> />
<AddWorkspace <AddWorkspace
onAddWorkspace={onAddWorkspace} onAddWorkspace={onAddWorkspace}

View File

@@ -50,13 +50,11 @@ const CloudWorkSpaceList = ({
server, server,
workspaces, workspaces,
onClickWorkspace, onClickWorkspace,
onClickWorkspaceSetting,
onClickEnableCloud, onClickEnableCloud,
}: { }: {
server: Server; server: Server;
workspaces: WorkspaceMetadata[]; workspaces: WorkspaceMetadata[];
onClickWorkspace: (workspaceMetadata: WorkspaceMetadata) => void; onClickWorkspace: (workspaceMetadata: WorkspaceMetadata) => void;
onClickWorkspaceSetting?: (workspaceMetadata: WorkspaceMetadata) => void;
onClickEnableCloud?: (meta: WorkspaceMetadata) => void; onClickEnableCloud?: (meta: WorkspaceMetadata) => void;
}) => { }) => {
const t = useI18n(); const t = useI18n();
@@ -161,7 +159,6 @@ const CloudWorkSpaceList = ({
<WorkspaceList <WorkspaceList
items={workspaces} items={workspaces}
onClick={onClickWorkspace} onClick={onClickWorkspace}
onSettingClick={onClickWorkspaceSetting}
onEnableCloudClick={onClickEnableCloud} onEnableCloudClick={onClickEnableCloud}
/> />
<MenuItem <MenuItem
@@ -215,16 +212,13 @@ export const AFFiNEWorkspaceList = ({
onEventEnd, onEventEnd,
onClickWorkspace, onClickWorkspace,
showEnableCloudButton, showEnableCloudButton,
showSettingsButton,
}: { }: {
onClickWorkspace?: (workspaceMetadata: WorkspaceMetadata) => void; onClickWorkspace?: (workspaceMetadata: WorkspaceMetadata) => void;
onEventEnd?: () => void; onEventEnd?: () => void;
showSettingsButton?: boolean;
showEnableCloudButton?: boolean; showEnableCloudButton?: boolean;
}) => { }) => {
const workspacesService = useService(WorkspacesService); const workspacesService = useService(WorkspacesService);
const workspaces = useLiveData(workspacesService.list.workspaces$); const workspaces = useLiveData(workspacesService.list.workspaces$);
const globalDialogService = useService(GlobalDialogService);
const confirmEnableCloud = useEnableCloud(); const confirmEnableCloud = useEnableCloud();
@@ -247,17 +241,6 @@ export const AFFiNEWorkspaceList = ({
[workspaces] [workspaces]
); );
const onClickWorkspaceSetting = useCallback(
(workspaceMetadata: WorkspaceMetadata) => {
globalDialogService.open('setting', {
activeTab: 'workspace:preference',
workspaceMetadata,
});
onEventEnd?.();
},
[globalDialogService, onEventEnd]
);
const onClickEnableCloud = useCallback( const onClickEnableCloud = useCallback(
(meta: WorkspaceMetadata) => { (meta: WorkspaceMetadata) => {
const { workspace, dispose } = workspacesService.open({ metadata: meta }); const { workspace, dispose } = workspacesService.open({ metadata: meta });
@@ -292,9 +275,6 @@ export const AFFiNEWorkspaceList = ({
({ flavour }) => flavour === server.id ({ flavour }) => flavour === server.id
)} )}
onClickWorkspace={handleClickWorkspace} onClickWorkspace={handleClickWorkspace}
onClickWorkspaceSetting={
showSettingsButton ? onClickWorkspaceSetting : undefined
}
/> />
<Divider size="thinner" /> <Divider size="thinner" />
</FrameworkScope> </FrameworkScope>
@@ -303,9 +283,6 @@ export const AFFiNEWorkspaceList = ({
<LocalWorkspaces <LocalWorkspaces
workspaces={localWorkspaces} workspaces={localWorkspaces}
onClickWorkspace={handleClickWorkspace} onClickWorkspace={handleClickWorkspace}
onClickWorkspaceSetting={
showSettingsButton ? onClickWorkspaceSetting : undefined
}
onClickEnableCloud={ onClickEnableCloud={
showEnableCloudButton ? onClickEnableCloud : undefined showEnableCloudButton ? onClickEnableCloud : undefined
} }

View File

@@ -10,7 +10,7 @@ import {
PenIcon, PenIcon,
} from '@blocksuite/icons/rc'; } from '@blocksuite/icons/rc';
import { useLiveData, useServices } from '@toeverything/infra'; import { useLiveData, useServices } from '@toeverything/infra';
import type { ReactElement, SVGProps } from 'react'; import type { ReactNode } from 'react';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { AuthService, ServerService } from '../../../../modules/cloud'; import { AuthService, ServerService } from '../../../../modules/cloud';
@@ -27,7 +27,7 @@ import { Shortcuts } from './shortcuts';
interface GeneralSettingListItem { interface GeneralSettingListItem {
key: SettingTab; key: SettingTab;
title: string; title: string;
icon: (props: SVGProps<SVGSVGElement>) => ReactElement; icon: ReactNode;
testId: string; testId: string;
} }
@@ -58,19 +58,19 @@ export const useGeneralSettingList = (): GeneralSettingList => {
{ {
key: 'appearance', key: 'appearance',
title: t['com.affine.settings.appearance'](), title: t['com.affine.settings.appearance'](),
icon: AppearanceIcon, icon: <AppearanceIcon />,
testId: 'appearance-panel-trigger', testId: 'appearance-panel-trigger',
}, },
{ {
key: 'shortcuts', key: 'shortcuts',
title: t['com.affine.keyboardShortcuts.title'](), title: t['com.affine.keyboardShortcuts.title'](),
icon: KeyboardIcon, icon: <KeyboardIcon />,
testId: 'shortcuts-panel-trigger', testId: 'shortcuts-panel-trigger',
}, },
{ {
key: 'about', key: 'about',
title: t['com.affine.aboutAFFiNE.title'](), title: t['com.affine.aboutAFFiNE.title'](),
icon: InformationIcon, icon: <InformationIcon />,
testId: 'about-panel-trigger', testId: 'about-panel-trigger',
}, },
]; ];
@@ -79,7 +79,7 @@ export const useGeneralSettingList = (): GeneralSettingList => {
settings.splice(1, 0, { settings.splice(1, 0, {
key: 'editor', key: 'editor',
title: t['com.affine.settings.editorSettings'](), title: t['com.affine.settings.editorSettings'](),
icon: PenIcon, icon: <PenIcon />,
testId: 'editor-panel-trigger', testId: 'editor-panel-trigger',
}); });
} }
@@ -88,14 +88,14 @@ export const useGeneralSettingList = (): GeneralSettingList => {
settings.splice(3, 0, { settings.splice(3, 0, {
key: 'plans', key: 'plans',
title: t['com.affine.payment.title'](), title: t['com.affine.payment.title'](),
icon: UpgradeIcon, icon: <UpgradeIcon />,
testId: 'plans-panel-trigger', testId: 'plans-panel-trigger',
}); });
if (status === 'authenticated') { if (status === 'authenticated') {
settings.splice(3, 0, { settings.splice(3, 0, {
key: 'billing', key: 'billing',
title: t['com.affine.payment.billing-setting.title'](), title: t['com.affine.payment.billing-setting.title'](),
icon: PaymentIcon, icon: <PaymentIcon />,
testId: 'billing-panel-trigger', testId: 'billing-panel-trigger',
}); });
} }
@@ -104,7 +104,7 @@ export const useGeneralSettingList = (): GeneralSettingList => {
settings.push({ settings.push({
key: 'experimental-features', key: 'experimental-features',
title: t['com.affine.settings.workspace.experimental-features'](), title: t['com.affine.settings.workspace.experimental-features'](),
icon: ExperimentIcon, icon: <ExperimentIcon />,
testId: 'experimental-features-trigger', testId: 'experimental-features-trigger',
}); });

View File

@@ -13,7 +13,6 @@ import type {
} from '@affine/core/modules/dialogs'; } from '@affine/core/modules/dialogs';
import type { SettingTab } from '@affine/core/modules/dialogs/constant'; import type { SettingTab } from '@affine/core/modules/dialogs/constant';
import { GlobalContextService } from '@affine/core/modules/global-context'; import { GlobalContextService } from '@affine/core/modules/global-context';
import type { WorkspaceMetadata } from '@affine/core/modules/workspace';
import { Trans } from '@affine/i18n'; import { Trans } from '@affine/i18n';
import { ContactWithUsIcon } from '@blocksuite/icons/rc'; import { ContactWithUsIcon } from '@blocksuite/icons/rc';
import { FrameworkScope, useLiveData, useService } from '@toeverything/infra'; import { FrameworkScope, useLiveData, useService } from '@toeverything/infra';
@@ -37,7 +36,6 @@ import { WorkspaceSetting } from './workspace-setting';
interface SettingProps extends ModalProps { interface SettingProps extends ModalProps {
activeTab?: SettingTab; activeTab?: SettingTab;
workspaceMetadata?: WorkspaceMetadata | null;
onCloseSetting: () => void; onCloseSetting: () => void;
} }
@@ -54,12 +52,10 @@ const CenteredLoading = () => {
const SettingModalInner = ({ const SettingModalInner = ({
activeTab: initialActiveTab = 'appearance', activeTab: initialActiveTab = 'appearance',
workspaceMetadata: initialWorkspaceMetadata = null,
onCloseSetting, onCloseSetting,
}: SettingProps) => { }: SettingProps) => {
const [settingState, setSettingState] = useState<SettingState>({ const [settingState, setSettingState] = useState<SettingState>({
activeTab: initialActiveTab, activeTab: initialActiveTab,
activeWorkspaceMetadata: initialWorkspaceMetadata,
scrollAnchor: undefined, scrollAnchor: undefined,
}); });
const globalContextService = useService(GlobalContextService); const globalContextService = useService(GlobalContextService);
@@ -122,8 +118,8 @@ const SettingModalInner = ({
}, []); }, []);
const onTabChange = useCallback( const onTabChange = useCallback(
(key: SettingTab, meta: WorkspaceMetadata | null) => { (key: SettingTab) => {
setSettingState({ activeTab: key, activeWorkspaceMetadata: meta }); setSettingState({ activeTab: key });
}, },
[setSettingState] [setSettingState]
); );
@@ -143,7 +139,6 @@ const SettingModalInner = ({
<SettingSidebar <SettingSidebar
activeTab={settingState.activeTab} activeTab={settingState.activeTab}
onTabChange={onTabChange} onTabChange={onTabChange}
selectedWorkspaceId={settingState.activeWorkspaceMetadata?.id ?? null}
/> />
<Scrollable.Root> <Scrollable.Root>
<Scrollable.Viewport <Scrollable.Viewport
@@ -158,11 +153,9 @@ const SettingModalInner = ({
{settingState.activeTab === 'account' && {settingState.activeTab === 'account' &&
loginStatus === 'authenticated' ? ( loginStatus === 'authenticated' ? (
<AccountSetting onChangeSettingState={setSettingState} /> <AccountSetting onChangeSettingState={setSettingState} />
) : isWorkspaceSetting(settingState.activeTab) && ) : isWorkspaceSetting(settingState.activeTab) ? (
settingState.activeWorkspaceMetadata ? (
<WorkspaceSetting <WorkspaceSetting
activeTab={settingState.activeTab} activeTab={settingState.activeTab}
workspaceMetadata={settingState.activeWorkspaceMetadata}
onCloseSetting={onCloseSetting} onCloseSetting={onCloseSetting}
onChangeSettingState={setSettingState} onChangeSettingState={setSettingState}
/> />
@@ -214,7 +207,6 @@ const SettingModalInner = ({
export const SettingDialog = ({ export const SettingDialog = ({
close, close,
activeTab, activeTab,
workspaceMetadata,
}: DialogComponentProps<GLOBAL_DIALOG_SCHEMA['setting']>) => { }: DialogComponentProps<GLOBAL_DIALOG_SCHEMA['setting']>) => {
return ( return (
<Modal <Modal
@@ -234,11 +226,7 @@ export const SettingDialog = ({
onOpenChange={() => close()} onOpenChange={() => close()}
> >
<Suspense fallback={<CenteredLoading />}> <Suspense fallback={<CenteredLoading />}>
<SettingModalInner <SettingModalInner activeTab={activeTab} onCloseSetting={close} />
activeTab={activeTab}
workspaceMetadata={workspaceMetadata}
onCloseSetting={close}
/>
</Suspense> </Suspense>
</Modal> </Modal>
); );

View File

@@ -1,30 +1,31 @@
import { import { WorkspaceListSkeleton } from '@affine/component/setting-components';
WorkspaceListItemSkeleton,
WorkspaceListSkeleton,
} from '@affine/component/setting-components';
import { Avatar } from '@affine/component/ui/avatar'; 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 { UserPlanButton } from '@affine/core/components/affine/auth/user-plan-button';
import { useCatchEventCallback } from '@affine/core/components/hooks/use-catch-event-hook'; import { useCatchEventCallback } from '@affine/core/components/hooks/use-catch-event-hook';
import { useWorkspaceInfo } from '@affine/core/components/hooks/use-workspace-info'; 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 { AuthService } from '@affine/core/modules/cloud';
import { UserFeatureService } from '@affine/core/modules/cloud/services/user-feature'; import { UserFeatureService } from '@affine/core/modules/cloud/services/user-feature';
import { GlobalDialogService } from '@affine/core/modules/dialogs'; import { GlobalDialogService } from '@affine/core/modules/dialogs';
import type { SettingTab } from '@affine/core/modules/dialogs/constant'; import type { SettingTab } from '@affine/core/modules/dialogs/constant';
import { GlobalContextService } from '@affine/core/modules/global-context';
import { import {
type WorkspaceMetadata, type WorkspaceMetadata,
WorkspacesService, WorkspaceService,
} from '@affine/core/modules/workspace'; } from '@affine/core/modules/workspace';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { track } from '@affine/track'; 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 { useLiveData, useService, useServices } from '@toeverything/infra';
import clsx from 'clsx'; import clsx from 'clsx';
import { import {
type HTMLAttributes,
type MouseEvent, type MouseEvent,
type ReactNode,
Suspense, Suspense,
useCallback, useCallback,
useEffect, 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 = ({ export const SettingSidebar = ({
activeTab, activeTab,
onTabChange, onTabChange,
selectedWorkspaceId,
}: { }: {
activeTab: SettingTab; activeTab: SettingTab;
onTabChange: ( onTabChange: (key: SettingTab) => void;
key: SettingTab,
workspaceMetadata: WorkspaceMetadata | null
) => void;
selectedWorkspaceId: string | null;
}) => { }) => {
const t = useI18n(); const t = useI18n();
const loginStatus = useLiveData(useService(AuthService).session.status$); const loginStatus = useLiveData(useService(AuthService).session.status$);
@@ -137,21 +157,21 @@ export const SettingSidebar = ({
const tab = e.currentTarget.dataset.eventArg; const tab = e.currentTarget.dataset.eventArg;
if (!tab) return; if (!tab) return;
track.$.settingsPanel.menu.openSettings({ to: tab }); track.$.settingsPanel.menu.openSettings({ to: tab });
onTabChange(tab as SettingTab, null); onTabChange(tab as SettingTab);
}, },
[onTabChange] [onTabChange]
); );
const onAccountSettingClick = useCallback(() => { const onAccountSettingClick = useCallback(() => {
track.$.settingsPanel.menu.openSettings({ to: 'account' }); track.$.settingsPanel.menu.openSettings({ to: 'account' });
onTabChange('account', null); onTabChange('account');
}, [onTabChange]); }, [onTabChange]);
const onWorkspaceSettingClick = useCallback( const onWorkspaceSettingClick = useCallback(
(tab: SettingTab, workspaceMetadata: WorkspaceMetadata) => { (tab: SettingTab) => {
track.$.settingsPanel.menu.openSettings({ track.$.settingsPanel.menu.openSettings({
to: 'workspace', to: 'workspace',
control: tab, control: tab,
}); });
onTabChange(tab, workspaceMetadata); onTabChange(tab);
}, },
[onTabChange] [onTabChange]
); );
@@ -161,43 +181,7 @@ export const SettingSidebar = ({
<div className={style.sidebarTitle}> <div className={style.sidebarTitle}>
{t['com.affine.settingSidebar.title']()} {t['com.affine.settingSidebar.title']()}
</div> </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}
/>
</Suspense>
</div>
<div className={style.sidebarFooter}>
{loginStatus === 'unauthenticated' ? <SignInButton /> : null} {loginStatus === 'unauthenticated' ? <SignInButton /> : null}
{loginStatus === 'authenticated' ? ( {loginStatus === 'authenticated' ? (
<Suspense> <Suspense>
@@ -208,148 +192,118 @@ export const SettingSidebar = ({
/> />
</Suspense> </Suspense>
) : null} ) : null}
</div>
</div>
);
};
export const WorkspaceList = ({ <div className={style.sidebarGroup}>
onWorkspaceSettingClick, <div className={style.sidebarSubtitle}>
selectedWorkspaceId, {t['com.affine.settingSidebar.settings.general']()}
activeTab, </div>
}: { <div className={style.sidebarItemsWrapper}>
onWorkspaceSettingClick: ( {generalList.map(({ title, icon, key, testId }) => {
activeTab: SettingTab,
workspaceMetadata: WorkspaceMetadata
) => void;
selectedWorkspaceId: string | null;
activeTab: SettingTab;
}) => {
const workspaces = useLiveData(
useService(WorkspacesService).list.workspaces$
);
return ( return (
<> <SettingSidebarItem
{workspaces.map(workspace => { isActive={key === activeTab}
return ( key={key}
<Suspense key={workspace.id} fallback={<WorkspaceListItemSkeleton />}> label={title}
<WorkspaceListItem data-event-arg={key}
meta={workspace} onClick={gotoTab}
onClick={subTab => { data-testid={testId}
onWorkspaceSettingClick(subTab, workspace); icon={icon}
}}
activeTab={
workspace.id === selectedWorkspaceId ? activeTab : undefined
}
/> />
</Suspense>
); );
})} })}
</> </div>
</div>
<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>
</div>
</div>
</div>
); );
}; };
const WorkspaceListItem = ({ const WorkspaceSettingItems = ({
onWorkspaceSettingClick,
activeTab, activeTab,
meta,
onClick,
}: { }: {
meta: WorkspaceMetadata; onWorkspaceSettingClick: (activeTab: SettingTab) => void;
activeTab?: SettingTab; activeTab: SettingTab;
onClick: (activeTab: SettingTab) => void;
}) => { }) => {
const { globalContextService, userFeatureService } = useServices({ const { userFeatureService } = useServices({
GlobalContextService,
UserFeatureService, UserFeatureService,
}); });
const information = useWorkspaceInfo(meta);
const name = information?.name ?? UNTITLED_WORKSPACE_NAME; const workspaceService = useService(WorkspaceService);
const currentWorkspaceId = useLiveData( const information = useWorkspaceInfo(workspaceService.workspace);
globalContextService.globalContext.workspaceId.$
);
const isCurrent = currentWorkspaceId === meta.id;
const t = useI18n(); const t = useI18n();
useEffect(() => { useEffect(() => {
userFeatureService.userFeature.revalidate(); userFeatureService.userFeature.revalidate();
}, [userFeatureService]); }, [userFeatureService]);
const onClickPreference = useCallback(() => {
onClick('workspace:preference');
}, [onClick]);
const showBilling = information?.isTeam && information?.isOwner; const showBilling = information?.isTeam && information?.isOwner;
const subTabs = useMemo(() => { const subTabs = useMemo(() => {
const subTabConfigs = [ const subTabConfigs = [
{ {
key: 'workspace:preference', key: 'workspace:preference',
title: 'com.affine.settings.workspace.preferences', title: 'com.affine.settings.workspace.preferences',
icon: <SettingsIcon />,
}, },
{ {
key: 'workspace:properties', key: 'workspace:properties',
title: 'com.affine.settings.workspace.properties', title: 'com.affine.settings.workspace.properties',
icon: <PropertyIcon />,
}, },
...(showBilling ...(showBilling
? [ ? [
{ {
key: 'workspace:billing' as SettingTab, key: 'workspace:billing' as SettingTab,
title: 'com.affine.settings.workspace.billing', title: 'com.affine.settings.workspace.billing',
icon: <PaymentIcon />,
}, },
] ]
: []), : []),
] satisfies { ] satisfies {
key: SettingTab; key: SettingTab;
title: keyof ReturnType<typeof useI18n>; title: keyof ReturnType<typeof useI18n>;
icon: ReactNode;
}[]; }[];
return subTabConfigs.map(({ key, title }) => { return subTabConfigs.map(({ key, title, icon }) => {
return ( return (
<div <SettingSidebarItem
isActive={activeTab === key}
label={t[title]()}
icon={icon}
data-testid={`workspace-list-item-${key}`} data-testid={`workspace-list-item-${key}`}
onClick={() => { onClick={() => {
onClick(key); onWorkspaceSettingClick(key);
}} }}
className={clsx(style.sidebarSelectSubItem, { className={clsx(style.sidebarSelectItem, {
active: activeTab === key, active: activeTab === key,
})} })}
key={key} key={key}
> />
{t[title]()}
</div>
); );
}); });
}, [activeTab, onClick, showBilling, t]); }, [activeTab, onWorkspaceSettingClick, showBilling, t]);
return ( return (
<> <div className={style.sidebarItemsWrapper}>
<div {/* TODO: remove the suspense? */}
className={clsx(style.sidebarSelectItem, { active: !!activeTab })} <Suspense fallback={<WorkspaceListSkeleton />}>{subTabs}</Suspense>
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> </div>
{activeTab && subTabs.length > 1 ? subTabs : null}
</>
); );
}; };

View File

@@ -1,28 +1,29 @@
import { cssVar } from '@toeverything/theme'; import { cssVar } from '@toeverything/theme';
import { cssVarV2 } from '@toeverything/theme/v2';
import { globalStyle, style } from '@vanilla-extract/css'; import { globalStyle, style } from '@vanilla-extract/css';
export const settingSlideBar = style({ export const settingSlideBar = style({
width: '25%', width: '25%',
maxWidth: '242px', maxWidth: '242px',
background: cssVar('backgroundSecondaryColor'), background: cssVar('backgroundSecondaryColor'),
padding: '20px 0px', padding: '20px 12px',
height: '100%', height: '100%',
flexShrink: 0, flexShrink: 0,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
gap: '16px',
overflowY: 'auto',
}); });
export const sidebarTitle = style({ export const sidebarTitle = style({
fontSize: cssVar('fontH6'), fontSize: cssVar('fontH6'),
fontWeight: '600', fontWeight: '600',
lineHeight: cssVar('lineHeight'), lineHeight: cssVar('lineHeight'),
padding: '0px 16px 0px 24px', padding: '0 8px',
}); });
export const sidebarSubtitle = style({ export const sidebarSubtitle = style({
fontSize: cssVar('fontSm'), fontSize: cssVar('fontSm'),
lineHeight: cssVar('lineHeight'), lineHeight: cssVar('lineHeight'),
color: cssVar('textSecondaryColor'), color: cssVar('textSecondaryColor'),
padding: '0px 16px 0px 24px', padding: '4px 8px',
marginTop: '20px',
marginBottom: '4px',
display: 'flex', display: 'flex',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
@@ -31,18 +32,11 @@ export const sidebarItemsWrapper = style({
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
gap: 4, gap: 4,
selectors: {
'&.scroll': {
flexGrow: 1,
overflowY: 'auto',
},
},
}); });
export const sidebarSelectItem = style({ export const sidebarSelectItem = style({
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
margin: '0px 16px', padding: '4px 8px',
padding: '0px 8px',
height: '30px', height: '30px',
flexShrink: 0, flexShrink: 0,
fontSize: cssVar('fontSm'), fontSize: cssVar('fontSm'),
@@ -76,19 +70,23 @@ export const sidebarSelectSubItem = style({
}, },
}, },
}); });
globalStyle(`${settingSlideBar} .icon`, {
export const sidebarSelectItemIcon = style({
width: '16px', width: '16px',
height: '16px', height: '16px',
marginRight: '10px', marginRight: '10px',
flexShrink: 0, flexShrink: 0,
color: cssVarV2('icon/primary'),
}); });
globalStyle(`${settingSlideBar} .setting-name`, {
export const sidebarSelectItemName = style({
minWidth: 0, minWidth: 0,
overflow: 'hidden', overflow: 'hidden',
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
flexGrow: 1, flexGrow: 1,
}); });
export const currentWorkspaceLabel = style({ export const currentWorkspaceLabel = style({
width: '20px', width: '20px',
height: '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({ export const accountButton = style({
padding: '4px 8px', padding: '4px 8px',
borderRadius: '8px', borderRadius: '8px',

View File

@@ -1,8 +1,6 @@
import type { SettingTab } from '@affine/core/modules/dialogs/constant'; import type { SettingTab } from '@affine/core/modules/dialogs/constant';
import type { WorkspaceMetadata } from '@affine/core/modules/workspace';
export interface SettingState { export interface SettingState {
activeTab: SettingTab; activeTab: SettingTab;
activeWorkspaceMetadata?: WorkspaceMetadata | null;
scrollAnchor?: string; scrollAnchor?: string;
} }

View File

@@ -8,7 +8,6 @@ import {
import { getUpgradeQuestionnaireLink } from '@affine/core/components/hooks/affine/use-subscription-notify'; import { getUpgradeQuestionnaireLink } from '@affine/core/components/hooks/affine/use-subscription-notify';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { useMutation } from '@affine/core/components/hooks/use-mutation'; import { useMutation } from '@affine/core/components/hooks/use-mutation';
import { useWorkspace } from '@affine/core/components/hooks/use-workspace';
import { import {
AuthService, AuthService,
SubscriptionService, SubscriptionService,
@@ -17,7 +16,7 @@ import {
} from '@affine/core/modules/cloud'; } from '@affine/core/modules/cloud';
import { WorkspaceQuotaService } from '@affine/core/modules/quota'; import { WorkspaceQuotaService } from '@affine/core/modules/quota';
import { UrlService } from '@affine/core/modules/url'; import { UrlService } from '@affine/core/modules/url';
import type { WorkspaceMetadata } from '@affine/core/modules/workspace'; import { WorkspaceService } from '@affine/core/modules/workspace';
import { import {
createCustomerPortalMutation, createCustomerPortalMutation,
type InvoicesQuery, type InvoicesQuery,
@@ -27,7 +26,7 @@ import {
UserFriendlyError, UserFriendlyError,
} from '@affine/graphql'; } from '@affine/graphql';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { FrameworkScope, useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
import { cssVar } from '@toeverything/theme'; import { cssVar } from '@toeverything/theme';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
@@ -37,14 +36,8 @@ import {
} from '../../general-setting/plans/actions'; } from '../../general-setting/plans/actions';
import * as styles from './styles.css'; import * as styles from './styles.css';
export const WorkspaceSettingBilling = ({ export const WorkspaceSettingBilling = () => {
workspaceMetadata, const workspace = useService(WorkspaceService).workspace;
}: {
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);
const t = useI18n(); const t = useI18n();
@@ -68,7 +61,7 @@ export const WorkspaceSettingBilling = ({
} }
return ( return (
<FrameworkScope scope={workspace.scope}> <>
<SettingHeader <SettingHeader
title={t['com.affine.payment.billing-setting.title']()} title={t['com.affine.payment.billing-setting.title']()}
subtitle={t['com.affine.payment.billing-setting.subtitle']()} subtitle={t['com.affine.payment.billing-setting.subtitle']()}
@@ -87,7 +80,7 @@ export const WorkspaceSettingBilling = ({
<SettingWrapper title={t['com.affine.payment.billing-setting.history']()}> <SettingWrapper title={t['com.affine.payment.billing-setting.history']()}>
<BillingHistory /> <BillingHistory />
</SettingWrapper> </SettingWrapper>
</FrameworkScope> </>
); );
}; };

View File

@@ -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 { 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 type { SettingState } from '../types';
import { WorkspaceSettingBilling } from './billing'; import { WorkspaceSettingBilling } from './billing';
@@ -7,31 +8,32 @@ import { WorkspaceSettingDetail } from './new-workspace-setting-detail';
import { WorkspaceSettingProperties } from './properties'; import { WorkspaceSettingProperties } from './properties';
export const WorkspaceSetting = ({ export const WorkspaceSetting = ({
workspaceMetadata,
activeTab, activeTab,
onCloseSetting, onCloseSetting,
onChangeSettingState, onChangeSettingState,
}: { }: {
workspaceMetadata: WorkspaceMetadata;
activeTab: SettingTab; activeTab: SettingTab;
onCloseSetting: () => void; onCloseSetting: () => void;
onChangeSettingState: (settingState: SettingState) => void; onChangeSettingState: (settingState: SettingState) => void;
}) => { }) => {
const element = useMemo(() => {
switch (activeTab) { switch (activeTab) {
case 'workspace:preference': case 'workspace:preference':
return ( return (
<WorkspaceSettingDetail <WorkspaceSettingDetail
onCloseSetting={onCloseSetting} onCloseSetting={onCloseSetting}
onChangeSettingState={onChangeSettingState} onChangeSettingState={onChangeSettingState}
workspaceMetadata={workspaceMetadata}
/> />
); );
case 'workspace:properties': case 'workspace:properties':
return ( return <WorkspaceSettingProperties />;
<WorkspaceSettingProperties workspaceMetadata={workspaceMetadata} />
);
case 'workspace:billing': case 'workspace:billing':
return <WorkspaceSettingBilling workspaceMetadata={workspaceMetadata} />; return <WorkspaceSettingBilling />;
} default:
return null; return null;
}
}, [activeTab, onCloseSetting, onChangeSettingState]);
return (
<CurrentWorkspaceScopeProvider>{element}</CurrentWorkspaceScopeProvider>
);
}; };

View File

@@ -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 { useSystemOnline } from '@affine/core/components/hooks/use-system-online';
import { DesktopApiService } from '@affine/core/modules/desktop-api'; import { DesktopApiService } from '@affine/core/modules/desktop-api';
import { WorkspacePermissionService } from '@affine/core/modules/permissions'; import { WorkspacePermissionService } from '@affine/core/modules/permissions';
import type { import type { Workspace } from '@affine/core/modules/workspace';
Workspace,
WorkspaceMetadata,
} from '@affine/core/modules/workspace';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import track from '@affine/track'; import track from '@affine/track';
import { useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
import { useState } from 'react'; import { useState } from 'react';
interface ExportPanelProps { interface ExportPanelProps {
workspaceMetadata: WorkspaceMetadata; workspace: Workspace;
workspace: Workspace | null;
} }
export const DesktopExportPanel = ({ export const DesktopExportPanel = ({ workspace }: ExportPanelProps) => {
workspaceMetadata, const workspaceId = workspace.id;
workspace,
}: ExportPanelProps) => {
const workspaceId = workspaceMetadata.id;
const workspacePermissionService = useService( const workspacePermissionService = useService(
WorkspacePermissionService WorkspacePermissionService
).permission; ).permission;

View File

@@ -3,13 +3,13 @@ import {
SettingRow, SettingRow,
SettingWrapper, SettingWrapper,
} from '@affine/component/setting-components'; } 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 { useWorkspaceInfo } from '@affine/core/components/hooks/use-workspace-info';
import { WorkspaceServerService } from '@affine/core/modules/cloud'; import { WorkspaceServerService } from '@affine/core/modules/cloud';
import { WorkspaceService } from '@affine/core/modules/workspace';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant'; import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { ArrowRightSmallIcon } from '@blocksuite/icons/rc'; import { ArrowRightSmallIcon } from '@blocksuite/icons/rc';
import { FrameworkScope } from '@toeverything/infra'; import { FrameworkScope, useService } from '@toeverything/infra';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { DeleteLeaveWorkspace } from './delete-leave-workspace'; import { DeleteLeaveWorkspace } from './delete-leave-workspace';
@@ -24,18 +24,15 @@ import type { WorkspaceSettingDetailProps } from './types';
import { WorkspaceQuotaPanel } from './workspace-quota'; import { WorkspaceQuotaPanel } from './workspace-quota';
export const WorkspaceSettingDetail = ({ export const WorkspaceSettingDetail = ({
workspaceMetadata,
onCloseSetting, onCloseSetting,
onChangeSettingState, onChangeSettingState,
}: WorkspaceSettingDetailProps) => { }: WorkspaceSettingDetailProps) => {
const t = useI18n(); const t = useI18n();
// useWorkspace hook is a vary heavy operation here, but we need syncing name and avatar changes here, const workspace = useService(WorkspaceService).workspace;
// we don't have a better way to do this now
const workspace = useWorkspace(workspaceMetadata);
const server = workspace?.scope.get(WorkspaceServerService).server; const server = workspace?.scope.get(WorkspaceServerService).server;
const workspaceInfo = useWorkspaceInfo(workspaceMetadata); const workspaceInfo = useWorkspaceInfo(workspace);
const handleResetSyncStatus = useCallback(() => { const handleResetSyncStatus = useCallback(() => {
workspace?.engine.doc workspace?.engine.doc
@@ -80,10 +77,7 @@ export const WorkspaceSettingDetail = ({
<SharingPanel /> <SharingPanel />
{BUILD_CONFIG.isElectron && ( {BUILD_CONFIG.isElectron && (
<SettingWrapper title={t['Storage and Export']()}> <SettingWrapper title={t['Storage and Export']()}>
<DesktopExportPanel <DesktopExportPanel workspace={workspace} />
workspace={workspace}
workspaceMetadata={workspaceMetadata}
/>
</SettingWrapper> </SettingWrapper>
)} )}
<SettingWrapper> <SettingWrapper>

View File

@@ -1,9 +1,6 @@
import type { WorkspaceMetadata } from '@affine/core/modules/workspace';
import type { SettingState } from '../../types'; import type { SettingState } from '../../types';
export interface WorkspaceSettingDetailProps { export interface WorkspaceSettingDetailProps {
workspaceMetadata: WorkspaceMetadata;
onCloseSetting: () => void; onCloseSetting: () => void;
onChangeSettingState: (settingState: SettingState) => void; onChangeSettingState: (settingState: SettingState) => void;
} }

View File

@@ -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 { CreatePropertyMenuItems } from '@affine/core/components/doc-properties/menu/create-doc-property';
import { useWorkspaceInfo } from '@affine/core/components/hooks/use-workspace-info'; import { useWorkspaceInfo } from '@affine/core/components/hooks/use-workspace-info';
import type { DocCustomPropertyInfo } from '@affine/core/modules/db'; 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 { Trans, useI18n } from '@affine/i18n';
import track from '@affine/track'; import track from '@affine/track';
import { FrameworkScope } from '@toeverything/infra'; import { FrameworkScope, useService } from '@toeverything/infra';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useWorkspace } from '../../../../../components/hooks/use-workspace';
import * as styles from './styles.css'; import * as styles from './styles.css';
const WorkspaceSettingPropertiesMain = () => { const WorkspaceSettingPropertiesMain = () => {
@@ -47,14 +46,10 @@ const WorkspaceSettingPropertiesMain = () => {
); );
}; };
export const WorkspaceSettingProperties = ({ export const WorkspaceSettingProperties = () => {
workspaceMetadata,
}: {
workspaceMetadata: WorkspaceMetadata;
}) => {
const t = useI18n(); const t = useI18n();
const workspace = useWorkspace(workspaceMetadata); const workspace = useService(WorkspaceService).workspace;
const workspaceInfo = useWorkspaceInfo(workspaceMetadata); const workspaceInfo = useWorkspaceInfo(workspace);
const title = workspaceInfo?.name || 'untitled'; const title = workspaceInfo?.name || 'untitled';
if (workspace === null) { if (workspace === null) {

View File

@@ -265,11 +265,7 @@ export function FallbackHeaderWithWorkspaceNavigator() {
return ( return (
<div className={styles.fallbackHeader}> <div className={styles.fallbackHeader}>
{currentWorkspace && navigate ? ( {currentWorkspace && navigate ? (
<WorkspaceNavigator <WorkspaceNavigator showSyncStatus showEnableCloudButton />
showSettingsButton
showSyncStatus
showEnableCloudButton
/>
) : ( ) : (
<FallbackHeaderSkeleton /> <FallbackHeaderSkeleton />
)} )}

View File

@@ -35,7 +35,8 @@ export type { ServerConfig } from './types';
import { type Framework } from '@toeverything/infra'; 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 { GlobalCache, GlobalState, GlobalStateService } from '../storage';
import { UrlService } from '../url'; import { UrlService } from '../url';
import { WorkspaceScope, WorkspaceService } from '../workspace'; import { WorkspaceScope, WorkspaceService } from '../workspace';

View File

@@ -1,6 +1,6 @@
import type { Framework } from '@toeverything/infra'; import type { Framework } from '@toeverything/infra';
import { WorkspaceServerService } from '../cloud'; import { WorkspaceServerService } from '../cloud/services/workspace-server';
import { WorkspaceScope, WorkspaceService } from '../workspace'; import { WorkspaceScope, WorkspaceService } from '../workspace';
import { WorkspaceDB } from './entities/db'; import { WorkspaceDB } from './entities/db';
import { WorkspaceDBTable } from './entities/table'; import { WorkspaceDBTable } from './entities/table';

View File

@@ -26,11 +26,7 @@ export type GLOBAL_DIALOG_SCHEMA = {
templateMode: DocMode; templateMode: DocMode;
snapshotUrl: string; snapshotUrl: string;
}) => void; }) => void;
setting: (props: { setting: (props: { activeTab?: SettingTab; scrollAnchor?: string }) => void;
activeTab?: SettingTab;
workspaceMetadata?: WorkspaceMetadata | null;
scrollAnchor?: string;
}) => void;
'sign-in': (props: { server?: string; step?: string }) => void; 'sign-in': (props: { server?: string; step?: string }) => void;
'change-password': (props: { server?: string }) => void; 'change-password': (props: { server?: string }) => void;
'verify-email': (props: { server?: string; changeEmail?: boolean }) => void; 'verify-email': (props: { server?: string; changeEmail?: boolean }) => void;

View File

@@ -8,7 +8,7 @@ export { DocsService } from './services/docs';
import type { Framework } from '@toeverything/infra'; import type { Framework } from '@toeverything/infra';
import { WorkspaceDBService } from '../db'; import { WorkspaceDBService } from '../db/services/db';
import { WorkspaceScope, WorkspaceService } from '../workspace'; import { WorkspaceScope, WorkspaceService } from '../workspace';
import { Doc } from './entities/doc'; import { Doc } from './entities/doc';
import { DocPropertyList } from './entities/property-list'; import { DocPropertyList } from './entities/property-list';

View File

@@ -58,7 +58,7 @@ test('should have pagination in member list', async ({ page }) => {
); );
await openSettingModal(page); await openSettingModal(page);
await openWorkspaceSettingPanel(page, 'test'); await openWorkspaceSettingPanel(page);
await page.waitForTimeout(1000); await page.waitForTimeout(1000);

View File

@@ -121,7 +121,7 @@ test('delete workspace', async ({ page }) => {
// }); // });
await page.waitForTimeout(1000); await page.waitForTimeout(1000);
await clickSideBarSettingButton(page); await clickSideBarSettingButton(page);
await page.getByTestId('current-workspace-label').click(); await page.getByTestId('workspace-list-item-workspace:preference').click();
await expect(page.getByTestId('workspace-name-input')).toHaveValue( await expect(page.getByTestId('workspace-name-input')).toHaveValue(
'Delete Me' 'Delete Me'
); );

View File

@@ -42,7 +42,7 @@ test('export then add', async ({ page, appInfo, workspace }) => {
const newWorkspaceName = 'new-test-name'; const newWorkspaceName = 'new-test-name';
// goto workspace setting // goto workspace setting
await page.getByTestId('workspace-list-item').click(); await page.getByTestId('workspace-list-item-workspace:preference').click();
const input = page.getByTestId('workspace-name-input'); const input = page.getByTestId('workspace-name-input');
await expect(input).toBeVisible(); await expect(input).toBeVisible();

View File

@@ -24,7 +24,7 @@ test('should create a page with a local first avatar and remove it', async ({
.nth(1) .nth(1)
.click({ position: { x: 10, y: 10 } }); .click({ position: { x: 10, y: 10 } });
await page.getByTestId('settings-modal-trigger').click(); await page.getByTestId('settings-modal-trigger').click();
await page.getByTestId('current-workspace-label').click(); await page.getByTestId('workspace-list-item-workspace:preference').click();
await page await page
.getByTestId('upload-avatar') .getByTestId('upload-avatar')
.setInputFiles(ProjectRoot.join('tests', 'fixtures', 'blue.png').value); .setInputFiles(ProjectRoot.join('tests', 'fixtures', 'blue.png').value);
@@ -61,7 +61,7 @@ test('should create a page with a local first avatar and remove it', async ({
// Click remove button to remove workspace avatar // Click remove button to remove workspace avatar
await page.getByTestId('settings-modal-trigger').click(); await page.getByTestId('settings-modal-trigger').click();
await page.getByTestId('current-workspace-label').click(); await page.getByTestId('workspace-list-item-workspace:preference').click();
await page.getByTestId('workspace-setting-avatar').hover(); await page.getByTestId('workspace-setting-avatar').hover();
await page.getByTestId('workspace-setting-remove-avatar-button').click(); await page.getByTestId('workspace-setting-remove-avatar-button').click();
await page.mouse.click(0, 0); await page.mouse.click(0, 0);

View File

@@ -26,7 +26,7 @@ test('Create new workspace, then delete it', async ({ page, workspace }) => {
'Test Workspace' 'Test Workspace'
); );
await openSettingModal(page); await openSettingModal(page);
await openWorkspaceSettingPanel(page, 'Test Workspace'); await openWorkspaceSettingPanel(page);
await page.getByTestId('delete-workspace-button').click(); await page.getByTestId('delete-workspace-button').click();
await expect( await expect(
page.locator('.affine-notification-center').first() page.locator('.affine-notification-center').first()
@@ -64,7 +64,7 @@ test('Delete last workspace', async ({ page }) => {
node => node.textContent node => node.textContent
); );
await openSettingModal(page); await openSettingModal(page);
await openWorkspaceSettingPanel(page, currentWorkspaceName as string); await openWorkspaceSettingPanel(page);
await page.getByTestId('delete-workspace-button').click(); await page.getByTestId('delete-workspace-button').click();
await page await page
.getByTestId('delete-workspace-input') .getByTestId('delete-workspace-input')

View File

@@ -10,7 +10,6 @@ import {
openSettingModal, openSettingModal,
openShortcutsPanel, openShortcutsPanel,
} from '@affine-test/kit/utils/setting'; } from '@affine-test/kit/utils/setting';
import { createLocalWorkspace } from '@affine-test/kit/utils/workspace';
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
test('Open settings modal', async ({ page }) => { test('Open settings modal', async ({ page }) => {
@@ -104,21 +103,3 @@ test('Open experimental features panel', async ({ page }) => {
const settings = page.getByTestId('experimental-settings'); const settings = page.getByTestId('experimental-settings');
await expect(settings).toBeVisible(); await expect(settings).toBeVisible();
}); });
test('Different workspace should have different name in the setting panel', async ({
page,
}) => {
await openHomePage(page);
await waitForEditorLoad(page);
await createLocalWorkspace({ name: 'New Workspace 2' }, page);
await createLocalWorkspace({ name: 'New Workspace 3' }, page);
await openSettingModal(page);
await page.getByTestId('current-workspace-label').click();
await expect(page.getByTestId('workspace-name-input')).toHaveValue(
'New Workspace 3'
);
await page.getByText('New Workspace 2').click();
await expect(page.getByTestId('workspace-name-input')).toHaveValue(
'New Workspace 2'
);
});

View File

@@ -282,7 +282,7 @@ export async function loginUserDirectly(
export async function enableCloudWorkspace(page: Page) { export async function enableCloudWorkspace(page: Page) {
await clickSideBarSettingButton(page); await clickSideBarSettingButton(page);
await page.getByTestId('current-workspace-label').click(); await page.getByTestId('workspace-list-item-workspace:preference').click();
await page.getByTestId('publish-enable-affine-cloud-button').click(); await page.getByTestId('publish-enable-affine-cloud-button').click();
await page.getByTestId('confirm-enable-affine-cloud-button').click(); await page.getByTestId('confirm-enable-affine-cloud-button').click();
// wait for upload and delete local workspace // wait for upload and delete local workspace

View File

@@ -149,9 +149,6 @@ export const expectPropertyOrdering = async (
export const openWorkspaceProperties = async (page: Page) => { export const openWorkspaceProperties = async (page: Page) => {
await page.getByTestId('slider-bar-workspace-setting-button').click(); await page.getByTestId('slider-bar-workspace-setting-button').click();
await page
.locator('[data-testid="workspace-list-item"] .setting-name')
.click();
await page.getByTestId('workspace-list-item-workspace:properties').click(); await page.getByTestId('workspace-list-item-workspace:properties').click();
}; };

View File

@@ -38,11 +38,11 @@ export async function confirmExperimentalPrompt(page: Page) {
await page.getByTestId('experimental-confirm-button').click(); await page.getByTestId('experimental-confirm-button').click();
} }
export async function openWorkspaceSettingPanel( export async function openWorkspaceSettingPanel(page: Page) {
page: Page, await page
workspaceName: string .getByTestId('settings-sidebar')
) { .getByTestId('workspace-list-item-workspace:preference')
await page.getByTestId('settings-sidebar').getByText(workspaceName).click(); .click();
} }
export async function clickUserInfoCard(page: Page) { export async function clickUserInfoCard(page: Page) {