mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 04:48:53 +00:00
refactor(core): new workspace selector and create dialog (#10323)
This commit is contained in:
@@ -97,6 +97,8 @@ export const WorkspaceSelector = ({
|
||||
...menuContentOptions,
|
||||
style: {
|
||||
width: '300px',
|
||||
maxHeight: 'min(800px, calc(100vh - 200px))',
|
||||
padding: 0,
|
||||
...menuContentOptions?.style,
|
||||
},
|
||||
}}
|
||||
|
||||
@@ -1,23 +1,5 @@
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
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: cssVar('iconSecondary'),
|
||||
});
|
||||
export const ItemText = style({
|
||||
fontSize: cssVar('fontSm'),
|
||||
lineHeight: '22px',
|
||||
color: cssVar('textSecondaryColor'),
|
||||
fontWeight: 400,
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
|
||||
export const addServerDividerWrapper = style({
|
||||
padding: '0px 12px',
|
||||
});
|
||||
|
||||
@@ -1,34 +1,50 @@
|
||||
import { MenuItem } from '@affine/component/ui/menu';
|
||||
import { Divider, MenuItem } from '@affine/component';
|
||||
import { GlobalDialogService } from '@affine/core/modules/dialogs';
|
||||
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { PlusIcon } from '@blocksuite/icons/rc';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import * as styles from './index.css';
|
||||
import {
|
||||
ItemContainer,
|
||||
ItemText,
|
||||
prefixIcon,
|
||||
} from '../add-workspace/index.css';
|
||||
import { addServerDividerWrapper } from './index.css';
|
||||
|
||||
export const AddServer = ({ onAddServer }: { onAddServer?: () => void }) => {
|
||||
export const AddServer = () => {
|
||||
const t = useI18n();
|
||||
const globalDialogService = useService(GlobalDialogService);
|
||||
const featureFlagService = useService(FeatureFlagService);
|
||||
const enableMultipleServer = useLiveData(
|
||||
featureFlagService.flags.enable_multiple_cloud_servers.$
|
||||
);
|
||||
|
||||
const onAddServer = useCallback(() => {
|
||||
globalDialogService.open('sign-in', { step: 'addSelfhosted' });
|
||||
}, [globalDialogService]);
|
||||
|
||||
if (!enableMultipleServer) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<div className={addServerDividerWrapper}>
|
||||
<Divider size="thinner" />
|
||||
</div>
|
||||
<MenuItem
|
||||
block={true}
|
||||
prefixIcon={<PlusIcon />}
|
||||
prefixIconClassName={prefixIcon}
|
||||
onClick={onAddServer}
|
||||
data-testid="new-server"
|
||||
className={styles.ItemContainer}
|
||||
className={ItemContainer}
|
||||
>
|
||||
<div className={styles.ItemText}>
|
||||
<div className={ItemText}>
|
||||
{t['com.affine.workspaceList.addServer']()}
|
||||
</div>
|
||||
</MenuItem>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const ItemContainer = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
padding: '8px 14px',
|
||||
gap: '14px',
|
||||
padding: '6px 16px 6px 11px',
|
||||
gap: '12px',
|
||||
cursor: 'pointer',
|
||||
borderRadius: '8px',
|
||||
transition: 'background-color 0.2s',
|
||||
fontSize: '24px',
|
||||
color: cssVar('iconSecondary'),
|
||||
});
|
||||
export const prefixIcon = style({
|
||||
width: 24,
|
||||
height: 24,
|
||||
fontSize: 24,
|
||||
color: cssVarV2.icon.secondary,
|
||||
});
|
||||
export const ItemText = style({
|
||||
fontSize: cssVar('fontSm'),
|
||||
lineHeight: '22px',
|
||||
color: cssVar('textSecondaryColor'),
|
||||
color: cssVarV2.text.secondary,
|
||||
fontWeight: 400,
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
|
||||
@@ -20,11 +20,12 @@ export const AddWorkspace = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
{BUILD_CONFIG.isElectron && (
|
||||
<MenuItem
|
||||
block={true}
|
||||
prefixIcon={<ImportIcon />}
|
||||
prefixIconClassName={styles.prefixIcon}
|
||||
onClick={onAddWorkspace}
|
||||
data-testid="add-workspace"
|
||||
className={styles.ItemContainer}
|
||||
@@ -37,6 +38,7 @@ export const AddWorkspace = ({
|
||||
<MenuItem
|
||||
block={true}
|
||||
prefixIcon={<PlusIcon />}
|
||||
prefixIconClassName={styles.prefixIcon}
|
||||
onClick={onNewWorkspace}
|
||||
data-testid="new-workspace"
|
||||
className={styles.ItemContainer}
|
||||
@@ -47,6 +49,6 @@ export const AddWorkspace = ({
|
||||
: t['com.affine.workspaceList.addWorkspace.create-cloud']()}
|
||||
</div>
|
||||
</MenuItem>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { style } from '@vanilla-extract/css';
|
||||
export const workspaceListWrapper = style({
|
||||
|
||||
export const workspaceScrollArea = style({
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
});
|
||||
export const workspaceScrollAreaViewport = style({
|
||||
padding: '10px 8px 0px 8px',
|
||||
});
|
||||
export const workspaceFooter = style({
|
||||
padding: '0px 8px 10px 8px',
|
||||
});
|
||||
export const scrollbar = style({
|
||||
width: 9,
|
||||
padding: '0px 2px',
|
||||
':hover': {
|
||||
padding: 0,
|
||||
},
|
||||
});
|
||||
export const scrollbarThumb = style({
|
||||
width: 5,
|
||||
});
|
||||
export const signInWrapper = style({
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ScrollableContainer } from '@affine/component';
|
||||
import { MenuItem } from '@affine/component/ui/menu';
|
||||
import { AuthService } from '@affine/core/modules/cloud';
|
||||
import { GlobalDialogService } from '@affine/core/modules/dialogs';
|
||||
@@ -9,7 +10,6 @@ import { Logo1Icon } from '@blocksuite/icons/rc';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { AddServer } from './add-server';
|
||||
import { AddWorkspace } from './add-workspace';
|
||||
import * as styles from './index.css';
|
||||
import { AFFiNEWorkspaceList } from './workspace-list';
|
||||
@@ -58,7 +58,7 @@ interface UserWithWorkspaceListProps {
|
||||
showEnableCloudButton?: boolean;
|
||||
}
|
||||
|
||||
const UserWithWorkspaceListInner = ({
|
||||
export const UserWithWorkspaceList = ({
|
||||
onEventEnd,
|
||||
onClickWorkspace,
|
||||
onCreatedWorkspace,
|
||||
@@ -109,26 +109,26 @@ const UserWithWorkspaceListInner = ({
|
||||
onEventEnd?.();
|
||||
}, [globalDialogService, onCreatedWorkspace, onEventEnd]);
|
||||
|
||||
const onAddServer = useCallback(() => {
|
||||
globalDialogService.open('sign-in', { step: 'addSelfhosted' });
|
||||
}, [globalDialogService]);
|
||||
|
||||
return (
|
||||
<div className={styles.workspaceListWrapper}>
|
||||
<AFFiNEWorkspaceList
|
||||
onEventEnd={onEventEnd}
|
||||
onClickWorkspace={onClickWorkspace}
|
||||
showEnableCloudButton={showEnableCloudButton}
|
||||
/>
|
||||
<AddWorkspace
|
||||
onAddWorkspace={onAddWorkspace}
|
||||
onNewWorkspace={onNewWorkspace}
|
||||
/>
|
||||
<AddServer onAddServer={onAddServer} />
|
||||
</div>
|
||||
<>
|
||||
<ScrollableContainer
|
||||
className={styles.workspaceScrollArea}
|
||||
viewPortClassName={styles.workspaceScrollAreaViewport}
|
||||
scrollBarClassName={styles.scrollbar}
|
||||
scrollThumbClassName={styles.scrollbarThumb}
|
||||
>
|
||||
<AFFiNEWorkspaceList
|
||||
onEventEnd={onEventEnd}
|
||||
onClickWorkspace={onClickWorkspace}
|
||||
showEnableCloudButton={showEnableCloudButton}
|
||||
/>
|
||||
</ScrollableContainer>
|
||||
<div className={styles.workspaceFooter}>
|
||||
<AddWorkspace
|
||||
onAddWorkspace={onAddWorkspace}
|
||||
onNewWorkspace={onNewWorkspace}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const UserWithWorkspaceList = (props: UserWithWorkspaceListProps) => {
|
||||
return <UserWithWorkspaceListInner {...props} />;
|
||||
};
|
||||
|
||||
@@ -1,81 +1,73 @@
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
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: 2,
|
||||
});
|
||||
|
||||
export const workspaceServer = style({
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
gap: 4,
|
||||
paddingLeft: '12px',
|
||||
marginBottom: '4px',
|
||||
alignItems: 'center',
|
||||
padding: '0px 8px',
|
||||
gap: 8,
|
||||
marginBottom: 6,
|
||||
});
|
||||
export const workspaceServerIcon = style({
|
||||
border: `1px solid ${cssVarV2.layer.insideBorder.border}`,
|
||||
borderRadius: 4,
|
||||
color: cssVarV2.icon.primary,
|
||||
fontSize: 18,
|
||||
width: 30,
|
||||
height: 30,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
});
|
||||
|
||||
export const workspaceServerContent = style({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
color: cssVarV2('text/secondary'),
|
||||
gap: 4,
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
});
|
||||
export const workspaceServerName = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
fontWeight: 500,
|
||||
fontSize: cssVar('fontXs'),
|
||||
lineHeight: '20px',
|
||||
const ellipsis = style({
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
});
|
||||
export const account = style({
|
||||
fontSize: cssVar('fontXs'),
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
});
|
||||
export const workspaceTypeIcon = style({
|
||||
color: cssVarV2('icon/primary'),
|
||||
fontSize: '16px',
|
||||
});
|
||||
export const scrollbar = style({
|
||||
width: '4px',
|
||||
});
|
||||
export const workspaceCard = style({
|
||||
height: '44px',
|
||||
padding: '0 12px',
|
||||
export const workspaceServerAccount = style([
|
||||
ellipsis,
|
||||
{
|
||||
fontSize: cssVar('fontXs'),
|
||||
lineHeight: '20px',
|
||||
color: cssVarV2.text.secondary,
|
||||
marginTop: -1.5,
|
||||
},
|
||||
]);
|
||||
export const workspaceServerName = style([
|
||||
ellipsis,
|
||||
{
|
||||
fontSize: cssVar('fontXs'),
|
||||
lineHeight: '20px',
|
||||
fontWeight: 500,
|
||||
color: cssVarV2.text.primary,
|
||||
selectors: {
|
||||
[`&:has(~ ${workspaceServerAccount})`]: {
|
||||
marginBottom: -1.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
export const workspaceServerSpacer = style({
|
||||
width: 0,
|
||||
flexGrow: 1,
|
||||
});
|
||||
|
||||
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: cssVarV2('icon/secondary'),
|
||||
export const workspaceCard = style({
|
||||
height: 36,
|
||||
padding: '7px 12px',
|
||||
});
|
||||
export const ItemText = style({
|
||||
fontSize: cssVar('fontSm'),
|
||||
lineHeight: '22px',
|
||||
color: cssVarV2('text/secondary'),
|
||||
fontWeight: 400,
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
export const workspaceCardInfoContainer = style({
|
||||
gap: 12,
|
||||
});
|
||||
|
||||
export const serverDivider = style({
|
||||
marginTop: 8,
|
||||
marginBottom: 12,
|
||||
});
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import {
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuItem,
|
||||
ScrollableContainer,
|
||||
} from '@affine/component';
|
||||
import { IconButton, Menu, MenuItem } from '@affine/component';
|
||||
import { Divider } from '@affine/component/ui/divider';
|
||||
import { useEnableCloud } from '@affine/core/components/hooks/affine/use-enable-cloud';
|
||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
||||
import type { Server } from '@affine/core/modules/cloud';
|
||||
import type { AuthAccountInfo, Server } from '@affine/core/modules/cloud';
|
||||
import { AuthService, ServersService } from '@affine/core/modules/cloud';
|
||||
import { GlobalDialogService } from '@affine/core/modules/dialogs';
|
||||
import { GlobalContextService } from '@affine/core/modules/global-context';
|
||||
@@ -17,14 +12,15 @@ import {
|
||||
WorkspaceService,
|
||||
WorkspacesService,
|
||||
} from '@affine/core/modules/workspace';
|
||||
import { ServerDeploymentType } from '@affine/graphql';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import {
|
||||
AccountIcon,
|
||||
CloudWorkspaceIcon,
|
||||
DeleteIcon,
|
||||
LocalWorkspaceIcon,
|
||||
MoreHorizontalIcon,
|
||||
PlusIcon,
|
||||
TeamWorkspaceIcon,
|
||||
SelfhostIcon,
|
||||
SignOutIcon,
|
||||
} from '@blocksuite/icons/rc';
|
||||
import {
|
||||
FrameworkScope,
|
||||
@@ -35,6 +31,7 @@ import {
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { WorkspaceCard } from '../../workspace-card';
|
||||
import { AddServer } from '../add-server';
|
||||
import * as styles from './index.css';
|
||||
|
||||
interface WorkspaceModalProps {
|
||||
@@ -46,6 +43,91 @@ interface WorkspaceModalProps {
|
||||
onAddWorkspace: () => void;
|
||||
}
|
||||
|
||||
const WorkspaceServerInfo = ({
|
||||
server,
|
||||
name,
|
||||
account,
|
||||
accountStatus,
|
||||
onDeleteServer,
|
||||
onSignOut,
|
||||
onSignIn,
|
||||
}: {
|
||||
server: string;
|
||||
name: string;
|
||||
account?: AuthAccountInfo | null;
|
||||
accountStatus?: 'authenticated' | 'unauthenticated';
|
||||
onDeleteServer?: () => void;
|
||||
onSignOut?: () => void;
|
||||
onSignIn?: () => void;
|
||||
}) => {
|
||||
const t = useI18n();
|
||||
const isCloud = server !== 'local';
|
||||
const isAffineCloud = server === 'affine-cloud';
|
||||
const Icon = isAffineCloud
|
||||
? CloudWorkspaceIcon
|
||||
: isCloud
|
||||
? SelfhostIcon
|
||||
: LocalWorkspaceIcon;
|
||||
|
||||
const menuItems = useMemo(
|
||||
() =>
|
||||
[
|
||||
server !== 'affine-cloud' && server !== 'local' && (
|
||||
<MenuItem
|
||||
prefixIcon={<DeleteIcon />}
|
||||
type="danger"
|
||||
key="delete-server"
|
||||
onClick={onDeleteServer}
|
||||
>
|
||||
{t['com.affine.server.delete']()}
|
||||
</MenuItem>
|
||||
),
|
||||
accountStatus === 'authenticated' && (
|
||||
<MenuItem
|
||||
prefixIcon={<SignOutIcon />}
|
||||
key="sign-out"
|
||||
onClick={onSignOut}
|
||||
type="danger"
|
||||
>
|
||||
{t['Sign out']()}
|
||||
</MenuItem>
|
||||
),
|
||||
accountStatus === 'unauthenticated' && (
|
||||
<MenuItem
|
||||
prefixIcon={<AccountIcon />}
|
||||
key="sign-in"
|
||||
onClick={onSignIn}
|
||||
>
|
||||
{t['Sign in']()}
|
||||
</MenuItem>
|
||||
),
|
||||
].filter(Boolean),
|
||||
[accountStatus, onDeleteServer, onSignIn, onSignOut, server, t]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.workspaceServer}>
|
||||
<div className={styles.workspaceServerIcon}>
|
||||
<Icon />
|
||||
</div>
|
||||
<div className={styles.workspaceServerContent}>
|
||||
<div className={styles.workspaceServerName}>{name}</div>
|
||||
{isCloud ? (
|
||||
<div className={styles.workspaceServerAccount}>
|
||||
{account ? account.email : 'Not signed in'}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className={styles.workspaceServerSpacer} />
|
||||
{menuItems.length ? (
|
||||
<Menu items={menuItems}>
|
||||
<IconButton icon={<MoreHorizontalIcon />} />
|
||||
</Menu>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CloudWorkSpaceList = ({
|
||||
server,
|
||||
workspaces,
|
||||
@@ -57,7 +139,6 @@ const CloudWorkSpaceList = ({
|
||||
onClickWorkspace: (workspaceMetadata: WorkspaceMetadata) => void;
|
||||
onClickEnableCloud?: (meta: WorkspaceMetadata) => void;
|
||||
}) => {
|
||||
const t = useI18n();
|
||||
const globalContextService = useService(GlobalContextService);
|
||||
const globalDialogService = useService(GlobalDialogService);
|
||||
const serverName = useLiveData(server.config$.selector(c => c.serverName));
|
||||
@@ -71,8 +152,6 @@ const CloudWorkSpaceList = ({
|
||||
globalContextService.globalContext.workspaceFlavour.$
|
||||
);
|
||||
|
||||
const serverType = server.config$.value.type;
|
||||
|
||||
const handleDeleteServer = useCallback(() => {
|
||||
serversService.removeServer(server.id);
|
||||
|
||||
@@ -100,78 +179,23 @@ const CloudWorkSpaceList = ({
|
||||
});
|
||||
}, [globalDialogService, server.baseUrl]);
|
||||
|
||||
const onNewWorkspace = useCallback(() => {
|
||||
globalDialogService.open(
|
||||
'create-workspace',
|
||||
{
|
||||
serverId: server.id,
|
||||
forcedCloud: true,
|
||||
},
|
||||
payload => {
|
||||
if (payload) {
|
||||
navigateHelper.openPage(payload.metadata.id, 'all');
|
||||
}
|
||||
}
|
||||
);
|
||||
}, [globalDialogService, navigateHelper, server.id]);
|
||||
|
||||
return (
|
||||
<div className={styles.workspaceListWrapper}>
|
||||
<div className={styles.workspaceServer}>
|
||||
<div className={styles.workspaceServerContent}>
|
||||
<div className={styles.workspaceServerName}>
|
||||
{serverType === ServerDeploymentType.Affine ? (
|
||||
<CloudWorkspaceIcon className={styles.workspaceTypeIcon} />
|
||||
) : (
|
||||
<TeamWorkspaceIcon className={styles.workspaceTypeIcon} />
|
||||
)}
|
||||
<div className={styles.account}>{serverName}</div>
|
||||
</div>
|
||||
<div className={styles.account}>
|
||||
{account ? account.email : 'Not signed in'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Menu
|
||||
items={[
|
||||
server.id !== 'affine-cloud' && (
|
||||
<MenuItem key="delete-server" onClick={handleDeleteServer}>
|
||||
{t['com.affine.server.delete']()}
|
||||
</MenuItem>
|
||||
),
|
||||
accountStatus === 'authenticated' && (
|
||||
<MenuItem key="sign-out" onClick={handleSignOut}>
|
||||
{t['Sign out']()}
|
||||
</MenuItem>
|
||||
),
|
||||
accountStatus === 'unauthenticated' && (
|
||||
<MenuItem key="sign-in" onClick={handleSignIn}>
|
||||
{t['Sign in']()}
|
||||
</MenuItem>
|
||||
),
|
||||
]}
|
||||
>
|
||||
<div>
|
||||
<IconButton icon={<MoreHorizontalIcon />} />
|
||||
</div>
|
||||
</Menu>
|
||||
</div>
|
||||
<>
|
||||
<WorkspaceServerInfo
|
||||
server={server.id}
|
||||
name={serverName}
|
||||
account={account}
|
||||
accountStatus={accountStatus}
|
||||
onDeleteServer={handleDeleteServer}
|
||||
onSignOut={handleSignOut}
|
||||
onSignIn={handleSignIn}
|
||||
/>
|
||||
<WorkspaceList
|
||||
items={workspaces}
|
||||
onClick={onClickWorkspace}
|
||||
onEnableCloudClick={onClickEnableCloud}
|
||||
/>
|
||||
<MenuItem
|
||||
block={true}
|
||||
prefixIcon={<PlusIcon />}
|
||||
onClick={onNewWorkspace}
|
||||
className={styles.ItemContainer}
|
||||
>
|
||||
<div className={styles.ItemText}>
|
||||
{t['com.affine.workspaceList.addWorkspace.create']()}
|
||||
</div>
|
||||
</MenuItem>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -186,25 +210,18 @@ const LocalWorkspaces = ({
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className={styles.workspaceListWrapper}>
|
||||
<div className={styles.workspaceServer}>
|
||||
<div className={styles.workspaceServerName}>
|
||||
<LocalWorkspaceIcon
|
||||
width={14}
|
||||
height={14}
|
||||
className={styles.workspaceTypeIcon}
|
||||
/>
|
||||
{t['com.affine.workspaceList.workspaceListType.local']()}
|
||||
</div>
|
||||
</div>
|
||||
<>
|
||||
<WorkspaceServerInfo
|
||||
server="local"
|
||||
name={t['com.affine.workspaceList.workspaceListType.local']()}
|
||||
/>
|
||||
<WorkspaceList
|
||||
items={workspaces}
|
||||
onClick={onClickWorkspace}
|
||||
onSettingClick={onClickWorkspaceSetting}
|
||||
onEnableCloudClick={onClickEnableCloud}
|
||||
/>
|
||||
<Divider size="thinner" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -224,6 +241,14 @@ export const AFFiNEWorkspaceList = ({
|
||||
|
||||
const serversService = useService(ServersService);
|
||||
const servers = useLiveData(serversService.servers$);
|
||||
const affineCloudServer = useMemo(
|
||||
() => servers.find(s => s.id === 'affine-cloud') as Server,
|
||||
[servers]
|
||||
);
|
||||
const selfhostServers = useMemo(
|
||||
() => servers.filter(s => s.id !== 'affine-cloud'),
|
||||
[servers]
|
||||
);
|
||||
|
||||
const cloudWorkspaces = useMemo(
|
||||
() =>
|
||||
@@ -262,24 +287,26 @@ export const AFFiNEWorkspaceList = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<ScrollableContainer
|
||||
className={styles.workspaceListsWrapper}
|
||||
scrollBarClassName={styles.scrollbar}
|
||||
>
|
||||
<div>
|
||||
{servers.map(server => (
|
||||
<FrameworkScope key={server.id} scope={server.scope}>
|
||||
<CloudWorkSpaceList
|
||||
server={server}
|
||||
workspaces={cloudWorkspaces.filter(
|
||||
({ flavour }) => flavour === server.id
|
||||
)}
|
||||
onClickWorkspace={handleClickWorkspace}
|
||||
/>
|
||||
<Divider size="thinner" />
|
||||
</FrameworkScope>
|
||||
<>
|
||||
{/* 1. affine-cloud */}
|
||||
<FrameworkScope
|
||||
key={affineCloudServer.id}
|
||||
scope={affineCloudServer.scope}
|
||||
>
|
||||
<CloudWorkSpaceList
|
||||
server={affineCloudServer}
|
||||
workspaces={cloudWorkspaces.filter(
|
||||
({ flavour }) => flavour === affineCloudServer.id
|
||||
)}
|
||||
onClickWorkspace={handleClickWorkspace}
|
||||
/>
|
||||
</FrameworkScope>
|
||||
{localWorkspaces.length > 0 ||
|
||||
(selfhostServers.length > 0 && (
|
||||
<Divider size="thinner" className={styles.serverDivider} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 2. local */}
|
||||
<LocalWorkspaces
|
||||
workspaces={localWorkspaces}
|
||||
onClickWorkspace={handleClickWorkspace}
|
||||
@@ -287,7 +314,28 @@ export const AFFiNEWorkspaceList = ({
|
||||
showEnableCloudButton ? onClickEnableCloud : undefined
|
||||
}
|
||||
/>
|
||||
</ScrollableContainer>
|
||||
{selfhostServers.length > 0 && (
|
||||
<Divider size="thinner" className={styles.serverDivider} />
|
||||
)}
|
||||
|
||||
{/* 3. selfhost */}
|
||||
{selfhostServers.map((server, index) => (
|
||||
<FrameworkScope key={server.id} scope={server.scope}>
|
||||
<CloudWorkSpaceList
|
||||
server={server}
|
||||
workspaces={cloudWorkspaces.filter(
|
||||
({ flavour }) => flavour === server.id
|
||||
)}
|
||||
onClickWorkspace={handleClickWorkspace}
|
||||
/>
|
||||
{index !== selfhostServers.length - 1 && (
|
||||
<Divider size="thinner" className={styles.serverDivider} />
|
||||
)}
|
||||
</FrameworkScope>
|
||||
))}
|
||||
<AddServer />
|
||||
<Divider size="thinner" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -317,9 +365,10 @@ const SortableWorkspaceItem = ({
|
||||
return (
|
||||
<WorkspaceCard
|
||||
className={styles.workspaceCard}
|
||||
infoClassName={styles.workspaceCardInfoContainer}
|
||||
workspaceMetadata={workspaceMetadata}
|
||||
onClick={handleClick}
|
||||
avatarSize={28}
|
||||
avatarSize={22}
|
||||
active={currentWorkspace?.id === workspaceMetadata.id}
|
||||
onClickOpenSettings={onSettingClick}
|
||||
onClickEnableCloud={onEnableCloudClick}
|
||||
|
||||
@@ -249,6 +249,7 @@ export const WorkspaceCard = forwardRef<
|
||||
hideCollaborationIcon?: boolean;
|
||||
hideTeamWorkspaceIcon?: boolean;
|
||||
active?: boolean;
|
||||
infoClassName?: string;
|
||||
onClickOpenSettings?: (workspaceMetadata: WorkspaceMetadata) => void;
|
||||
onClickEnableCloud?: (workspaceMetadata: WorkspaceMetadata) => void;
|
||||
}
|
||||
@@ -262,6 +263,7 @@ export const WorkspaceCard = forwardRef<
|
||||
onClickOpenSettings,
|
||||
onClickEnableCloud,
|
||||
className,
|
||||
infoClassName,
|
||||
disable,
|
||||
hideCollaborationIcon,
|
||||
hideTeamWorkspaceIcon,
|
||||
@@ -296,7 +298,7 @@ export const WorkspaceCard = forwardRef<
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
<div className={styles.infoContainer}>
|
||||
<div className={clsx(styles.infoContainer, infoClassName)}>
|
||||
{information ? (
|
||||
<WorkspaceAvatar
|
||||
meta={workspaceMetadata}
|
||||
@@ -332,34 +334,35 @@ export const WorkspaceCard = forwardRef<
|
||||
Enable Cloud
|
||||
</Button>
|
||||
) : null}
|
||||
{hideCollaborationIcon || information?.isOwner ? null : (
|
||||
<Tooltip
|
||||
content={t['com.affine.settings.workspace.state.joined']()}
|
||||
>
|
||||
<CollaborationIcon className={styles.collaborationIcon} />
|
||||
</Tooltip>
|
||||
)}
|
||||
{hideTeamWorkspaceIcon || !information?.isTeam ? null : (
|
||||
<Tooltip
|
||||
content={t['com.affine.settings.workspace.state.team']()}
|
||||
>
|
||||
<TeamWorkspaceIcon className={styles.collaborationIcon} />
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{onClickOpenSettings && (
|
||||
<div className={styles.settingButton} onClick={onOpenSettings}>
|
||||
<SettingsIcon width={16} height={16} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{showArrowDownIcon && <ArrowDownSmallIcon />}
|
||||
</div>
|
||||
|
||||
{active && (
|
||||
<div className={styles.activeContainer}>
|
||||
<DoneIcon className={styles.activeIcon} />
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.suffixIcons}>
|
||||
{hideCollaborationIcon || information?.isOwner ? null : (
|
||||
<Tooltip
|
||||
content={t['com.affine.settings.workspace.state.joined']()}
|
||||
>
|
||||
<CollaborationIcon className={styles.collaborationIcon} />
|
||||
</Tooltip>
|
||||
)}
|
||||
{hideTeamWorkspaceIcon || !information?.isTeam ? null : (
|
||||
<Tooltip content={t['com.affine.settings.workspace.state.team']()}>
|
||||
<TeamWorkspaceIcon className={styles.collaborationIcon} />
|
||||
</Tooltip>
|
||||
)}
|
||||
{active && (
|
||||
<div className={styles.activeContainer}>
|
||||
<DoneIcon className={styles.activeIcon} />
|
||||
</div>
|
||||
)}
|
||||
{showArrowDownIcon && <ArrowDownSmallIcon />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export const container = style({
|
||||
width: '100%',
|
||||
maxWidth: 500,
|
||||
color: cssVarV2('text/primary'),
|
||||
overflow: 'hidden',
|
||||
':hover': {
|
||||
cursor: 'pointer',
|
||||
background: cssVar('hoverColor'),
|
||||
@@ -34,6 +35,7 @@ export const infoContainer = style({
|
||||
});
|
||||
export const activeContainer = style({
|
||||
flexShrink: 0,
|
||||
lineHeight: 0,
|
||||
});
|
||||
|
||||
export const disable = style({
|
||||
@@ -187,6 +189,9 @@ export const showOnCardHover = style({
|
||||
[`.${container}:hover &`]: {
|
||||
position: 'relative',
|
||||
},
|
||||
'&:empty': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -194,3 +199,14 @@ export const activeIcon = style({
|
||||
fontSize: 14,
|
||||
color: cssVarV2('icon/activated'),
|
||||
});
|
||||
|
||||
export const suffixIcons = style({
|
||||
display: 'flex',
|
||||
gap: 8,
|
||||
alignItems: 'center',
|
||||
selectors: {
|
||||
'&:empty': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user