diff --git a/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/add-workspace/index.css.ts b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/add-workspace/index.css.ts
new file mode 100644
index 0000000000..e1dc962b5c
--- /dev/null
+++ b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/add-workspace/index.css.ts
@@ -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',
+});
diff --git a/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/add-workspace/index.tsx b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/add-workspace/index.tsx
new file mode 100644
index 0000000000..75f04e46b8
--- /dev/null
+++ b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/add-workspace/index.tsx
@@ -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 (
+
+ {runtimeConfig.enableSQLiteProvider && environment.isDesktop ? (
+
}
+ onClick={onAddWorkspace}
+ data-testid="add-workspace"
+ className={styles.ItemContainer}
+ >
+
+ {t['com.affine.workspace.local.import']()}
+
+
+ ) : null}
+
}
+ onClick={onNewWorkspace}
+ data-testid="new-workspace"
+ className={styles.ItemContainer}
+ >
+
+ {t['com.affine.workspaceList.addWorkspace.create']()}
+
+
+
+ );
+};
diff --git a/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/index.css.ts b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/index.css.ts
new file mode 100644
index 0000000000..5bd4cff9da
--- /dev/null
+++ b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/index.css.ts
@@ -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',
+});
diff --git a/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/index.tsx b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/index.tsx
index 1a38ead079..e1045d593f 100644
--- a/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/index.tsx
+++ b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/index.tsx
@@ -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 (
-
-
-
-
-
- );
-};
-
-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 (
- <>
-
-
-
- {t['com.affine.workspace.cloud']()}
-
-
-
-
- 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]
- )}
- />
-
- >
+
);
};
@@ -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 ? (
-
-
- {
- if (!runtimeConfig.enableCloud) {
- setDisableCloudOpen(true);
- } else {
- setOpen(state => ({
- ...state,
- openModal: true,
- }));
- }
- }}
- data-testid="cloud-signin-button"
- >
-
-
-
-
-
-
- {t['com.affine.workspace.cloud.auth']()}
-
-
- {t['com.affine.workspace.cloud.description']()}
-
-
-
-
-
-
-
+
+ {isAuthenticated ? (
+
) : (
-
-
- {session?.user.email}
-
-
- }
- contentOptions={{
- side: 'right',
- sideOffset: 30,
- }}
- >
- }
- type="plain"
- />
-
-
-
-
-
+
)}
-
- {isLoggedIn && cloudWorkspaces.length !== 0 ? (
- <>
-
-
- >
- ) : null}
-
-
- {t['com.affine.workspace.local']()}
-
-
-
- {
- const { active, over } = event;
- if (active.id !== over?.id) {
- onMoveWorkspace(active.id as string, over?.id as string);
- }
- },
- [onMoveWorkspace]
- )}
- />
-
- {runtimeConfig.enableSQLiteProvider && environment.isDesktop ? (
-
-
-
-
-
-
-
-
{t['com.affine.workspace.local.import']()}
-
-
-
-
- ) : null}
-
-
-
-
-
-
-
-
-
-
{t['New Workspace']()}
-
-
-
-
-
- >
+
+
+ {workspaces.length > 0 ?
: null}
+
+
);
};
diff --git a/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/styles.ts b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/styles.ts
deleted file mode 100644
index 60c5b96e85..0000000000
--- a/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/styles.ts
+++ /dev/null
@@ -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)',
- }
- : {}),
- };
-});
diff --git a/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/user-account/index.css.ts b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/user-account/index.css.ts
new file mode 100644
index 0000000000..43275e4318
--- /dev/null
+++ b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/user-account/index.css.ts
@@ -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)',
+});
diff --git a/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/user-account/index.tsx b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/user-account/index.tsx
new file mode 100644
index 0000000000..827ad56587
--- /dev/null
+++ b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/user-account/index.tsx
@@ -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 (
+
+
+
+
+
+ );
+};
+
+export const UserAccountItem = ({
+ email,
+ onEventEnd,
+}: {
+ email: string;
+ onEventEnd?: () => void;
+}) => {
+ return (
+
+
{email}
+
}
+ contentOptions={{
+ side: 'right',
+ sideOffset: 12,
+ }}
+ >
+
}
+ type="plain"
+ />
+
+
+ );
+};
diff --git a/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/workspace-list/index.css.ts b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/workspace-list/index.css.ts
new file mode 100644
index 0000000000..0ace06dbcc
--- /dev/null
+++ b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/workspace-list/index.css.ts
@@ -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',
+});
diff --git a/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/workspace-list/index.tsx b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/workspace-list/index.tsx
new file mode 100644
index 0000000000..be17d2dcf0
--- /dev/null
+++ b/apps/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/workspace-list/index.tsx
@@ -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 (
+
+
+ {t['com.affine.workspaceList.workspaceListType.cloud']()}
+
+
+
+ );
+};
+
+const LocalWorkspaces = ({
+ disabled,
+ workspaces,
+ onClickWorkspace,
+ onClickWorkspaceSetting,
+ currentWorkspaceId,
+ onDragEnd,
+}: WorkspaceModalProps) => {
+ const t = useAFFiNEI18N();
+ if (workspaces.length === 0) {
+ return null;
+ }
+ return (
+
+
+ {t['com.affine.workspaceList.workspaceListType.local']()}
+
+
+
+ );
+};
+
+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 (
+
+ {isAuthenticated ? (
+
+
+ {localWorkspaces.length > 0 && cloudWorkspaces.length > 0 ? (
+
+ ) : null}
+
+ ) : null}
+
+
+ );
+};
diff --git a/apps/core/src/components/pure/workspace-slider-bar/workspace-card/index.tsx b/apps/core/src/components/pure/workspace-slider-bar/workspace-card/index.tsx
index 5937c32650..d7829bea30 100644
--- a/apps/core/src/components/pure/workspace-slider-bar/workspace-card/index.tsx
+++ b/apps/core/src/components/pure/workspace-slider-bar/workspace-card/index.tsx
@@ -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
);
diff --git a/apps/core/src/components/pure/workspace-slider-bar/workspace-card/styles.ts b/apps/core/src/components/pure/workspace-slider-bar/workspace-card/styles.ts
index c59ff8e731..a510341dfb 100644
--- a/apps/core/src/components/pure/workspace-slider-bar/workspace-card/styles.ts
+++ b/apps/core/src/components/pure/workspace-slider-bar/workspace-card/styles.ts
@@ -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',
diff --git a/apps/core/src/components/root-app-sidebar/index.tsx b/apps/core/src/components/root-app-sidebar/index.tsx
index 0eaf6d2b69..9cc1cd6de9 100644
--- a/apps/core/src/components/root-app-sidebar/index.tsx
+++ b/apps/core/src/components/root-app-sidebar/index.tsx
@@ -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 = ({
}
>
-
-
-
+
}
contentOptions={{
// hide trigger
sideOffset: -58,
onInteractOutside: closeUserWorkspaceList,
onEscapeKeyDown: closeUserWorkspaceList,
+ style: {
+ width: '300px',
+ },
}}
>
-
+
{
<>
-
+
}
+ 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',
+ },
+ }}
+ >
+
+
>
diff --git a/apps/storybook/src/stories/card.stories.tsx b/apps/storybook/src/stories/card.stories.tsx
index 8602e61c48..305554f2a3 100644
--- a/apps/storybook/src/stories/card.stories.tsx
+++ b/apps/storybook/src/stories/card.stories.tsx
@@ -37,6 +37,7 @@ export const AffineWorkspaceCard = () => {
onClick={() => {}}
onSettingClick={() => {}}
currentWorkspaceId={null}
+ isOwner={true}
/>
);
};
diff --git a/packages/component/src/components/card/workspace-card/index.tsx b/packages/component/src/components/card/workspace-card/index.tsx
index 433f244bd0..779355b2c8 100644
--- a/packages/component/src/components/card/workspace-card/index.tsx
+++ b/packages/component/src/components/card/workspace-card/index.tsx
@@ -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 (
-
- {t['com.affine.workspaceType.local']()}
-
+
+
+ {t['Local']()}
+
);
}
return isOwner ? (
-
- {t['com.affine.workspaceType.cloud']()}
-
+
+
+
+ {t['com.affine.brand.affineCloud']()}
+
+
) : (
-
- {t['com.affine.workspaceType.joined']()}
-
+
+
+
+ {t['com.affine.brand.affineCloud']()}
+
+
+
+
+
+
+
+
);
};
@@ -58,19 +71,35 @@ export interface WorkspaceCardProps {
meta: RootWorkspaceMetadata;
onClick: (workspaceId: string) => void;
onSettingClick: (workspaceId: string) => void;
+ isOwner?: boolean;
}
+export const WorkspaceCardSkeleton = () => {
+ return (
+
+
+
+
+
+
+ );
+};
+
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 (
{name}
{
e.stopPropagation();
@@ -92,17 +122,10 @@ export const WorkspaceCard = ({
}}
withoutHoverStyle={true}
>
-
+
- {/* {meta.flavour === WorkspaceFlavour.LOCAL && (
-
-
-
-
-
- )} */}
-
+
);
diff --git a/packages/component/src/components/card/workspace-card/styles.ts b/packages/component/src/components/card/workspace-card/styles.ts
index 6ca289f4f5..bd1d645003 100644
--- a/packages/component/src/components/card/workspace-card/styles.ts
+++ b/packages/component/src/components/card/workspace-card/styles.ts
@@ -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)',
+ };
+});
diff --git a/packages/component/src/components/workspace-list/index.tsx b/packages/component/src/components/workspace-list/index.tsx
index 24004ee1a5..3ec35c8674 100644
--- a/packages/component/src/components/workspace-list/index.tsx
+++ b/packages/component/src/components/workspace-list/index.tsx
@@ -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 {
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 (
{
{...listeners}
>
);
@@ -106,7 +120,9 @@ export const WorkspaceList = (props: WorkspaceListProps) => {
{optimisticList.map(item => (
-
+ } key={item.id}>
+
+
))}
diff --git a/packages/component/src/ui/scrollbar/scrollbar.tsx b/packages/component/src/ui/scrollbar/scrollbar.tsx
index 6d06d2ca25..e9beb6ce2f 100644
--- a/packages/component/src/ui/scrollbar/scrollbar.tsx
+++ b/packages/component/src/ui/scrollbar/scrollbar.tsx
@@ -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) => {
const [hasScrollTop, ref] = useHasScrollTop();
return (
@@ -39,7 +41,7 @@ export const ScrollableContainer = ({
diff --git a/packages/i18n/src/resources/en.json b/packages/i18n/src/resources/en.json
index 786cf633ca..825673ab43 100644
--- a/packages/i18n/src/resources/en.json
+++ b/packages/i18n/src/resources/en.json
@@ -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"
}
diff --git a/tests/affine-local/e2e/local-first-collections-items.spec.ts b/tests/affine-local/e2e/local-first-collections-items.spec.ts
index 301563d75e..c9c51ec106 100644
--- a/tests/affine-local/e2e/local-first-collections-items.spec.ts
+++ b/tests/affine-local/e2e/local-first-collections-items.spec.ts
@@ -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',