feat(core): sidebar local workspace enable cloud directly (#6366)

- Add a new hook `useEnableClould`, remove `<EnableAffineClouldModal />`
- Sidebar local workspace list item support enable AFFiNE Cloud
This commit is contained in:
CatsJuice
2024-03-29 02:08:09 +00:00
parent 1a873ecf3c
commit 35715ab1d8
11 changed files with 273 additions and 211 deletions

View File

@@ -1,10 +1,12 @@
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import type { WorkspaceFlavour } from '@affine/env/workspace';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { CollaborationIcon, SettingsIcon } from '@blocksuite/icons';
import type { WorkspaceMetadata } from '@toeverything/infra';
import { useCallback } from 'react';
import clsx from 'clsx';
import { type MouseEvent, useCallback } from 'react';
import { Avatar, type AvatarProps } from '../../../ui/avatar';
import { Button } from '../../../ui/button';
import { Skeleton } from '../../../ui/skeleton';
import * as styles from './styles.css';
@@ -18,7 +20,10 @@ export interface WorkspaceCardProps {
meta: WorkspaceMetadata;
onClick: (metadata: WorkspaceMetadata) => void;
onSettingClick: (metadata: WorkspaceMetadata) => void;
onEnableCloudClick?: (meta: WorkspaceMetadata) => void;
isOwner?: boolean;
openingId?: string | null;
enableCloudText?: string;
avatar?: string;
name?: string;
}
@@ -45,13 +50,25 @@ const avatarImageProps = {
export const WorkspaceCard = ({
onClick,
onSettingClick,
onEnableCloudClick,
openingId,
currentWorkspaceId,
meta,
isOwner = true,
enableCloudText = 'Enable Cloud',
name,
avatar,
}: WorkspaceCardProps) => {
const isLocal = meta.flavour === WorkspaceFlavour.LOCAL;
const displayName = name ?? UNTITLED_WORKSPACE_NAME;
const onEnableCloud = useCallback(
(e: MouseEvent) => {
e.stopPropagation();
onEnableCloudClick?.(meta);
},
[meta, onEnableCloudClick]
);
return (
<div
className={styles.card}
@@ -73,6 +90,17 @@ export const WorkspaceCard = ({
<div className={styles.workspaceTitle}>{displayName}</div>
<div className={styles.actionButtons}>
{isLocal ? (
<Button
loading={!!openingId && openingId === meta.id}
disabled={!!openingId}
type="default"
className={clsx(styles.enableCloudButton, styles.showOnCardHover)}
onClick={onEnableCloud}
>
{enableCloudText}
</Button>
) : null}
{isOwner ? null : <CollaborationIcon />}
<div
className={styles.settingButton}

View File

@@ -84,3 +84,16 @@ export const settingButton = style({
// },
},
});
export const enableCloudButton = style({
background: 'transparent',
});
export const showOnCardHover = style({
display: 'none',
selectors: {
[`.${card}:hover &`]: {
display: 'block',
},
},
});

View File

@@ -19,8 +19,10 @@ export interface WorkspaceListProps {
disabled?: boolean;
currentWorkspaceId?: string | null;
items: WorkspaceMetadata[];
openingId?: string | null;
onClick: (workspace: WorkspaceMetadata) => void;
onSettingClick: (workspace: WorkspaceMetadata) => void;
onEnableCloudClick?: (meta: WorkspaceMetadata) => void;
onDragEnd: (event: DragEndEvent) => void;
useIsWorkspaceOwner: (workspaceMetadata: WorkspaceMetadata) => boolean;
useWorkspaceAvatar: (
@@ -38,12 +40,14 @@ interface SortableWorkspaceItemProps extends Omit<WorkspaceListProps, 'items'> {
const SortableWorkspaceItem = ({
disabled,
item,
openingId,
useIsWorkspaceOwner,
useWorkspaceAvatar,
useWorkspaceName,
currentWorkspaceId,
onClick,
onSettingClick,
onEnableCloudClick,
}: SortableWorkspaceItemProps) => {
const { setNodeRef, attributes, listeners, transform, transition } =
useSortable({
@@ -77,6 +81,8 @@ const SortableWorkspaceItem = ({
meta={item}
onClick={onClick}
onSettingClick={onSettingClick}
onEnableCloudClick={onEnableCloudClick}
openingId={openingId}
isOwner={isOwner}
name={name}
avatar={avatar}

View File

@@ -56,9 +56,16 @@ export const ConfirmModal = ({
);
};
interface OpenConfirmModalOptions {
autoClose?: boolean;
onSuccess?: () => void;
}
interface ConfirmModalContextProps {
modalProps: ConfirmModalProps;
openConfirmModal: (props?: ConfirmModalProps) => void;
openConfirmModal: (
props?: ConfirmModalProps,
options?: OpenConfirmModalOptions
) => void;
closeConfirmModal: () => void;
}
const ConfirmModalContext = createContext<ConfirmModalContextProps>({
@@ -86,7 +93,8 @@ export const ConfirmModalProvider = ({ children }: PropsWithChildren) => {
}, []);
const openConfirmModal = useCallback(
(props?: ConfirmModalProps) => {
(props?: ConfirmModalProps, options?: OpenConfirmModalOptions) => {
const { autoClose = true, onSuccess } = options ?? {};
if (!props) {
setModalProps({ open: true });
return;
@@ -97,17 +105,22 @@ export const ConfirmModalProvider = ({ children }: PropsWithChildren) => {
const onConfirm = () => {
setLoading(true);
_onConfirm?.()
?.catch(console.error)
?.finally(() => closeConfirmModal());
?.then(() => onSuccess?.())
.catch(console.error)
.finally(() => autoClose && closeConfirmModal());
};
setModalProps({ ...otherProps, onConfirm, open: true });
},
[closeConfirmModal, setLoading]
);
const onOpenChange = useCallback((open: boolean) => {
setModalProps(props => ({ ...props, open }));
}, []);
const onOpenChange = useCallback(
(open: boolean) => {
modalProps.onOpenChange?.(open);
setModalProps(props => ({ ...props, open }));
},
[modalProps]
);
return (
<ConfirmModalContext.Provider
@@ -115,7 +128,7 @@ export const ConfirmModalProvider = ({ children }: PropsWithChildren) => {
>
{children}
{/* TODO: multi-instance support(unnecessary for now) */}
<ConfirmModal onOpenChange={onOpenChange} {...modalProps} />
<ConfirmModal {...modalProps} onOpenChange={onOpenChange} />
</ConfirmModalContext.Provider>
);
};