mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-23 08:21:46 +03:00
refactor: workspace list (#4432)
This commit is contained in:
parent
092e2e0a3d
commit
edd7d00104
@ -0,0 +1,24 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const ItemContainer = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
padding: '8px 14px',
|
||||
gap: '14px',
|
||||
cursor: 'pointer',
|
||||
borderRadius: '8px',
|
||||
transition: 'background-color 0.2s',
|
||||
fontSize: '24px',
|
||||
color: 'var(--affine-icon-secondary)',
|
||||
});
|
||||
|
||||
export const ItemText = style({
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
lineHeight: '22px',
|
||||
color: 'var(--affine-text-secondary-color)',
|
||||
fontWeight: 400,
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
});
|
@ -0,0 +1,44 @@
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { ImportIcon, PlusIcon } from '@blocksuite/icons';
|
||||
import { MenuItem } from '@toeverything/components/menu';
|
||||
|
||||
import * as styles from './index.css';
|
||||
|
||||
export const AddWorkspace = ({
|
||||
onAddWorkspace,
|
||||
onNewWorkspace,
|
||||
}: {
|
||||
onAddWorkspace?: () => void;
|
||||
onNewWorkspace?: () => void;
|
||||
}) => {
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runtimeConfig.enableSQLiteProvider && environment.isDesktop ? (
|
||||
<MenuItem
|
||||
block={true}
|
||||
preFix={<ImportIcon />}
|
||||
onClick={onAddWorkspace}
|
||||
data-testid="add-workspace"
|
||||
className={styles.ItemContainer}
|
||||
>
|
||||
<div className={styles.ItemText}>
|
||||
{t['com.affine.workspace.local.import']()}
|
||||
</div>
|
||||
</MenuItem>
|
||||
) : null}
|
||||
<MenuItem
|
||||
block={true}
|
||||
preFix={<PlusIcon />}
|
||||
onClick={onNewWorkspace}
|
||||
data-testid="new-workspace"
|
||||
className={styles.ItemContainer}
|
||||
>
|
||||
<div className={styles.ItemText}>
|
||||
{t['com.affine.workspaceList.addWorkspace.create']()}
|
||||
</div>
|
||||
</MenuItem>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,57 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const workspaceListWrapper = style({
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
});
|
||||
|
||||
export const signInWrapper = style({
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
gap: '12px',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
borderRadius: '8px',
|
||||
});
|
||||
|
||||
export const iconContainer = style({
|
||||
width: '28px',
|
||||
padding: '2px 4px 4px',
|
||||
borderRadius: '14px',
|
||||
background: 'var(--affine-white)',
|
||||
display: 'flex',
|
||||
border: '1px solid var(--affine-icon-secondary)',
|
||||
color: 'var(--affine-icon-secondary)',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: '20px',
|
||||
});
|
||||
|
||||
export const signInTextContainer = style({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
});
|
||||
|
||||
export const signInTextPrimary = style({
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
fontWeight: 600,
|
||||
lineHeight: '22px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
});
|
||||
|
||||
export const signInTextSecondary = style({
|
||||
fontSize: 'var(--affine-font-xs)',
|
||||
fontWeight: 400,
|
||||
lineHeight: '20px',
|
||||
color: 'var(--affine-text-secondary-color)',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
});
|
||||
|
||||
export const menuItem = style({
|
||||
borderRadius: '8px',
|
||||
});
|
@ -1,155 +1,62 @@
|
||||
import { WorkspaceList } from '@affine/component/workspace-list';
|
||||
import type {
|
||||
AffineCloudWorkspace,
|
||||
LocalWorkspace,
|
||||
} from '@affine/env/workspace';
|
||||
import { WorkspaceFlavour, WorkspaceSubPath } from '@affine/env/workspace';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
|
||||
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
||||
import {
|
||||
AccountIcon,
|
||||
ImportIcon,
|
||||
Logo1Icon,
|
||||
MoreHorizontalIcon,
|
||||
PlusIcon,
|
||||
SignOutIcon,
|
||||
} from '@blocksuite/icons';
|
||||
import type { DragEndEvent } from '@dnd-kit/core';
|
||||
import { arrayMove } from '@dnd-kit/sortable';
|
||||
import { IconButton } from '@toeverything/components/button';
|
||||
import { Logo1Icon } from '@blocksuite/icons';
|
||||
import { Divider } from '@toeverything/components/divider';
|
||||
import { Menu, MenuIcon, MenuItem } from '@toeverything/components/menu';
|
||||
import {
|
||||
currentPageIdAtom,
|
||||
currentWorkspaceIdAtom,
|
||||
} from '@toeverything/infra/atom';
|
||||
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
|
||||
import { MenuItem } from '@toeverything/components/menu';
|
||||
import { useAtomValue, useSetAtom } from 'jotai';
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { startTransition, useCallback, useMemo, useTransition } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import {
|
||||
authAtom,
|
||||
openCreateWorkspaceModalAtom,
|
||||
openDisableCloudAlertModalAtom,
|
||||
openSettingModalAtom,
|
||||
} from '../../../../atoms';
|
||||
import type { AllWorkspace } from '../../../../shared';
|
||||
import { signOutCloud } from '../../../../utils/cloud-utils';
|
||||
import { useNavigateHelper } from '../.././../../hooks/use-navigate-helper';
|
||||
import {
|
||||
StyledCreateWorkspaceCardPill,
|
||||
StyledCreateWorkspaceCardPillContent,
|
||||
StyledCreateWorkspaceCardPillIcon,
|
||||
StyledImportWorkspaceCardPill,
|
||||
StyledItem,
|
||||
StyledModalBody,
|
||||
StyledModalContent,
|
||||
StyledModalFooterContent,
|
||||
StyledModalHeader,
|
||||
StyledModalHeaderContent,
|
||||
StyledModalHeaderLeft,
|
||||
StyledModalTitle,
|
||||
StyledOperationWrapper,
|
||||
StyledSignInCardPill,
|
||||
StyledSignInCardPillTextCotainer,
|
||||
StyledSignInCardPillTextPrimary,
|
||||
StyledSignInCardPillTextSecondary,
|
||||
StyledWorkspaceFlavourTitle,
|
||||
} from './styles';
|
||||
import { AddWorkspace } from './add-workspace';
|
||||
import * as styles from './index.css';
|
||||
import { UserAccountItem } from './user-account';
|
||||
import { AFFiNEWorkspaceList } from './workspace-list';
|
||||
|
||||
interface WorkspaceModalProps {
|
||||
disabled?: boolean;
|
||||
workspaces: RootWorkspaceMetadata[];
|
||||
currentWorkspaceId: AllWorkspace['id'] | null;
|
||||
onClickWorkspace: (workspace: RootWorkspaceMetadata['id']) => void;
|
||||
onClickWorkspaceSetting: (workspace: RootWorkspaceMetadata['id']) => void;
|
||||
onNewWorkspace: () => void;
|
||||
onAddWorkspace: () => void;
|
||||
onMoveWorkspace: (activeId: string, overId: string) => void;
|
||||
}
|
||||
const SignInItem = () => {
|
||||
const setDisableCloudOpen = useSetAtom(openDisableCloudAlertModalAtom);
|
||||
|
||||
const setOpen = useSetAtom(authAtom);
|
||||
|
||||
const AccountMenu = ({
|
||||
onOpenAccountSetting,
|
||||
onSignOut,
|
||||
}: {
|
||||
onOpenAccountSetting: () => void;
|
||||
onSignOut: () => void;
|
||||
}) => {
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MenuItem
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<AccountIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
data-testid="editor-option-menu-import"
|
||||
onClick={onOpenAccountSetting}
|
||||
>
|
||||
{t['com.affine.workspace.cloud.account.settings']()}
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<SignOutIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
data-testid="editor-option-menu-import"
|
||||
onClick={onSignOut}
|
||||
>
|
||||
{t['com.affine.workspace.cloud.account.logout']()}
|
||||
</MenuItem>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CloudWorkSpaceList = ({
|
||||
disabled,
|
||||
workspaces,
|
||||
onClickWorkspace,
|
||||
onClickWorkspaceSetting,
|
||||
currentWorkspaceId,
|
||||
onMoveWorkspace,
|
||||
}: WorkspaceModalProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const onClickSignIn = useCallback(async () => {
|
||||
if (!runtimeConfig.enableCloud) {
|
||||
setDisableCloudOpen(true);
|
||||
} else {
|
||||
setOpen(state => ({
|
||||
...state,
|
||||
openModal: true,
|
||||
}));
|
||||
}
|
||||
}, [setOpen, setDisableCloudOpen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledModalHeader>
|
||||
<StyledModalHeaderLeft>
|
||||
<StyledWorkspaceFlavourTitle>
|
||||
{t['com.affine.workspace.cloud']()}
|
||||
</StyledWorkspaceFlavourTitle>
|
||||
</StyledModalHeaderLeft>
|
||||
</StyledModalHeader>
|
||||
<StyledModalContent>
|
||||
<WorkspaceList
|
||||
disabled={disabled}
|
||||
items={
|
||||
workspaces.filter(
|
||||
({ flavour }) => flavour === WorkspaceFlavour.AFFINE_CLOUD
|
||||
) as (AffineCloudWorkspace | LocalWorkspace)[]
|
||||
}
|
||||
currentWorkspaceId={currentWorkspaceId}
|
||||
onClick={onClickWorkspace}
|
||||
onSettingClick={onClickWorkspaceSetting}
|
||||
onDragEnd={useCallback(
|
||||
(event: DragEndEvent) => {
|
||||
const { active, over } = event;
|
||||
if (active.id !== over?.id) {
|
||||
onMoveWorkspace(active.id as string, over?.id as string);
|
||||
}
|
||||
},
|
||||
[onMoveWorkspace]
|
||||
)}
|
||||
/>
|
||||
</StyledModalContent>
|
||||
</>
|
||||
<MenuItem
|
||||
className={styles.menuItem}
|
||||
onClick={onClickSignIn}
|
||||
data-testid="cloud-signin-button"
|
||||
>
|
||||
<div className={styles.signInWrapper}>
|
||||
<div className={styles.iconContainer}>
|
||||
<Logo1Icon />
|
||||
</div>
|
||||
|
||||
<div className={styles.signInTextContainer}>
|
||||
<div className={styles.signInTextPrimary}>
|
||||
{t['com.affine.workspace.cloud.auth']()}
|
||||
</div>
|
||||
<div className={styles.signInTextSecondary}>
|
||||
{t['com.affine.workspace.cloud.description']()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MenuItem>
|
||||
);
|
||||
};
|
||||
|
||||
@ -158,240 +65,43 @@ export const UserWithWorkspaceList = ({
|
||||
}: {
|
||||
onEventEnd?: () => void;
|
||||
}) => {
|
||||
const { data: session, status } = useSession();
|
||||
|
||||
const isAuthenticated = useMemo(() => status === 'authenticated', [status]);
|
||||
|
||||
const setOpenCreateWorkspaceModal = useSetAtom(openCreateWorkspaceModalAtom);
|
||||
|
||||
const { jumpToSubPath, jumpToIndex } = useNavigateHelper();
|
||||
const workspaces = useAtomValue(rootWorkspacesMetadataAtom, {
|
||||
delay: 0,
|
||||
});
|
||||
const setWorkspaces = useSetAtom(rootWorkspacesMetadataAtom);
|
||||
const [currentWorkspaceId, setCurrentWorkspaceId] = useAtom(
|
||||
currentWorkspaceIdAtom
|
||||
);
|
||||
const setCurrentPageId = useSetAtom(currentPageIdAtom);
|
||||
const [, startCloseTransition] = useTransition();
|
||||
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||
const setSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||
|
||||
const t = useAFFiNEI18N();
|
||||
const setOpen = useSetAtom(authAtom);
|
||||
const setDisableCloudOpen = useSetAtom(openDisableCloudAlertModalAtom);
|
||||
// TODO: AFFiNE Cloud support
|
||||
const { data: session, status } = useSession();
|
||||
const isLoggedIn = useMemo(() => status === 'authenticated', [status]);
|
||||
const cloudWorkspaces = useMemo(
|
||||
() =>
|
||||
workspaces.filter(
|
||||
({ flavour }) => flavour === WorkspaceFlavour.AFFINE_CLOUD
|
||||
) as (AffineCloudWorkspace | LocalWorkspace)[],
|
||||
[workspaces]
|
||||
);
|
||||
const localWorkspaces = useMemo(
|
||||
() =>
|
||||
workspaces.filter(
|
||||
({ flavour }) => flavour === WorkspaceFlavour.LOCAL
|
||||
) as (AffineCloudWorkspace | LocalWorkspace)[],
|
||||
[workspaces]
|
||||
);
|
||||
|
||||
const onClickWorkspaceSetting = useCallback(
|
||||
(workspaceId: string) => {
|
||||
setOpenSettingModalAtom({
|
||||
open: true,
|
||||
activeTab: 'workspace',
|
||||
workspaceId,
|
||||
});
|
||||
onEventEnd?.();
|
||||
},
|
||||
[onEventEnd, setOpenSettingModalAtom]
|
||||
);
|
||||
|
||||
const onMoveWorkspace = useCallback(
|
||||
(activeId: string, overId: string) => {
|
||||
const oldIndex = workspaces.findIndex(w => w.id === activeId);
|
||||
const newIndex = workspaces.findIndex(w => w.id === overId);
|
||||
startTransition(() => {
|
||||
setWorkspaces(workspaces => arrayMove(workspaces, oldIndex, newIndex));
|
||||
});
|
||||
},
|
||||
[setWorkspaces, workspaces]
|
||||
);
|
||||
const onClickWorkspace = useCallback(
|
||||
(workspaceId: string) => {
|
||||
startCloseTransition(() => {
|
||||
setCurrentWorkspaceId(workspaceId);
|
||||
setCurrentPageId(null);
|
||||
jumpToSubPath(workspaceId, WorkspaceSubPath.ALL);
|
||||
});
|
||||
onEventEnd?.();
|
||||
},
|
||||
[jumpToSubPath, onEventEnd, setCurrentPageId, setCurrentWorkspaceId]
|
||||
);
|
||||
const onNewWorkspace = useCallback(() => {
|
||||
setOpenCreateWorkspaceModal('new');
|
||||
onEventEnd?.();
|
||||
}, [onEventEnd, setOpenCreateWorkspaceModal]);
|
||||
|
||||
const onAddWorkspace = useCallback(async () => {
|
||||
setOpenCreateWorkspaceModal('add');
|
||||
onEventEnd?.();
|
||||
}, [onEventEnd, setOpenCreateWorkspaceModal]);
|
||||
|
||||
const onOpenAccountSetting = useCallback(() => {
|
||||
setSettingModalAtom(prev => ({
|
||||
...prev,
|
||||
open: true,
|
||||
activeTab: 'account',
|
||||
}));
|
||||
onEventEnd?.();
|
||||
}, [onEventEnd, setSettingModalAtom]);
|
||||
const onSignOut = useCallback(async () => {
|
||||
signOutCloud()
|
||||
.then(() => {
|
||||
jumpToIndex();
|
||||
})
|
||||
.catch(console.error);
|
||||
onEventEnd?.();
|
||||
}, [onEventEnd, jumpToIndex]);
|
||||
const workspaces = useAtomValue(rootWorkspacesMetadataAtom, {
|
||||
delay: 0,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{!isLoggedIn ? (
|
||||
<StyledModalHeaderContent>
|
||||
<StyledSignInCardPill>
|
||||
<StyledItem
|
||||
onClick={async () => {
|
||||
if (!runtimeConfig.enableCloud) {
|
||||
setDisableCloudOpen(true);
|
||||
} else {
|
||||
setOpen(state => ({
|
||||
...state,
|
||||
openModal: true,
|
||||
}));
|
||||
}
|
||||
}}
|
||||
data-testid="cloud-signin-button"
|
||||
>
|
||||
<StyledCreateWorkspaceCardPillContent>
|
||||
<StyledCreateWorkspaceCardPillIcon>
|
||||
<Logo1Icon />
|
||||
</StyledCreateWorkspaceCardPillIcon>
|
||||
<StyledSignInCardPillTextCotainer>
|
||||
<StyledSignInCardPillTextPrimary>
|
||||
{t['com.affine.workspace.cloud.auth']()}
|
||||
</StyledSignInCardPillTextPrimary>
|
||||
<StyledSignInCardPillTextSecondary>
|
||||
{t['com.affine.workspace.cloud.description']()}
|
||||
</StyledSignInCardPillTextSecondary>
|
||||
</StyledSignInCardPillTextCotainer>
|
||||
</StyledCreateWorkspaceCardPillContent>
|
||||
</StyledItem>
|
||||
</StyledSignInCardPill>
|
||||
<Divider
|
||||
style={{
|
||||
margin: '12px 0px',
|
||||
}}
|
||||
/>
|
||||
</StyledModalHeaderContent>
|
||||
<div className={styles.workspaceListWrapper}>
|
||||
{isAuthenticated ? (
|
||||
<UserAccountItem
|
||||
email={session?.user.email ?? 'Unknown User'}
|
||||
onEventEnd={onEventEnd}
|
||||
/>
|
||||
) : (
|
||||
<StyledModalHeaderContent>
|
||||
<StyledModalHeader>
|
||||
<StyledModalTitle>{session?.user.email}</StyledModalTitle>
|
||||
<StyledOperationWrapper>
|
||||
<Menu
|
||||
items={
|
||||
<AccountMenu
|
||||
onOpenAccountSetting={onOpenAccountSetting}
|
||||
onSignOut={onSignOut}
|
||||
/>
|
||||
}
|
||||
contentOptions={{
|
||||
side: 'right',
|
||||
sideOffset: 30,
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
data-testid="more-button"
|
||||
icon={<MoreHorizontalIcon />}
|
||||
type="plain"
|
||||
/>
|
||||
</Menu>
|
||||
</StyledOperationWrapper>
|
||||
</StyledModalHeader>
|
||||
<Divider style={{ margin: '12px 0px' }} />
|
||||
</StyledModalHeaderContent>
|
||||
<SignInItem />
|
||||
)}
|
||||
<StyledModalBody>
|
||||
{isLoggedIn && cloudWorkspaces.length !== 0 ? (
|
||||
<>
|
||||
<CloudWorkSpaceList
|
||||
workspaces={workspaces}
|
||||
onClickWorkspace={onClickWorkspace}
|
||||
onClickWorkspaceSetting={onClickWorkspaceSetting}
|
||||
onNewWorkspace={onNewWorkspace}
|
||||
onAddWorkspace={onAddWorkspace}
|
||||
currentWorkspaceId={currentWorkspaceId}
|
||||
onMoveWorkspace={onMoveWorkspace}
|
||||
/>
|
||||
<Divider
|
||||
style={{
|
||||
margin: '12px 0px',
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
<StyledModalHeader>
|
||||
<StyledWorkspaceFlavourTitle>
|
||||
{t['com.affine.workspace.local']()}
|
||||
</StyledWorkspaceFlavourTitle>
|
||||
</StyledModalHeader>
|
||||
<StyledModalContent>
|
||||
<WorkspaceList
|
||||
items={localWorkspaces}
|
||||
currentWorkspaceId={currentWorkspaceId}
|
||||
onClick={onClickWorkspace}
|
||||
onSettingClick={onClickWorkspaceSetting}
|
||||
onDragEnd={useCallback(
|
||||
(event: DragEndEvent) => {
|
||||
const { active, over } = event;
|
||||
if (active.id !== over?.id) {
|
||||
onMoveWorkspace(active.id as string, over?.id as string);
|
||||
}
|
||||
},
|
||||
[onMoveWorkspace]
|
||||
)}
|
||||
/>
|
||||
</StyledModalContent>
|
||||
{runtimeConfig.enableSQLiteProvider && environment.isDesktop ? (
|
||||
<StyledImportWorkspaceCardPill>
|
||||
<StyledItem onClick={onAddWorkspace} data-testid="add-workspace">
|
||||
<StyledCreateWorkspaceCardPillContent
|
||||
style={{ gap: '14px', paddingLeft: '2px' }}
|
||||
>
|
||||
<StyledCreateWorkspaceCardPillIcon style={{ fontSize: '24px' }}>
|
||||
<ImportIcon />
|
||||
</StyledCreateWorkspaceCardPillIcon>
|
||||
<div>
|
||||
<p>{t['com.affine.workspace.local.import']()}</p>
|
||||
</div>
|
||||
</StyledCreateWorkspaceCardPillContent>
|
||||
</StyledItem>
|
||||
</StyledImportWorkspaceCardPill>
|
||||
) : null}
|
||||
</StyledModalBody>
|
||||
<StyledModalFooterContent>
|
||||
<StyledCreateWorkspaceCardPill>
|
||||
<StyledItem onClick={onNewWorkspace} data-testid="new-workspace">
|
||||
<StyledCreateWorkspaceCardPillContent>
|
||||
<StyledCreateWorkspaceCardPillIcon>
|
||||
<PlusIcon />
|
||||
</StyledCreateWorkspaceCardPillIcon>
|
||||
<div>
|
||||
<p>{t['New Workspace']()}</p>
|
||||
</div>
|
||||
</StyledCreateWorkspaceCardPillContent>
|
||||
</StyledItem>
|
||||
</StyledCreateWorkspaceCardPill>
|
||||
</StyledModalFooterContent>
|
||||
</>
|
||||
<Divider size="thinner" />
|
||||
<AFFiNEWorkspaceList workspaces={workspaces} onEventEnd={onEventEnd} />
|
||||
{workspaces.length > 0 ? <Divider size="thinner" /> : null}
|
||||
<AddWorkspace
|
||||
onAddWorkspace={onAddWorkspace}
|
||||
onNewWorkspace={onNewWorkspace}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,273 +0,0 @@
|
||||
import { displayFlex, styled, textEllipsis } from '@affine/component';
|
||||
|
||||
export const StyledSplitLine = styled('div')(() => {
|
||||
return {
|
||||
width: '1px',
|
||||
height: '20px',
|
||||
background: 'var(--affine-border-color)',
|
||||
marginRight: '12px',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyleWorkspaceInfo = styled('div')(() => {
|
||||
return {
|
||||
marginLeft: '15px',
|
||||
width: '202px',
|
||||
p: {
|
||||
height: '20px',
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
...displayFlex('flex-start', 'center'),
|
||||
},
|
||||
svg: {
|
||||
marginRight: '10px',
|
||||
fontSize: '16px',
|
||||
flexShrink: 0,
|
||||
},
|
||||
span: {
|
||||
flexGrow: 1,
|
||||
...textEllipsis(1),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const StyleWorkspaceTitle = styled('div')(() => {
|
||||
return {
|
||||
fontSize: 'var(--affine-font-base)',
|
||||
fontWeight: 600,
|
||||
lineHeight: '24px',
|
||||
marginBottom: '10px',
|
||||
maxWidth: '200px',
|
||||
...textEllipsis(1),
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledCreateWorkspaceCard = styled('div')(() => {
|
||||
return {
|
||||
width: '310px',
|
||||
height: '124px',
|
||||
marginBottom: '24px',
|
||||
cursor: 'pointer',
|
||||
padding: '16px',
|
||||
boxShadow: 'var(--affine-shadow-1)',
|
||||
borderRadius: '12px',
|
||||
transition: 'all .1s',
|
||||
background: 'var(--affine-white-80)',
|
||||
...displayFlex('flex-start', 'flex-start'),
|
||||
color: 'var(--affine-text-secondary-color)',
|
||||
|
||||
':hover': {
|
||||
background: 'var(--affine-hover-color)',
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
'.add-icon': {
|
||||
borderColor: 'var(--affine-white)',
|
||||
color: 'var(--affine-primary-color)',
|
||||
},
|
||||
},
|
||||
'@media (max-width: 720px)': {
|
||||
width: '100%',
|
||||
},
|
||||
};
|
||||
});
|
||||
export const StyledCreateWorkspaceCardPillContainer = styled('div')(() => {
|
||||
return {
|
||||
borderRadius: '10px',
|
||||
display: 'flex',
|
||||
margin: '-8px -4px',
|
||||
flexFlow: 'column',
|
||||
gap: '12px',
|
||||
background: 'var(--affine-background-overlay-panel-color)',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledCreateWorkspaceCardPill = styled('div')(() => {
|
||||
return {
|
||||
borderRadius: '8px',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
height: '58px',
|
||||
border: `1px solid var(--affine-border-color)`,
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledSignInCardPill = styled('div')(() => {
|
||||
return {
|
||||
borderRadius: '8px',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
height: '58px',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledImportWorkspaceCardPill = styled('div')(() => {
|
||||
return {
|
||||
borderRadius: '5px',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledCreateWorkspaceCardPillContent = styled('div')(() => {
|
||||
return {
|
||||
display: 'flex',
|
||||
gap: '12px',
|
||||
alignItems: 'center',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledCreateWorkspaceCardPillIcon = styled('div')(() => {
|
||||
return {
|
||||
fontSize: '28px',
|
||||
width: '1em',
|
||||
height: '1em',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledSignInCardPillTextCotainer = styled('div')(() => {
|
||||
return {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledSignInCardPillTextSecondary = styled('div')(() => {
|
||||
return {
|
||||
fontSize: '12px',
|
||||
color: 'var(--affine-text-secondary-color)',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledSignInCardPillTextPrimary = styled('div')(() => {
|
||||
return {
|
||||
fontSize: 'var(--affine-font-base)',
|
||||
fontWeight: 600,
|
||||
lineHeight: '24px',
|
||||
maxWidth: '200px',
|
||||
textAlign: 'left',
|
||||
...textEllipsis(1),
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledModalHeaderLeft = styled('div')(() => {
|
||||
return { ...displayFlex('flex-start', 'center') };
|
||||
});
|
||||
export const StyledModalTitle = styled('div')(() => {
|
||||
return {
|
||||
fontWeight: 600,
|
||||
fontSize: 'var(--affine-font-h6)',
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledHelperContainer = styled('div')(() => {
|
||||
return {
|
||||
color: 'var(--affine-icon-color)',
|
||||
marginLeft: '15px',
|
||||
fontWeight: 400,
|
||||
fontSize: 'var(--affine-font-h6)',
|
||||
...displayFlex('center', 'center'),
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledModalContent = styled('div')({
|
||||
...displayFlex('space-between', 'flex-start', 'flex-start'),
|
||||
flexWrap: 'wrap',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
gap: '4px',
|
||||
});
|
||||
|
||||
export const StyledModalFooterContent = styled('div')({
|
||||
...displayFlex('space-between', 'flex-start', 'flex-start'),
|
||||
flexWrap: 'wrap',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
marginTop: '12px',
|
||||
backgroundColor: 'var(--affine-background-overlay-panel-color)',
|
||||
});
|
||||
|
||||
export const StyledModalHeaderContent = styled('div')({
|
||||
...displayFlex('space-between', 'flex-start', 'flex-start'),
|
||||
flexWrap: 'wrap',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
backgroundColor: 'var(--affine-background-overlay-panel-color)',
|
||||
});
|
||||
|
||||
export const StyledOperationWrapper = styled('div')(() => {
|
||||
return {
|
||||
...displayFlex('flex-end', 'center'),
|
||||
};
|
||||
});
|
||||
|
||||
export const StyleWorkspaceAdd = styled('div')(() => {
|
||||
return {
|
||||
width: '58px',
|
||||
height: '58px',
|
||||
borderRadius: '100%',
|
||||
background: 'var(--affine-background-overlay-panel-color)',
|
||||
border: '1.5px dashed #f4f5fa',
|
||||
transition: 'background .2s',
|
||||
fontSize: '24px',
|
||||
...displayFlex('center', 'center'),
|
||||
borderColor: 'var(--affine-white)',
|
||||
color: 'var(--affine-background-overlay-panel-color)',
|
||||
};
|
||||
});
|
||||
export const StyledModalHeader = styled('div')(() => {
|
||||
return {
|
||||
width: '100%',
|
||||
left: 0,
|
||||
top: 0,
|
||||
borderRadius: '24px 24px 0 0',
|
||||
padding: '0px 14px',
|
||||
...displayFlex('space-between', 'center'),
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledModalBody = styled('div')(() => {
|
||||
return {
|
||||
display: 'inline-flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
gap: '4px',
|
||||
flex: 1,
|
||||
overflowY: 'auto',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledWorkspaceFlavourTitle = styled('div')(() => {
|
||||
return {
|
||||
fontSize: 'var(--affine-font-xs)',
|
||||
color: 'var(--affine-text-secondary-color)',
|
||||
marginBottom: '4px',
|
||||
};
|
||||
});
|
||||
export const StyledItem = styled('button')<{
|
||||
active?: boolean;
|
||||
}>(({ active = false }) => {
|
||||
return {
|
||||
height: 'auto',
|
||||
padding: '8px 12px',
|
||||
width: '100%',
|
||||
borderRadius: '5px',
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
...displayFlex('flex-start', 'center'),
|
||||
cursor: 'pointer',
|
||||
position: 'relative',
|
||||
backgroundColor: 'transparent',
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
svg: {
|
||||
color: 'var(--affine-icon-color)',
|
||||
},
|
||||
|
||||
':hover': {
|
||||
backgroundColor: 'var(--affine-hover-color)',
|
||||
},
|
||||
|
||||
...(active
|
||||
? {
|
||||
backgroundColor: 'var(--affine-hover-color)',
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
});
|
@ -0,0 +1,18 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const userAccountContainer = style({
|
||||
display: 'flex',
|
||||
padding: '4px 0px 4px 12px',
|
||||
gap: '12px',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
});
|
||||
export const userEmail = style({
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
fontWeight: 400,
|
||||
lineHeight: '22px',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
maxWidth: 'calc(100% - 36px)',
|
||||
});
|
@ -0,0 +1,96 @@
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import {
|
||||
AccountIcon,
|
||||
MoreHorizontalIcon,
|
||||
SignOutIcon,
|
||||
} from '@blocksuite/icons';
|
||||
import { IconButton } from '@toeverything/components/button';
|
||||
import { Divider } from '@toeverything/components/divider';
|
||||
import { Menu, MenuIcon, MenuItem } from '@toeverything/components/menu';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { openSettingModalAtom } from '../../../../../atoms';
|
||||
import { signOutCloud } from '../../../../../utils/cloud-utils';
|
||||
import { useNavigateHelper } from '../.././../../../hooks/use-navigate-helper';
|
||||
import * as styles from './index.css';
|
||||
|
||||
const AccountMenu = ({ onEventEnd }: { onEventEnd?: () => void }) => {
|
||||
const setSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||
|
||||
const { jumpToIndex } = useNavigateHelper();
|
||||
|
||||
const onOpenAccountSetting = useCallback(() => {
|
||||
setSettingModalAtom(prev => ({
|
||||
...prev,
|
||||
open: true,
|
||||
activeTab: 'account',
|
||||
}));
|
||||
}, [setSettingModalAtom]);
|
||||
|
||||
const onSignOut = useCallback(async () => {
|
||||
signOutCloud()
|
||||
.then(() => {
|
||||
jumpToIndex();
|
||||
})
|
||||
.catch(console.error);
|
||||
onEventEnd?.();
|
||||
}, [onEventEnd, jumpToIndex]);
|
||||
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MenuItem
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<AccountIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
data-testid="editor-option-menu-import"
|
||||
onClick={onOpenAccountSetting}
|
||||
>
|
||||
{t['com.affine.workspace.cloud.account.settings']()}
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<SignOutIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
data-testid="editor-option-menu-import"
|
||||
onClick={onSignOut}
|
||||
>
|
||||
{t['com.affine.workspace.cloud.account.logout']()}
|
||||
</MenuItem>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const UserAccountItem = ({
|
||||
email,
|
||||
onEventEnd,
|
||||
}: {
|
||||
email: string;
|
||||
onEventEnd?: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<div className={styles.userAccountContainer}>
|
||||
<div className={styles.userEmail}>{email}</div>
|
||||
<Menu
|
||||
items={<AccountMenu onEventEnd={onEventEnd} />}
|
||||
contentOptions={{
|
||||
side: 'right',
|
||||
sideOffset: 12,
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
data-testid="more-button"
|
||||
icon={<MoreHorizontalIcon />}
|
||||
type="plain"
|
||||
/>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,29 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const workspaceListsWrapper = style({
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
maxHeight: 'calc(100vh - 300px)',
|
||||
});
|
||||
export const workspaceListWrapper = style({
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
gap: '4px',
|
||||
});
|
||||
|
||||
export const workspaceType = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: '0px 12px',
|
||||
fontSize: 'var(--affine-font-xs)',
|
||||
lineHeight: '20px',
|
||||
color: 'var(--affine-text-secondary-color)',
|
||||
});
|
||||
|
||||
export const scrollbar = style({
|
||||
transform: 'translateX(10px)',
|
||||
width: '4px',
|
||||
});
|
@ -0,0 +1,233 @@
|
||||
import { ScrollableContainer } from '@affine/component';
|
||||
import { WorkspaceList } from '@affine/component/workspace-list';
|
||||
import type {
|
||||
AffineCloudWorkspace,
|
||||
LocalWorkspace,
|
||||
} from '@affine/env/workspace';
|
||||
import { WorkspaceFlavour, WorkspaceSubPath } from '@affine/env/workspace';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
|
||||
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
||||
import type { DragEndEvent } from '@dnd-kit/core';
|
||||
import { arrayMove } from '@dnd-kit/sortable';
|
||||
import { Divider } from '@toeverything/components/divider';
|
||||
import {
|
||||
currentPageIdAtom,
|
||||
currentWorkspaceIdAtom,
|
||||
} from '@toeverything/infra/atom';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { startTransition, useCallback, useMemo, useTransition } from 'react';
|
||||
|
||||
import {
|
||||
openCreateWorkspaceModalAtom,
|
||||
openSettingModalAtom,
|
||||
} from '../../../../../atoms';
|
||||
import type { AllWorkspace } from '../../../../../shared';
|
||||
import { useIsWorkspaceOwner } from '../.././../../../hooks/affine/use-is-workspace-owner';
|
||||
import { useNavigateHelper } from '../.././../../../hooks/use-navigate-helper';
|
||||
import * as styles from './index.css';
|
||||
interface WorkspaceModalProps {
|
||||
disabled?: boolean;
|
||||
workspaces: (AffineCloudWorkspace | LocalWorkspace)[];
|
||||
currentWorkspaceId: AllWorkspace['id'] | null;
|
||||
onClickWorkspace: (workspace: RootWorkspaceMetadata['id']) => void;
|
||||
onClickWorkspaceSetting: (workspace: RootWorkspaceMetadata['id']) => void;
|
||||
onNewWorkspace: () => void;
|
||||
onAddWorkspace: () => void;
|
||||
onDragEnd: (event: DragEndEvent) => void;
|
||||
}
|
||||
|
||||
const CloudWorkSpaceList = ({
|
||||
disabled,
|
||||
workspaces,
|
||||
onClickWorkspace,
|
||||
onClickWorkspaceSetting,
|
||||
currentWorkspaceId,
|
||||
onDragEnd,
|
||||
}: WorkspaceModalProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
if (workspaces.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className={styles.workspaceListWrapper}>
|
||||
<div className={styles.workspaceType}>
|
||||
{t['com.affine.workspaceList.workspaceListType.cloud']()}
|
||||
</div>
|
||||
<WorkspaceList
|
||||
disabled={disabled}
|
||||
items={workspaces}
|
||||
currentWorkspaceId={currentWorkspaceId}
|
||||
onClick={onClickWorkspace}
|
||||
onSettingClick={onClickWorkspaceSetting}
|
||||
onDragEnd={onDragEnd}
|
||||
useIsWorkspaceOwner={useIsWorkspaceOwner}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const LocalWorkspaces = ({
|
||||
disabled,
|
||||
workspaces,
|
||||
onClickWorkspace,
|
||||
onClickWorkspaceSetting,
|
||||
currentWorkspaceId,
|
||||
onDragEnd,
|
||||
}: WorkspaceModalProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
if (workspaces.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className={styles.workspaceListWrapper}>
|
||||
<div className={styles.workspaceType}>
|
||||
{t['com.affine.workspaceList.workspaceListType.local']()}
|
||||
</div>
|
||||
<WorkspaceList
|
||||
disabled={disabled}
|
||||
items={workspaces}
|
||||
currentWorkspaceId={currentWorkspaceId}
|
||||
onClick={onClickWorkspace}
|
||||
onSettingClick={onClickWorkspaceSetting}
|
||||
onDragEnd={onDragEnd}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const AFFiNEWorkspaceList = ({
|
||||
workspaces,
|
||||
onEventEnd,
|
||||
}: {
|
||||
workspaces: RootWorkspaceMetadata[];
|
||||
onEventEnd?: () => void;
|
||||
}) => {
|
||||
const setOpenCreateWorkspaceModal = useSetAtom(openCreateWorkspaceModalAtom);
|
||||
|
||||
const { jumpToSubPath } = useNavigateHelper();
|
||||
|
||||
const setWorkspaces = useSetAtom(rootWorkspacesMetadataAtom);
|
||||
|
||||
const [currentWorkspaceId, setCurrentWorkspaceId] = useAtom(
|
||||
currentWorkspaceIdAtom
|
||||
);
|
||||
|
||||
const setCurrentPageId = useSetAtom(currentPageIdAtom);
|
||||
|
||||
const [, startCloseTransition] = useTransition();
|
||||
|
||||
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||
|
||||
// TODO: AFFiNE Cloud support
|
||||
const { status } = useSession();
|
||||
|
||||
const isAuthenticated = useMemo(() => status === 'authenticated', [status]);
|
||||
|
||||
const cloudWorkspaces = useMemo(
|
||||
() =>
|
||||
workspaces.filter(
|
||||
({ flavour }) => flavour === WorkspaceFlavour.AFFINE_CLOUD
|
||||
) as (AffineCloudWorkspace | LocalWorkspace)[],
|
||||
[workspaces]
|
||||
);
|
||||
|
||||
const localWorkspaces = useMemo(
|
||||
() =>
|
||||
workspaces.filter(
|
||||
({ flavour }) => flavour === WorkspaceFlavour.LOCAL
|
||||
) as (AffineCloudWorkspace | LocalWorkspace)[],
|
||||
[workspaces]
|
||||
);
|
||||
|
||||
const onClickWorkspaceSetting = useCallback(
|
||||
(workspaceId: string) => {
|
||||
setOpenSettingModalAtom({
|
||||
open: true,
|
||||
activeTab: 'workspace',
|
||||
workspaceId,
|
||||
});
|
||||
onEventEnd?.();
|
||||
},
|
||||
[onEventEnd, setOpenSettingModalAtom]
|
||||
);
|
||||
|
||||
const onMoveWorkspace = useCallback(
|
||||
(activeId: string, overId: string) => {
|
||||
const oldIndex = workspaces.findIndex(w => w.id === activeId);
|
||||
|
||||
const newIndex = workspaces.findIndex(w => w.id === overId);
|
||||
startTransition(() => {
|
||||
setWorkspaces(workspaces => arrayMove(workspaces, oldIndex, newIndex));
|
||||
});
|
||||
},
|
||||
[setWorkspaces, workspaces]
|
||||
);
|
||||
|
||||
const onClickWorkspace = useCallback(
|
||||
(workspaceId: string) => {
|
||||
startCloseTransition(() => {
|
||||
setCurrentWorkspaceId(workspaceId);
|
||||
setCurrentPageId(null);
|
||||
jumpToSubPath(workspaceId, WorkspaceSubPath.ALL);
|
||||
});
|
||||
onEventEnd?.();
|
||||
},
|
||||
[jumpToSubPath, onEventEnd, setCurrentPageId, setCurrentWorkspaceId]
|
||||
);
|
||||
|
||||
const onDragEnd = useCallback(
|
||||
(event: DragEndEvent) => {
|
||||
const { active, over } = event;
|
||||
if (active.id !== over?.id) {
|
||||
onMoveWorkspace(active.id as string, over?.id as string);
|
||||
}
|
||||
},
|
||||
[onMoveWorkspace]
|
||||
);
|
||||
|
||||
const onNewWorkspace = useCallback(() => {
|
||||
setOpenCreateWorkspaceModal('new');
|
||||
onEventEnd?.();
|
||||
}, [onEventEnd, setOpenCreateWorkspaceModal]);
|
||||
|
||||
const onAddWorkspace = useCallback(async () => {
|
||||
setOpenCreateWorkspaceModal('add');
|
||||
onEventEnd?.();
|
||||
}, [onEventEnd, setOpenCreateWorkspaceModal]);
|
||||
|
||||
return (
|
||||
<ScrollableContainer
|
||||
className={styles.workspaceListsWrapper}
|
||||
scrollBarClassName={styles.scrollbar}
|
||||
>
|
||||
{isAuthenticated ? (
|
||||
<div>
|
||||
<CloudWorkSpaceList
|
||||
workspaces={cloudWorkspaces}
|
||||
onClickWorkspace={onClickWorkspace}
|
||||
onClickWorkspaceSetting={onClickWorkspaceSetting}
|
||||
onNewWorkspace={onNewWorkspace}
|
||||
onAddWorkspace={onAddWorkspace}
|
||||
currentWorkspaceId={currentWorkspaceId}
|
||||
onDragEnd={onDragEnd}
|
||||
/>
|
||||
{localWorkspaces.length > 0 && cloudWorkspaces.length > 0 ? (
|
||||
<Divider size="thinner" />
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
<LocalWorkspaces
|
||||
workspaces={localWorkspaces}
|
||||
onClickWorkspace={onClickWorkspace}
|
||||
onClickWorkspaceSetting={onClickWorkspaceSetting}
|
||||
onNewWorkspace={onNewWorkspace}
|
||||
onAddWorkspace={onAddWorkspace}
|
||||
currentWorkspaceId={currentWorkspaceId}
|
||||
onDragEnd={onDragEnd}
|
||||
/>
|
||||
</ScrollableContainer>
|
||||
);
|
||||
};
|
@ -30,6 +30,7 @@ import {
|
||||
} from './styles';
|
||||
|
||||
const hoverAtom = atom(false);
|
||||
|
||||
// FIXME:
|
||||
// 1. Remove mui style
|
||||
// 2. Refactor the code to improve readability
|
||||
@ -41,6 +42,7 @@ const CloudWorkspaceStatus = () => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const SyncingWorkspaceStatus = () => {
|
||||
return (
|
||||
<>
|
||||
@ -49,6 +51,7 @@ const SyncingWorkspaceStatus = () => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const UnSyncWorkspaceStatus = () => {
|
||||
return (
|
||||
<>
|
||||
@ -82,11 +85,14 @@ const WorkspaceStatus = ({
|
||||
currentWorkspace: AllWorkspace;
|
||||
}) => {
|
||||
const isOnline = useSystemOnline();
|
||||
|
||||
// todo: finish display sync status
|
||||
const [forceSyncStatus, startForceSync] = useDatasourceSync(
|
||||
currentWorkspace.blockSuiteWorkspace
|
||||
);
|
||||
|
||||
const setIsHovered = useSetAtom(hoverAtom);
|
||||
|
||||
const content = useMemo(() => {
|
||||
if (currentWorkspace.flavour === WorkspaceFlavour.LOCAL) {
|
||||
return 'Saved locally';
|
||||
@ -103,6 +109,7 @@ const WorkspaceStatus = ({
|
||||
return 'Sync with AFFiNE Cloud';
|
||||
}
|
||||
}, [currentWorkspace.flavour, forceSyncStatus.type, isOnline]);
|
||||
|
||||
const CloudWorkspaceSyncStatus = useCallback(() => {
|
||||
if (forceSyncStatus.type === 'syncing') {
|
||||
return SyncingWorkspaceStatus();
|
||||
@ -160,6 +167,7 @@ export const WorkspaceCard = forwardRef<
|
||||
const [name] = useBlockSuiteWorkspaceName(
|
||||
currentWorkspace.blockSuiteWorkspace
|
||||
);
|
||||
|
||||
const [workspaceAvatar] = useBlockSuiteWorkspaceAvatarUrl(
|
||||
currentWorkspace.blockSuiteWorkspace
|
||||
);
|
||||
|
@ -6,6 +6,7 @@ export const StyledSelectorContainer = styled('div')({
|
||||
alignItems: 'center',
|
||||
padding: '0 6px',
|
||||
borderRadius: '8px',
|
||||
outline: 'none',
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
':hover': {
|
||||
cursor: 'pointer',
|
||||
|
@ -19,17 +19,10 @@ import {
|
||||
} from '@blocksuite/icons';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import { useDroppable } from '@dnd-kit/core';
|
||||
import { Popover } from '@toeverything/components/popover';
|
||||
import { Menu } from '@toeverything/components/menu';
|
||||
import { useAtom } from 'jotai';
|
||||
import type { HTMLAttributes, ReactElement } from 'react';
|
||||
import {
|
||||
forwardRef,
|
||||
Suspense,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { useHistoryAtom } from '../../atoms/history';
|
||||
import { useAppSetting } from '../../atoms/settings';
|
||||
@ -175,18 +168,21 @@ export const RootAppSidebar = ({
|
||||
}
|
||||
>
|
||||
<SidebarContainer>
|
||||
<Popover
|
||||
open={openUserWorkspaceList}
|
||||
content={
|
||||
<Suspense>
|
||||
<UserWithWorkspaceList onEventEnd={closeUserWorkspaceList} />
|
||||
</Suspense>
|
||||
<Menu
|
||||
rootOptions={{
|
||||
open: openUserWorkspaceList,
|
||||
}}
|
||||
items={
|
||||
<UserWithWorkspaceList onEventEnd={closeUserWorkspaceList} />
|
||||
}
|
||||
contentOptions={{
|
||||
// hide trigger
|
||||
sideOffset: -58,
|
||||
onInteractOutside: closeUserWorkspaceList,
|
||||
onEscapeKeyDown: closeUserWorkspaceList,
|
||||
style: {
|
||||
width: '300px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<WorkspaceCard
|
||||
@ -195,7 +191,7 @@ export const RootAppSidebar = ({
|
||||
setOpenUserWorkspaceList(true);
|
||||
}, [])}
|
||||
/>
|
||||
</Popover>
|
||||
</Menu>
|
||||
<QuickSearchInput
|
||||
data-testid="slider-bar-quick-search-button"
|
||||
onClick={onOpenQuickSearchModal}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import { DEFAULT_HELLO_WORLD_PAGE_ID_SUFFIX } from '@affine/env/constant';
|
||||
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
||||
import { Menu } from '@toeverything/components/menu';
|
||||
import { getWorkspace } from '@toeverything/infra/__internal__/workspace';
|
||||
import { getCurrentStore } from '@toeverything/infra/atom';
|
||||
import { lazy } from 'react';
|
||||
@ -61,15 +62,29 @@ export const Component = () => {
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
width: 300,
|
||||
margin: '80px auto',
|
||||
borderRadius: '8px',
|
||||
boxShadow: 'var(--affine-shadow-2)',
|
||||
backgroundColor: 'var(--affine-background-overlay-panel-color)',
|
||||
padding: '16px 12px',
|
||||
position: 'fixed',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
}}
|
||||
>
|
||||
<UserWithWorkspaceList />
|
||||
<Menu
|
||||
rootOptions={{
|
||||
open: true,
|
||||
}}
|
||||
items={<UserWithWorkspaceList />}
|
||||
contentOptions={{
|
||||
style: {
|
||||
width: 300,
|
||||
transform: 'translate(-50%, -50%)',
|
||||
borderRadius: '8px',
|
||||
boxShadow: 'var(--affine-shadow-2)',
|
||||
backgroundColor: 'var(--affine-background-overlay-panel-color)',
|
||||
padding: '16px 12px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div></div>
|
||||
</Menu>
|
||||
</div>
|
||||
<AllWorkspaceModals />
|
||||
</>
|
||||
|
@ -37,6 +37,7 @@ export const AffineWorkspaceCard = () => {
|
||||
onClick={() => {}}
|
||||
onSettingClick={() => {}}
|
||||
currentWorkspaceId={null}
|
||||
isOwner={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
|
||||
import { SettingsIcon } from '@blocksuite/icons';
|
||||
import { CollaborationIcon, SettingsIcon } from '@blocksuite/icons';
|
||||
import { Skeleton } from '@mui/material';
|
||||
import { Avatar } from '@toeverything/components/avatar';
|
||||
import { Divider } from '@toeverything/components/divider';
|
||||
import { Tooltip } from '@toeverything/components/tooltip';
|
||||
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url';
|
||||
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
|
||||
import { useStaticBlockSuiteWorkspace } from '@toeverything/infra/__internal__/react';
|
||||
@ -10,46 +13,56 @@ import { useCallback } from 'react';
|
||||
|
||||
import {
|
||||
StyledCard,
|
||||
StyledIconContainer,
|
||||
StyledSettingLink,
|
||||
StyledWorkspaceInfo,
|
||||
StyledWorkspaceTitle,
|
||||
StyledWorkspaceTitleArea,
|
||||
StyledWorkspaceType,
|
||||
StyledWorkspaceTypeEllipse,
|
||||
StyledWorkspaceTypeText,
|
||||
} from './styles';
|
||||
|
||||
export interface WorkspaceTypeProps {
|
||||
flavour: WorkspaceFlavour;
|
||||
isOwner: boolean;
|
||||
}
|
||||
|
||||
const WorkspaceType = ({ flavour }: WorkspaceTypeProps) => {
|
||||
const WorkspaceType = ({ flavour, isOwner }: WorkspaceTypeProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
// fixme: cloud regression
|
||||
const isOwner = true;
|
||||
|
||||
if (flavour === WorkspaceFlavour.LOCAL) {
|
||||
return (
|
||||
<p
|
||||
style={{ fontSize: '10px' }}
|
||||
title={t['com.affine.workspaceType.local']()}
|
||||
>
|
||||
<span>{t['com.affine.workspaceType.local']()}</span>
|
||||
</p>
|
||||
<StyledWorkspaceType>
|
||||
<StyledWorkspaceTypeEllipse />
|
||||
<StyledWorkspaceTypeText>{t['Local']()}</StyledWorkspaceTypeText>
|
||||
</StyledWorkspaceType>
|
||||
);
|
||||
}
|
||||
|
||||
return isOwner ? (
|
||||
<p
|
||||
style={{ fontSize: '10px' }}
|
||||
title={t['com.affine.workspaceType.cloud']()}
|
||||
>
|
||||
<span>{t['com.affine.workspaceType.cloud']()}</span>
|
||||
</p>
|
||||
<StyledWorkspaceType>
|
||||
<StyledWorkspaceTypeEllipse cloud={true} />
|
||||
<StyledWorkspaceTypeText>
|
||||
{t['com.affine.brand.affineCloud']()}
|
||||
</StyledWorkspaceTypeText>
|
||||
</StyledWorkspaceType>
|
||||
) : (
|
||||
<p
|
||||
style={{ fontSize: '10px' }}
|
||||
title={t['com.affine.workspaceType.joined']()}
|
||||
>
|
||||
<span>{t['com.affine.workspaceType.joined']()}</span>
|
||||
</p>
|
||||
<StyledWorkspaceType>
|
||||
<StyledWorkspaceTypeEllipse cloud={true} />
|
||||
<StyledWorkspaceTypeText>
|
||||
{t['com.affine.brand.affineCloud']()}
|
||||
</StyledWorkspaceTypeText>
|
||||
<Divider
|
||||
orientation="vertical"
|
||||
size="thinner"
|
||||
style={{ margin: '0px 8px', height: '7px' }}
|
||||
/>
|
||||
<Tooltip content={t['com.affine.workspaceType.joined']()}>
|
||||
<StyledIconContainer>
|
||||
<CollaborationIcon />
|
||||
</StyledIconContainer>
|
||||
</Tooltip>
|
||||
</StyledWorkspaceType>
|
||||
);
|
||||
};
|
||||
|
||||
@ -58,19 +71,35 @@ export interface WorkspaceCardProps {
|
||||
meta: RootWorkspaceMetadata;
|
||||
onClick: (workspaceId: string) => void;
|
||||
onSettingClick: (workspaceId: string) => void;
|
||||
isOwner?: boolean;
|
||||
}
|
||||
|
||||
export const WorkspaceCardSkeleton = () => {
|
||||
return (
|
||||
<div>
|
||||
<StyledCard data-testid="workspace-card">
|
||||
<Skeleton variant="circular" width={28} height={28} />
|
||||
<Skeleton
|
||||
variant="rectangular"
|
||||
height={43}
|
||||
width={220}
|
||||
style={{ marginLeft: '12px' }}
|
||||
/>
|
||||
</StyledCard>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const WorkspaceCard = ({
|
||||
onClick,
|
||||
onSettingClick,
|
||||
currentWorkspaceId,
|
||||
meta,
|
||||
isOwner = true,
|
||||
}: WorkspaceCardProps) => {
|
||||
// const t = useAFFiNEI18N();
|
||||
const workspace = useStaticBlockSuiteWorkspace(meta.id);
|
||||
const [name] = useBlockSuiteWorkspaceName(workspace);
|
||||
const [workspaceAvatar] = useBlockSuiteWorkspaceAvatarUrl(workspace);
|
||||
|
||||
return (
|
||||
<StyledCard
|
||||
data-testid="workspace-card"
|
||||
@ -85,6 +114,7 @@ export const WorkspaceCard = ({
|
||||
<StyledWorkspaceTitle>{name}</StyledWorkspaceTitle>
|
||||
|
||||
<StyledSettingLink
|
||||
size="small"
|
||||
className="setting-entry"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
@ -92,17 +122,10 @@ export const WorkspaceCard = ({
|
||||
}}
|
||||
withoutHoverStyle={true}
|
||||
>
|
||||
<SettingsIcon style={{ margin: '0px' }} />
|
||||
<SettingsIcon />
|
||||
</StyledSettingLink>
|
||||
</StyledWorkspaceTitleArea>
|
||||
{/* {meta.flavour === WorkspaceFlavour.LOCAL && (
|
||||
<p title={t['com.affine.workspaceType.offline']()}>
|
||||
<LocalDataIcon />
|
||||
<WorkspaceType flavour={meta.flavour} />
|
||||
</p>
|
||||
|
||||
)} */}
|
||||
<WorkspaceType flavour={meta.flavour} />
|
||||
<WorkspaceType isOwner={isOwner} flavour={meta.flavour} />
|
||||
</StyledWorkspaceInfo>
|
||||
</StyledCard>
|
||||
);
|
||||
|
@ -5,30 +5,16 @@ import { displayFlex, styled, textEllipsis } from '../../../styles';
|
||||
export const StyledWorkspaceInfo = styled('div')(() => {
|
||||
return {
|
||||
marginLeft: '12px',
|
||||
width: '202px',
|
||||
p: {
|
||||
height: '20px',
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
...displayFlex('flex-start', 'center'),
|
||||
},
|
||||
svg: {
|
||||
marginRight: '10px',
|
||||
fontSize: '16px',
|
||||
flexShrink: 0,
|
||||
},
|
||||
span: {
|
||||
flexGrow: 1,
|
||||
...textEllipsis(1),
|
||||
},
|
||||
width: '100%',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledWorkspaceTitle = styled('div')(() => {
|
||||
return {
|
||||
fontSize: 'var(--affine-font-base)',
|
||||
fontWeight: 600,
|
||||
lineHeight: '24px',
|
||||
maxWidth: '200px',
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
fontWeight: 700,
|
||||
lineHeight: '22px',
|
||||
maxWidth: '190px',
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
...textEllipsis(1),
|
||||
};
|
||||
@ -38,13 +24,12 @@ export const StyledCard = styled('div')<{
|
||||
active?: boolean;
|
||||
}>(({ active }) => {
|
||||
const borderColor = active ? 'var(--affine-primary-color)' : 'transparent';
|
||||
const backgroundColor = active ? 'var(--affine-white)' : 'transparent';
|
||||
const backgroundColor = active ? 'var(--affine-white-30)' : 'transparent';
|
||||
return {
|
||||
width: '280px',
|
||||
height: '58px',
|
||||
width: '100%',
|
||||
cursor: 'pointer',
|
||||
padding: '12px',
|
||||
borderRadius: '12px',
|
||||
borderRadius: '8px',
|
||||
border: `1px solid ${borderColor}`,
|
||||
...displayFlex('flex-start', 'flex-start'),
|
||||
transition: 'background .2s',
|
||||
@ -91,8 +76,8 @@ export const StyledModalHeader = styled('div')(() => {
|
||||
export const StyledSettingLink = styled(IconButton)(() => {
|
||||
return {
|
||||
position: 'absolute',
|
||||
right: '6px',
|
||||
bottom: '6px',
|
||||
right: '10px',
|
||||
top: '10px',
|
||||
opacity: 0,
|
||||
borderRadius: '4px',
|
||||
color: 'var(--affine-primary-color)',
|
||||
@ -104,9 +89,11 @@ export const StyledSettingLink = styled(IconButton)(() => {
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledWorkspaceType = styled('p')(() => {
|
||||
export const StyledWorkspaceType = styled('div')(() => {
|
||||
return {
|
||||
fontSize: 10,
|
||||
...displayFlex('flex-start', 'center'),
|
||||
width: '100%',
|
||||
height: '20px',
|
||||
};
|
||||
});
|
||||
|
||||
@ -116,3 +103,35 @@ export const StyledWorkspaceTitleArea = styled('div')(() => {
|
||||
justifyContent: 'space-between',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledWorkspaceTypeEllipse = styled('div')<{
|
||||
cloud?: boolean;
|
||||
}>(({ cloud }) => {
|
||||
return {
|
||||
width: '5px',
|
||||
height: '5px',
|
||||
borderRadius: '50%',
|
||||
background: cloud
|
||||
? 'var(--affine-palette-shape-blue)'
|
||||
: 'var(--affine-palette-shape-green)',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledWorkspaceTypeText = styled('div')(() => {
|
||||
return {
|
||||
fontSize: '12px',
|
||||
fontWeight: 500,
|
||||
lineHeight: '20px',
|
||||
marginLeft: '4px',
|
||||
color: 'var(--affine-text-secondary-color)',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledIconContainer = styled('div')(() => {
|
||||
return {
|
||||
...displayFlex('flex-start', 'center'),
|
||||
fontSize: '14px',
|
||||
gap: '8px',
|
||||
color: 'var(--affine-icon-secondary)',
|
||||
};
|
||||
});
|
||||
|
@ -16,9 +16,12 @@ import {
|
||||
} from '@dnd-kit/modifiers';
|
||||
import { arrayMove, SortableContext, useSortable } from '@dnd-kit/sortable';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Suspense, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { WorkspaceCard } from '../../components/card/workspace-card';
|
||||
import {
|
||||
WorkspaceCard,
|
||||
WorkspaceCardSkeleton,
|
||||
} from '../../components/card/workspace-card';
|
||||
import { workspaceItemStyle } from './index.css';
|
||||
|
||||
export interface WorkspaceListProps {
|
||||
@ -28,16 +31,25 @@ export interface WorkspaceListProps {
|
||||
onClick: (workspaceId: string) => void;
|
||||
onSettingClick: (workspaceId: string) => void;
|
||||
onDragEnd: (event: DragEndEvent) => void;
|
||||
useIsWorkspaceOwner?: (workspaceId: string) => boolean;
|
||||
}
|
||||
|
||||
interface SortableWorkspaceItemProps extends Omit<WorkspaceListProps, 'items'> {
|
||||
item: RootWorkspaceMetadata;
|
||||
useIsWorkspaceOwner?: (workspaceId: string) => boolean;
|
||||
}
|
||||
|
||||
const SortableWorkspaceItem = (props: SortableWorkspaceItemProps) => {
|
||||
const SortableWorkspaceItem = ({
|
||||
disabled,
|
||||
item,
|
||||
useIsWorkspaceOwner,
|
||||
currentWorkspaceId,
|
||||
onClick,
|
||||
onSettingClick,
|
||||
}: SortableWorkspaceItemProps) => {
|
||||
const { setNodeRef, attributes, listeners, transform, transition } =
|
||||
useSortable({
|
||||
id: props.item.id,
|
||||
id: item.id,
|
||||
});
|
||||
const style: CSSProperties = useMemo(
|
||||
() => ({
|
||||
@ -45,11 +57,12 @@ const SortableWorkspaceItem = (props: SortableWorkspaceItemProps) => {
|
||||
? `translate3d(${transform.x}px, ${transform.y}px, 0)`
|
||||
: undefined,
|
||||
transition,
|
||||
pointerEvents: props.disabled ? 'none' : undefined,
|
||||
opacity: props.disabled ? 0.6 : undefined,
|
||||
pointerEvents: disabled ? 'none' : undefined,
|
||||
opacity: disabled ? 0.6 : undefined,
|
||||
}),
|
||||
[props.disabled, transform, transition]
|
||||
[disabled, transform, transition]
|
||||
);
|
||||
const isOwner = useIsWorkspaceOwner?.(item.id);
|
||||
return (
|
||||
<div
|
||||
className={workspaceItemStyle}
|
||||
@ -60,10 +73,11 @@ const SortableWorkspaceItem = (props: SortableWorkspaceItemProps) => {
|
||||
{...listeners}
|
||||
>
|
||||
<WorkspaceCard
|
||||
currentWorkspaceId={props.currentWorkspaceId}
|
||||
meta={props.item}
|
||||
onClick={props.onClick}
|
||||
onSettingClick={props.onSettingClick}
|
||||
currentWorkspaceId={currentWorkspaceId}
|
||||
meta={item}
|
||||
onClick={onClick}
|
||||
onSettingClick={onSettingClick}
|
||||
isOwner={isOwner}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@ -106,7 +120,9 @@ export const WorkspaceList = (props: WorkspaceListProps) => {
|
||||
<DndContext sensors={sensors} onDragEnd={onDragEnd} modifiers={modifiers}>
|
||||
<SortableContext items={optimisticList}>
|
||||
{optimisticList.map(item => (
|
||||
<SortableWorkspaceItem {...props} item={item} key={item.id} />
|
||||
<Suspense fallback={<WorkspaceCardSkeleton />} key={item.id}>
|
||||
<SortableWorkspaceItem {...props} item={item} key={item.id} />
|
||||
</Suspense>
|
||||
))}
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
|
@ -11,6 +11,7 @@ export type ScrollableContainerProps = {
|
||||
className?: string;
|
||||
viewPortClassName?: string;
|
||||
styles?: React.CSSProperties;
|
||||
scrollBarClassName?: string;
|
||||
};
|
||||
|
||||
export const ScrollableContainer = ({
|
||||
@ -20,6 +21,7 @@ export const ScrollableContainer = ({
|
||||
className,
|
||||
styles: _styles,
|
||||
viewPortClassName,
|
||||
scrollBarClassName,
|
||||
}: PropsWithChildren<ScrollableContainerProps>) => {
|
||||
const [hasScrollTop, ref] = useHasScrollTop();
|
||||
return (
|
||||
@ -39,7 +41,7 @@ export const ScrollableContainer = ({
|
||||
</ScrollArea.Viewport>
|
||||
<ScrollArea.Scrollbar
|
||||
orientation="vertical"
|
||||
className={clsx(styles.scrollbar, {
|
||||
className={clsx(styles.scrollbar, scrollBarClassName, {
|
||||
[styles.TableScrollbar]: inTableView,
|
||||
})}
|
||||
>
|
||||
|
@ -580,5 +580,9 @@
|
||||
"Successfully enabled AFFiNE Cloud": "Successfully enabled AFFiNE Cloud",
|
||||
"404.hint": "Sorry, you do not have access or this content does not exist...",
|
||||
"404.back": "Back to My Content",
|
||||
"404.signOut": "Sign in to another account"
|
||||
"404.signOut": "Sign in to another account",
|
||||
"com.affine.workspaceList.addWorkspace.create": "Create Workspace",
|
||||
"com.affine.workspaceList.workspaceListType.local": "Local Storage",
|
||||
"com.affine.workspaceList.workspaceListType.cloud": "Cloud Sync",
|
||||
"Local": "Local"
|
||||
}
|
||||
|
@ -74,8 +74,6 @@ test('Show collections items in sidebar', async ({ page }) => {
|
||||
skipInitialPage: true,
|
||||
});
|
||||
expect(await items.count()).toBe(1);
|
||||
|
||||
await clickSideBarCurrentWorkspaceBanner(page);
|
||||
await createLocalWorkspace(
|
||||
{
|
||||
name: 'Test 1',
|
||||
|
Loading…
Reference in New Issue
Block a user