diff --git a/packages/frontend/component/src/components/card/workspace-card/index.tsx b/packages/frontend/component/src/components/card/workspace-card/index.tsx
index cce605e305..eb419d8e4b 100644
--- a/packages/frontend/component/src/components/card/workspace-card/index.tsx
+++ b/packages/frontend/component/src/components/card/workspace-card/index.tsx
@@ -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 (
+ {isLocal ? (
+
+ ) : null}
{isOwner ? null :
}
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 {
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}
diff --git a/packages/frontend/component/src/ui/modal/confirm-modal.tsx b/packages/frontend/component/src/ui/modal/confirm-modal.tsx
index 349056ca8b..6ef27bbf2b 100644
--- a/packages/frontend/component/src/ui/modal/confirm-modal.tsx
+++ b/packages/frontend/component/src/ui/modal/confirm-modal.tsx
@@ -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({
@@ -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 (
{
>
{children}
{/* TODO: multi-instance support(unnecessary for now) */}
-
+
);
};
diff --git a/packages/frontend/core/src/components/affine/enable-affine-cloud-modal/index.tsx b/packages/frontend/core/src/components/affine/enable-affine-cloud-modal/index.tsx
deleted file mode 100644
index c1efc7c1c9..0000000000
--- a/packages/frontend/core/src/components/affine/enable-affine-cloud-modal/index.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import type { ConfirmModalProps } from '@affine/component/ui/modal';
-import { ConfirmModal } from '@affine/component/ui/modal';
-import { useAFFiNEI18N } from '@affine/i18n/hooks';
-import { useSetAtom } from 'jotai';
-import { useCallback } from 'react';
-
-import { authAtom } from '../../../atoms';
-import { setOnceSignedInEventAtom } from '../../../atoms/event';
-import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status';
-
-export const EnableAffineCloudModal = ({
- onConfirm: propsOnConfirm,
- ...props
-}: Omit & { onConfirm: () => void }) => {
- const t = useAFFiNEI18N();
- const loginStatus = useCurrentLoginStatus();
- const setAuthAtom = useSetAtom(authAtom);
- const setOnceSignedInEvent = useSetAtom(setOnceSignedInEventAtom);
-
- const confirm = useCallback(() => {
- return propsOnConfirm?.();
- }, [propsOnConfirm]);
-
- const onConfirm = useCallback(() => {
- if (loginStatus === 'unauthenticated') {
- setAuthAtom(prev => ({
- ...prev,
- openModal: true,
- }));
- setOnceSignedInEvent(confirm);
- }
- if (loginStatus === 'authenticated') {
- return propsOnConfirm?.();
- }
- }, [confirm, loginStatus, propsOnConfirm, setAuthAtom, setOnceSignedInEvent]);
-
- return (
-
- );
-};
diff --git a/packages/frontend/core/src/components/affine/history-tips-modal/index.tsx b/packages/frontend/core/src/components/affine/history-tips-modal/index.tsx
index f0ef444a03..697a350b33 100644
--- a/packages/frontend/core/src/components/affine/history-tips-modal/index.tsx
+++ b/packages/frontend/core/src/components/affine/history-tips-modal/index.tsx
@@ -3,64 +3,43 @@ import {
openDisableCloudAlertModalAtom,
openHistoryTipsModalAtom,
} from '@affine/core/atoms';
-import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
-import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
-import { WorkspaceSubPath } from '@affine/core/shared';
+import { useEnableCloud } from '@affine/core/hooks/affine/use-enable-cloud';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
-import { useService, Workspace, WorkspaceManager } from '@toeverything/infra';
+import { useService, Workspace } from '@toeverything/infra';
import { useAtom, useSetAtom } from 'jotai';
-import { useCallback, useState } from 'react';
+import { useCallback } from 'react';
-import { EnableAffineCloudModal } from '../enable-affine-cloud-modal';
import TopSvg from './top-svg';
export const HistoryTipsModal = () => {
const t = useAFFiNEI18N();
- const { openPage } = useNavigateHelper();
- const workspaceManager = useService(WorkspaceManager);
const currentWorkspace = useService(Workspace);
const [open, setOpen] = useAtom(openHistoryTipsModalAtom);
const setTempDisableCloudOpen = useSetAtom(openDisableCloudAlertModalAtom);
- const [openEnableCloudModal, setOpenEnableCloudModal] = useState(false);
+ const confirmEnableCloud = useEnableCloud();
const handleConfirm = useCallback(() => {
setOpen(false);
if (runtimeConfig.enableCloud) {
- return setOpenEnableCloudModal(true);
- }
- return setTempDisableCloudOpen(true);
- }, [setOpen, setTempDisableCloudOpen]);
-
- const handEnableCloud = useAsyncCallback(async () => {
- if (!currentWorkspace) {
+ confirmEnableCloud(currentWorkspace);
return;
}
- const { id: newId } =
- await workspaceManager.transformLocalToCloud(currentWorkspace);
- openPage(newId, WorkspaceSubPath.ALL);
- setOpenEnableCloudModal(false);
- }, [openPage, currentWorkspace, workspaceManager]);
+ return setTempDisableCloudOpen(true);
+ }, [confirmEnableCloud, currentWorkspace, setOpen, setTempDisableCloudOpen]);
return (
- <>
- }
- title={t['com.affine.history-vision.tips-modal.title']()}
- onOpenChange={setOpen}
- description={t['com.affine.history-vision.tips-modal.description']()}
- cancelText={t['com.affine.history-vision.tips-modal.cancel']()}
- confirmButtonOptions={{
- type: 'primary',
- }}
- onConfirm={handleConfirm}
- confirmText={t['com.affine.history-vision.tips-modal.confirm']()}
- />
-
- >
+ }
+ title={t['com.affine.history-vision.tips-modal.title']()}
+ onOpenChange={setOpen}
+ description={t['com.affine.history-vision.tips-modal.description']()}
+ cancelText={t['com.affine.history-vision.tips-modal.cancel']()}
+ confirmButtonOptions={{
+ type: 'primary',
+ }}
+ onConfirm={handleConfirm}
+ confirmText={t['com.affine.history-vision.tips-modal.confirm']()}
+ />
);
};
diff --git a/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/enable-cloud.tsx b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/enable-cloud.tsx
index 307f8070b0..4feccc7d4b 100644
--- a/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/enable-cloud.tsx
+++ b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/enable-cloud.tsx
@@ -1,19 +1,15 @@
import { SettingRow } from '@affine/component/setting-components';
import { Button } from '@affine/component/ui/button';
-import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
+import { useEnableCloud } from '@affine/core/hooks/affine/use-enable-cloud';
import { useWorkspaceInfo } from '@affine/core/hooks/use-workspace-info';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { Workspace } from '@toeverything/infra';
-import { useService, WorkspaceManager } from '@toeverything/infra';
import { useSetAtom } from 'jotai';
-import { useState } from 'react';
+import { useCallback, useState } from 'react';
import { openSettingModalAtom } from '../../../../../atoms';
-import { useNavigateHelper } from '../../../../../hooks/use-navigate-helper';
-import { WorkspaceSubPath } from '../../../../../shared';
-import { EnableAffineCloudModal } from '../../../enable-affine-cloud-modal';
import { TmpDisableAffineCloudModal } from '../../../tmp-disable-affine-cloud-modal';
import type { WorkspaceSettingDetailProps } from './types';
@@ -26,28 +22,21 @@ export const EnableCloudPanel = ({
workspace,
}: PublishPanelProps) => {
const t = useAFFiNEI18N();
+ const confirmEnableCloud = useEnableCloud();
- const { openPage } = useNavigateHelper();
-
- const workspaceManager = useService(WorkspaceManager);
const workspaceInfo = useWorkspaceInfo(workspaceMetadata);
const setSettingModal = useSetAtom(openSettingModalAtom);
const [open, setOpen] = useState(false);
- const handleEnableCloud = useAsyncCallback(async () => {
- if (!workspace) {
- return;
- }
- const { id: newId } =
- await workspaceManager.transformLocalToCloud(workspace);
- openPage(newId, WorkspaceSubPath.ALL);
- setOpen(false);
- setSettingModal(settings => ({
- ...settings,
- open: false,
- }));
- }, [openPage, setSettingModal, workspace, workspaceManager]);
+ const confirmEnableCloudAndClose = useCallback(() => {
+ if (!workspace) return;
+ confirmEnableCloud(workspace, {
+ onSuccess: () => {
+ setSettingModal(settings => ({ ...settings, open: false }));
+ },
+ });
+ }, [confirmEnableCloud, setSettingModal, workspace]);
if (workspaceMetadata.flavour !== WorkspaceFlavour.LOCAL) {
return null;
@@ -69,21 +58,13 @@ export const EnableCloudPanel = ({
- {runtimeConfig.enableCloud ? (
-
- ) : (
+ {runtimeConfig.enableCloud ? null : (
)}
>
diff --git a/packages/frontend/core/src/components/affine/share-page-modal/index.tsx b/packages/frontend/core/src/components/affine/share-page-modal/index.tsx
index af62e7f68b..e697661b1a 100644
--- a/packages/frontend/core/src/components/affine/share-page-modal/index.tsx
+++ b/packages/frontend/core/src/components/affine/share-page-modal/index.tsx
@@ -1,12 +1,7 @@
-import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
-import { WorkspaceFlavour } from '@affine/env/workspace';
+import { useEnableCloud } from '@affine/core/hooks/affine/use-enable-cloud';
import type { Doc } from '@blocksuite/store';
import type { Workspace } from '@toeverything/infra';
-import { useService, WorkspaceManager } from '@toeverything/infra';
-import { useState } from 'react';
-import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
-import { EnableAffineCloudModal } from '../enable-affine-cloud-modal';
import { ShareMenu } from './share-menu';
type SharePageModalProps = {
@@ -20,37 +15,18 @@ export const SharePageButton = ({
page,
isJournal,
}: SharePageModalProps) => {
- const [open, setOpen] = useState(false);
-
- const { openPage } = useNavigateHelper();
-
- const workspaceManager = useService(WorkspaceManager);
-
- const handleConfirm = useAsyncCallback(async () => {
- if (workspace.flavour !== WorkspaceFlavour.LOCAL) {
- return;
- }
- const { id: newId } =
- await workspaceManager.transformLocalToCloud(workspace);
- openPage(newId, page.id);
- setOpen(false);
- }, [openPage, page.id, workspace, workspaceManager]);
+ const confirmEnableCloud = useEnableCloud();
return (
- <>
- setOpen(true)}
- />
- {workspace.flavour === WorkspaceFlavour.LOCAL ? (
-
- ) : null}
- >
+
+ confirmEnableCloud(workspace, {
+ openPageId: page.id,
+ })
+ }
+ />
);
};
diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/workspace-list/index.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/workspace-list/index.tsx
index 4e72fce242..afd8e23f16 100644
--- a/packages/frontend/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/workspace-list/index.tsx
+++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/workspace-list/index.tsx
@@ -2,6 +2,7 @@ import { ScrollableContainer } from '@affine/component';
import { Divider } from '@affine/component/ui/divider';
import { WorkspaceList } from '@affine/component/workspace-list';
import { useSession } from '@affine/core/hooks/affine/use-current-user';
+import { useEnableCloud } from '@affine/core/hooks/affine/use-enable-cloud';
import {
useWorkspaceAvatar,
useWorkspaceName,
@@ -13,7 +14,7 @@ import type { DragEndEvent } from '@dnd-kit/core';
import type { WorkspaceMetadata } from '@toeverything/infra';
import { useLiveData, useService, WorkspaceManager } from '@toeverything/infra';
import { useSetAtom } from 'jotai';
-import { useCallback, useMemo } from 'react';
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
openCreateWorkspaceModalAtom,
@@ -28,8 +29,10 @@ interface WorkspaceModalProps {
disabled?: boolean;
workspaces: WorkspaceMetadata[];
currentWorkspaceId?: string | null;
+ openingId?: string | null;
onClickWorkspace: (workspaceMetadata: WorkspaceMetadata) => void;
onClickWorkspaceSetting: (workspaceMetadata: WorkspaceMetadata) => void;
+ onClickEnableCloud?: (meta: WorkspaceMetadata) => void;
onNewWorkspace: () => void;
onAddWorkspace: () => void;
onDragEnd: (event: DragEndEvent) => void;
@@ -77,6 +80,8 @@ const LocalWorkspaces = ({
workspaces,
onClickWorkspace,
onClickWorkspaceSetting,
+ onClickEnableCloud,
+ openingId,
currentWorkspaceId,
onDragEnd,
}: WorkspaceModalProps) => {
@@ -95,11 +100,13 @@ const LocalWorkspaces = ({
{t['com.affine.workspaceList.workspaceListType.local']()}
void;
}) => {
- const workspaces = useLiveData(
- useService(WorkspaceManager).list.workspaceList$
- );
+ const openWsRef = useRef>();
+ const workspaceManager = useService(WorkspaceManager);
+ const workspaces = useLiveData(workspaceManager.list.workspaceList$);
const setOpenCreateWorkspaceModal = useSetAtom(openCreateWorkspaceModalAtom);
+ const [openingId, setOpeningId] = useState(null);
+ const confirmEnableCloud = useEnableCloud();
const { jumpToSubPath } = useNavigateHelper();
@@ -160,6 +169,28 @@ export const AFFiNEWorkspaceList = ({
[onEventEnd, setOpenSettingModalAtom]
);
+ const onClickEnableCloud = useCallback(
+ (meta: WorkspaceMetadata) => {
+ openWsRef.current?.release();
+ openWsRef.current = workspaceManager.open(meta);
+ confirmEnableCloud(openWsRef.current.workspace, {
+ onFinished: () => {
+ openWsRef.current?.release();
+ openWsRef.current = undefined;
+ setOpeningId(null);
+ },
+ });
+ setOpeningId(meta.id);
+ },
+ [confirmEnableCloud, workspaceManager]
+ );
+
+ useEffect(() => {
+ return () => {
+ openWsRef.current?.release();
+ };
+ }, []);
+
const onMoveWorkspace = useCallback((_activeId: string, _overId: string) => {
// TODO: order
// const oldIndex = workspaces.findIndex(w => w.id === activeId);
@@ -219,9 +250,11 @@ export const AFFiNEWorkspaceList = ({
) : null}