mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-27 10:52:40 +08:00
feat(core): support creating cloud workspaces to different servers (#9006)
This commit is contained in:
@@ -23,14 +23,15 @@ export class WorkspaceTransformService extends Service {
|
|||||||
*/
|
*/
|
||||||
transformLocalToCloud = async (
|
transformLocalToCloud = async (
|
||||||
local: Workspace,
|
local: Workspace,
|
||||||
accountId: string
|
accountId: string,
|
||||||
|
flavour: string
|
||||||
): Promise<WorkspaceMetadata> => {
|
): Promise<WorkspaceMetadata> => {
|
||||||
assertEquals(local.flavour, 'local');
|
assertEquals(local.flavour, 'local');
|
||||||
|
|
||||||
const localDocStorage = local.engine.doc.storage.behavior;
|
const localDocStorage = local.engine.doc.storage.behavior;
|
||||||
|
|
||||||
const newMetadata = await this.factory.create(
|
const newMetadata = await this.factory.create(
|
||||||
'affine-cloud',
|
flavour,
|
||||||
async (docCollection, blobStorage, docStorage) => {
|
async (docCollection, blobStorage, docStorage) => {
|
||||||
const rootDocBinary = await localDocStorage.doc.get(
|
const rootDocBinary = await localDocStorage.doc.get(
|
||||||
local.docCollection.doc.guid
|
local.docCollection.doc.guid
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export function registerAffineCreationCommands({
|
|||||||
run() {
|
run() {
|
||||||
track.$.cmdk.workspace.createWorkspace();
|
track.$.cmdk.workspace.createWorkspace();
|
||||||
|
|
||||||
globalDialogService.open('create-workspace', undefined);
|
globalDialogService.open('create-workspace', {});
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { notify, useConfirmModal } from '@affine/component';
|
import { notify, useConfirmModal } from '@affine/component';
|
||||||
import { AuthService } from '@affine/core/modules/cloud';
|
import { AuthService, ServersService } from '@affine/core/modules/cloud';
|
||||||
import { GlobalDialogService } from '@affine/core/modules/dialogs';
|
import { GlobalDialogService } from '@affine/core/modules/dialogs';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import type { Workspace } from '@toeverything/infra';
|
import type { Workspace } from '@toeverything/infra';
|
||||||
@@ -22,6 +22,7 @@ interface ConfirmEnableCloudOptions {
|
|||||||
*/
|
*/
|
||||||
onFinished?: () => void;
|
onFinished?: () => void;
|
||||||
openPageId?: string;
|
openPageId?: string;
|
||||||
|
serverId?: string;
|
||||||
}
|
}
|
||||||
type ConfirmEnableArgs = [Workspace, ConfirmEnableCloudOptions | undefined];
|
type ConfirmEnableArgs = [Workspace, ConfirmEnableCloudOptions | undefined];
|
||||||
|
|
||||||
@@ -33,6 +34,9 @@ export const useEnableCloud = () => {
|
|||||||
const globalDialogService = useService(GlobalDialogService);
|
const globalDialogService = useService(GlobalDialogService);
|
||||||
const { openConfirmModal, closeConfirmModal } = useConfirmModal();
|
const { openConfirmModal, closeConfirmModal } = useConfirmModal();
|
||||||
const workspacesService = useService(WorkspacesService);
|
const workspacesService = useService(WorkspacesService);
|
||||||
|
const serversService = useService(ServersService);
|
||||||
|
const serverList = useLiveData(serversService.servers$);
|
||||||
|
|
||||||
const { jumpToPage } = useNavigateHelper();
|
const { jumpToPage } = useNavigateHelper();
|
||||||
|
|
||||||
const enableCloud = useCallback(
|
const enableCloud = useCallback(
|
||||||
@@ -42,7 +46,8 @@ export const useEnableCloud = () => {
|
|||||||
if (!account) return;
|
if (!account) return;
|
||||||
const { id: newId } = await workspacesService.transformLocalToCloud(
|
const { id: newId } = await workspacesService.transformLocalToCloud(
|
||||||
ws,
|
ws,
|
||||||
account.id
|
account.id,
|
||||||
|
'affine-cloud'
|
||||||
);
|
);
|
||||||
jumpToPage(newId, options?.openPageId || 'all');
|
jumpToPage(newId, options?.openPageId || 'all');
|
||||||
options?.onSuccess?.();
|
options?.onSuccess?.();
|
||||||
@@ -56,9 +61,13 @@ export const useEnableCloud = () => {
|
|||||||
[account, jumpToPage, t, workspacesService]
|
[account, jumpToPage, t, workspacesService]
|
||||||
);
|
);
|
||||||
|
|
||||||
const openSignIn = useCallback(() => {
|
const openSignIn = useCallback(
|
||||||
globalDialogService.open('sign-in', {});
|
() =>
|
||||||
}, [globalDialogService]);
|
globalDialogService.open('sign-in', {
|
||||||
|
step: 'signIn',
|
||||||
|
}),
|
||||||
|
[globalDialogService]
|
||||||
|
);
|
||||||
|
|
||||||
const signInOrEnableCloud = useCallback(
|
const signInOrEnableCloud = useCallback(
|
||||||
async (...args: ConfirmEnableArgs) => {
|
async (...args: ConfirmEnableArgs) => {
|
||||||
@@ -76,13 +85,22 @@ export const useEnableCloud = () => {
|
|||||||
|
|
||||||
const confirmEnableCloud = useCallback(
|
const confirmEnableCloud = useCallback(
|
||||||
(ws: Workspace, options?: ConfirmEnableCloudOptions) => {
|
(ws: Workspace, options?: ConfirmEnableCloudOptions) => {
|
||||||
const { onSuccess, onFinished } = options ?? {};
|
const { onSuccess, onFinished, serverId, openPageId } = options ?? {};
|
||||||
|
|
||||||
const closeOnSuccess = () => {
|
const closeOnSuccess = () => {
|
||||||
closeConfirmModal();
|
closeConfirmModal();
|
||||||
onSuccess?.();
|
onSuccess?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (serverList.length > 1) {
|
||||||
|
globalDialogService.open('enable-cloud', {
|
||||||
|
workspaceId: ws.id,
|
||||||
|
serverId,
|
||||||
|
openPageId,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
openConfirmModal(
|
openConfirmModal(
|
||||||
{
|
{
|
||||||
title: t['Enable AFFiNE Cloud'](),
|
title: t['Enable AFFiNE Cloud'](),
|
||||||
@@ -110,7 +128,15 @@ export const useEnableCloud = () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[closeConfirmModal, loginStatus, openConfirmModal, signInOrEnableCloud, t]
|
[
|
||||||
|
closeConfirmModal,
|
||||||
|
globalDialogService,
|
||||||
|
loginStatus,
|
||||||
|
openConfirmModal,
|
||||||
|
serverList.length,
|
||||||
|
signInOrEnableCloud,
|
||||||
|
t,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return confirmEnableCloud;
|
return confirmEnableCloud;
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { Menu, MenuItem, type MenuProps, MenuTrigger } from '@affine/component';
|
||||||
|
import type { Server } from '@affine/core/modules/cloud';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { triggerStyle } from './style.css';
|
||||||
|
|
||||||
|
export const ServerSelector = ({
|
||||||
|
servers,
|
||||||
|
selectedSeverName,
|
||||||
|
onSelect,
|
||||||
|
contentOptions,
|
||||||
|
}: {
|
||||||
|
servers: Server[];
|
||||||
|
selectedSeverName: string;
|
||||||
|
onSelect: (server: Server) => void;
|
||||||
|
contentOptions?: MenuProps['contentOptions'];
|
||||||
|
}) => {
|
||||||
|
const menuItems = useMemo(() => {
|
||||||
|
return servers.map(server => (
|
||||||
|
<MenuItem key={server.id} onSelect={() => onSelect(server)}>
|
||||||
|
{server.config$.value.serverName} ({server.baseUrl})
|
||||||
|
</MenuItem>
|
||||||
|
));
|
||||||
|
}, [servers, onSelect]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
items={menuItems}
|
||||||
|
contentOptions={{
|
||||||
|
...contentOptions,
|
||||||
|
style: {
|
||||||
|
...contentOptions?.style,
|
||||||
|
width: 'var(--radix-dropdown-menu-trigger-width)',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuTrigger className={triggerStyle}>{selectedSeverName}</MenuTrigger>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const triggerStyle = style({
|
||||||
|
width: '100%',
|
||||||
|
});
|
||||||
@@ -24,12 +24,18 @@ export interface SignInState {
|
|||||||
export const SignInPanel = ({
|
export const SignInPanel = ({
|
||||||
onClose,
|
onClose,
|
||||||
server: initialServerBaseUrl,
|
server: initialServerBaseUrl,
|
||||||
|
initStep,
|
||||||
}: {
|
}: {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
server?: string;
|
server?: string;
|
||||||
|
initStep?: SignInStep | undefined;
|
||||||
}) => {
|
}) => {
|
||||||
const [state, setState] = useState<SignInState>({
|
const [state, setState] = useState<SignInState>({
|
||||||
step: initialServerBaseUrl ? 'addSelfhosted' : 'signIn',
|
step: initStep
|
||||||
|
? initStep
|
||||||
|
: initialServerBaseUrl
|
||||||
|
? 'addSelfhosted'
|
||||||
|
: 'signIn',
|
||||||
initialServerBaseUrl: initialServerBaseUrl,
|
initialServerBaseUrl: initialServerBaseUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { cssVar } from '@toeverything/theme';
|
||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const root = style({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
|
gap: '20px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const textContainer = style({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
textAlign: 'center',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const title = style({
|
||||||
|
fontSize: cssVar('fontH6'),
|
||||||
|
fontWeight: 600,
|
||||||
|
lineHeight: '26px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const description = style({
|
||||||
|
fontSize: cssVar('fontBase'),
|
||||||
|
fontWeight: 400,
|
||||||
|
lineHeight: '24px',
|
||||||
|
color: cssVar('textSecondaryColor'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const serverSelector = style({
|
||||||
|
width: '100%',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const button = style({
|
||||||
|
width: '100%',
|
||||||
|
});
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import type { Server } from '@affine/core/modules/cloud';
|
||||||
|
import { CloudWorkspaceIcon } from '@blocksuite/icons/rc';
|
||||||
|
|
||||||
|
import { ServerSelector } from '../../server-selector';
|
||||||
|
import * as styles from './enable-cloud.css';
|
||||||
|
|
||||||
|
export const CustomServerEnableCloud = ({
|
||||||
|
serverList,
|
||||||
|
selectedServer,
|
||||||
|
setSelectedServer,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
}: {
|
||||||
|
serverList: Server[];
|
||||||
|
selectedServer: Server;
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
setSelectedServer: (server: Server) => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className={styles.root}>
|
||||||
|
<CloudWorkspaceIcon width={'36px'} height={'36px'} />
|
||||||
|
<div className={styles.textContainer}>
|
||||||
|
{title ? <div className={styles.title}>{title}</div> : null}
|
||||||
|
{description ? (
|
||||||
|
<div className={styles.description}>{description}</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
<div className={styles.serverSelector}>
|
||||||
|
<ServerSelector
|
||||||
|
servers={serverList}
|
||||||
|
selectedSeverName={`${selectedServer.config$.value.serverName} (${selectedServer.baseUrl})`}
|
||||||
|
onSelect={setSelectedServer}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './enable-cloud';
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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',
|
||||||
|
});
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { MenuItem } from '@affine/component/ui/menu';
|
||||||
|
import { useI18n } from '@affine/i18n';
|
||||||
|
import { PlusIcon } from '@blocksuite/icons/rc';
|
||||||
|
import {
|
||||||
|
FeatureFlagService,
|
||||||
|
useLiveData,
|
||||||
|
useService,
|
||||||
|
} from '@toeverything/infra';
|
||||||
|
|
||||||
|
import * as styles from './index.css';
|
||||||
|
|
||||||
|
export const AddServer = ({ onAddServer }: { onAddServer?: () => void }) => {
|
||||||
|
const t = useI18n();
|
||||||
|
const featureFlagService = useService(FeatureFlagService);
|
||||||
|
const enableMultipleServer = useLiveData(
|
||||||
|
featureFlagService.flags.enable_multiple_cloud_servers.$
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!enableMultipleServer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<MenuItem
|
||||||
|
block={true}
|
||||||
|
prefixIcon={<PlusIcon />}
|
||||||
|
onClick={onAddServer}
|
||||||
|
data-testid="new-server"
|
||||||
|
className={styles.ItemContainer}
|
||||||
|
>
|
||||||
|
<div className={styles.ItemText}>
|
||||||
|
{t['com.affine.workspaceList.addServer']()}
|
||||||
|
</div>
|
||||||
|
</MenuItem>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -14,10 +14,9 @@ import {
|
|||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { useCatchEventCallback } from '../../hooks/use-catch-event-hook';
|
import { AddServer } from './add-server';
|
||||||
import { AddWorkspace } from './add-workspace';
|
import { AddWorkspace } from './add-workspace';
|
||||||
import * as styles from './index.css';
|
import * as styles from './index.css';
|
||||||
import { UserAccountItem } from './user-account';
|
|
||||||
import { AFFiNEWorkspaceList } from './workspace-list';
|
import { AFFiNEWorkspaceList } from './workspace-list';
|
||||||
|
|
||||||
export const SignInItem = () => {
|
export const SignInItem = () => {
|
||||||
@@ -90,7 +89,7 @@ const UserWithWorkspaceListInner = ({
|
|||||||
return openSignInModal();
|
return openSignInModal();
|
||||||
}
|
}
|
||||||
track.$.navigationPanel.workspaceList.createWorkspace();
|
track.$.navigationPanel.workspaceList.createWorkspace();
|
||||||
globalDialogService.open('create-workspace', undefined, payload => {
|
globalDialogService.open('create-workspace', {}, payload => {
|
||||||
if (payload) {
|
if (payload) {
|
||||||
onCreatedWorkspace?.(payload);
|
onCreatedWorkspace?.(payload);
|
||||||
}
|
}
|
||||||
@@ -117,28 +116,15 @@ const UserWithWorkspaceListInner = ({
|
|||||||
onEventEnd?.();
|
onEventEnd?.();
|
||||||
}, [globalDialogService, onCreatedWorkspace, onEventEnd]);
|
}, [globalDialogService, onCreatedWorkspace, onEventEnd]);
|
||||||
|
|
||||||
|
const onAddServer = useCallback(() => {
|
||||||
|
globalDialogService.open('sign-in', { step: 'addSelfhosted' });
|
||||||
|
}, [globalDialogService]);
|
||||||
|
|
||||||
const workspaceManager = useService(WorkspacesService);
|
const workspaceManager = useService(WorkspacesService);
|
||||||
const workspaces = useLiveData(workspaceManager.list.workspaces$);
|
const workspaces = useLiveData(workspaceManager.list.workspaces$);
|
||||||
|
|
||||||
const onOpenPricingPlan = useCatchEventCallback(() => {
|
|
||||||
globalDialogService.open('setting', {
|
|
||||||
activeTab: 'plans',
|
|
||||||
scrollAnchor: 'cloudPricingPlan',
|
|
||||||
});
|
|
||||||
}, [globalDialogService]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.workspaceListWrapper}>
|
<div className={styles.workspaceListWrapper}>
|
||||||
{isAuthenticated ? (
|
|
||||||
<UserAccountItem
|
|
||||||
email={session.session.account.email ?? 'Unknown User'}
|
|
||||||
onEventEnd={onEventEnd}
|
|
||||||
onClick={onOpenPricingPlan}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<SignInItem />
|
|
||||||
)}
|
|
||||||
<Divider size="thinner" />
|
|
||||||
<AFFiNEWorkspaceList
|
<AFFiNEWorkspaceList
|
||||||
onEventEnd={onEventEnd}
|
onEventEnd={onEventEnd}
|
||||||
onClickWorkspace={onClickWorkspace}
|
onClickWorkspace={onClickWorkspace}
|
||||||
@@ -150,6 +136,7 @@ const UserWithWorkspaceListInner = ({
|
|||||||
onAddWorkspace={onAddWorkspace}
|
onAddWorkspace={onAddWorkspace}
|
||||||
onNewWorkspace={onNewWorkspace}
|
onNewWorkspace={onNewWorkspace}
|
||||||
/>
|
/>
|
||||||
|
<AddServer onAddServer={onAddServer} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { cssVar } from '@toeverything/theme';
|
import { cssVar } from '@toeverything/theme';
|
||||||
|
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||||
import { style } from '@vanilla-extract/css';
|
import { style } from '@vanilla-extract/css';
|
||||||
export const workspaceListsWrapper = style({
|
export const workspaceListsWrapper = style({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -15,11 +16,19 @@ export const workspaceListWrapper = style({
|
|||||||
export const workspaceServer = style({
|
export const workspaceServer = style({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
|
||||||
gap: 4,
|
gap: 4,
|
||||||
padding: '0px 12px',
|
paddingLeft: '12px',
|
||||||
|
marginBottom: '4px',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const workspaceServerContent = style({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
color: cssVarV2('text/secondary'),
|
||||||
|
gap: 4,
|
||||||
|
width: '100%',
|
||||||
|
overflow: 'hidden',
|
||||||
|
});
|
||||||
export const workspaceServerName = style({
|
export const workspaceServerName = style({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -27,10 +36,19 @@ export const workspaceServerName = style({
|
|||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
fontSize: cssVar('fontXs'),
|
fontSize: cssVar('fontXs'),
|
||||||
lineHeight: '20px',
|
lineHeight: '20px',
|
||||||
color: cssVar('textSecondaryColor'),
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
});
|
||||||
|
export const account = style({
|
||||||
|
fontSize: cssVar('fontXs'),
|
||||||
|
overflow: 'hidden',
|
||||||
|
width: '100%',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
});
|
});
|
||||||
export const workspaceTypeIcon = style({
|
export const workspaceTypeIcon = style({
|
||||||
color: cssVar('iconSecondary'),
|
color: cssVarV2('icon/primary'),
|
||||||
|
fontSize: '16px',
|
||||||
});
|
});
|
||||||
export const scrollbar = style({
|
export const scrollbar = style({
|
||||||
width: '4px',
|
width: '4px',
|
||||||
@@ -39,3 +57,25 @@ export const workspaceCard = style({
|
|||||||
height: '44px',
|
height: '44px',
|
||||||
padding: '0 12px',
|
padding: '0 12px',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 ItemText = style({
|
||||||
|
fontSize: cssVar('fontSm'),
|
||||||
|
lineHeight: '22px',
|
||||||
|
color: cssVarV2('text/secondary'),
|
||||||
|
fontWeight: 400,
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
});
|
||||||
|
|||||||
@@ -11,11 +11,14 @@ import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-he
|
|||||||
import type { Server } from '@affine/core/modules/cloud';
|
import type { Server } from '@affine/core/modules/cloud';
|
||||||
import { AuthService, ServersService } from '@affine/core/modules/cloud';
|
import { AuthService, ServersService } from '@affine/core/modules/cloud';
|
||||||
import { GlobalDialogService } from '@affine/core/modules/dialogs';
|
import { GlobalDialogService } from '@affine/core/modules/dialogs';
|
||||||
|
import { ServerDeploymentType } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import {
|
import {
|
||||||
CloudWorkspaceIcon,
|
CloudWorkspaceIcon,
|
||||||
LocalWorkspaceIcon,
|
LocalWorkspaceIcon,
|
||||||
MoreHorizontalIcon,
|
MoreHorizontalIcon,
|
||||||
|
PlusIcon,
|
||||||
|
TeamWorkspaceIcon,
|
||||||
} from '@blocksuite/icons/rc';
|
} from '@blocksuite/icons/rc';
|
||||||
import type { WorkspaceMetadata } from '@toeverything/infra';
|
import type { WorkspaceMetadata } from '@toeverything/infra';
|
||||||
import {
|
import {
|
||||||
@@ -54,6 +57,7 @@ const CloudWorkSpaceList = ({
|
|||||||
onClickWorkspaceSetting?: (workspaceMetadata: WorkspaceMetadata) => void;
|
onClickWorkspaceSetting?: (workspaceMetadata: WorkspaceMetadata) => void;
|
||||||
onClickEnableCloud?: (meta: WorkspaceMetadata) => void;
|
onClickEnableCloud?: (meta: WorkspaceMetadata) => void;
|
||||||
}) => {
|
}) => {
|
||||||
|
const t = useI18n();
|
||||||
const globalContextService = useService(GlobalContextService);
|
const globalContextService = useService(GlobalContextService);
|
||||||
const globalDialogService = useService(GlobalDialogService);
|
const globalDialogService = useService(GlobalDialogService);
|
||||||
const serverName = useLiveData(server.config$.selector(c => c.serverName));
|
const serverName = useLiveData(server.config$.selector(c => c.serverName));
|
||||||
@@ -67,6 +71,8 @@ const CloudWorkSpaceList = ({
|
|||||||
globalContextService.globalContext.workspaceFlavour.$
|
globalContextService.globalContext.workspaceFlavour.$
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const serverType = server.config$.value.type;
|
||||||
|
|
||||||
const handleDeleteServer = useCallback(() => {
|
const handleDeleteServer = useCallback(() => {
|
||||||
serversService.removeServer(server.id);
|
serversService.removeServer(server.id);
|
||||||
|
|
||||||
@@ -94,18 +100,38 @@ const CloudWorkSpaceList = ({
|
|||||||
});
|
});
|
||||||
}, [globalDialogService, server.baseUrl]);
|
}, [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 (
|
return (
|
||||||
<div className={styles.workspaceListWrapper}>
|
<div className={styles.workspaceListWrapper}>
|
||||||
<div className={styles.workspaceServer}>
|
<div className={styles.workspaceServer}>
|
||||||
<div className={styles.workspaceServerName}>
|
<div className={styles.workspaceServerContent}>
|
||||||
<CloudWorkspaceIcon
|
<div className={styles.workspaceServerName}>
|
||||||
width={14}
|
{serverType === ServerDeploymentType.Affine ? (
|
||||||
height={14}
|
<CloudWorkspaceIcon className={styles.workspaceTypeIcon} />
|
||||||
className={styles.workspaceTypeIcon}
|
) : (
|
||||||
/>
|
<TeamWorkspaceIcon className={styles.workspaceTypeIcon} />
|
||||||
{serverName} -
|
)}
|
||||||
{account ? account.email : 'Not signed in'}
|
<div className={styles.account}>{serverName}</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.account}>
|
||||||
|
{account ? account.email : 'Not signed in'}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Menu
|
<Menu
|
||||||
items={[
|
items={[
|
||||||
server.id !== 'affine-cloud' && (
|
server.id !== 'affine-cloud' && (
|
||||||
@@ -125,7 +151,9 @@ const CloudWorkSpaceList = ({
|
|||||||
),
|
),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<IconButton icon={<MoreHorizontalIcon />} />
|
<div>
|
||||||
|
<IconButton icon={<MoreHorizontalIcon />} />
|
||||||
|
</div>
|
||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
<WorkspaceList
|
<WorkspaceList
|
||||||
@@ -134,6 +162,16 @@ const CloudWorkSpaceList = ({
|
|||||||
onSettingClick={onClickWorkspaceSetting}
|
onSettingClick={onClickWorkspaceSetting}
|
||||||
onEnableCloudClick={onClickEnableCloud}
|
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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import * as styles from './dialog.css';
|
|||||||
|
|
||||||
interface NameWorkspaceContentProps extends ConfirmModalProps {
|
interface NameWorkspaceContentProps extends ConfirmModalProps {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
forcedCloud?: boolean;
|
||||||
|
serverId?: string;
|
||||||
onConfirmName: (
|
onConfirmName: (
|
||||||
name: string,
|
name: string,
|
||||||
workspaceFlavour: string,
|
workspaceFlavour: string,
|
||||||
@@ -33,15 +35,14 @@ interface NameWorkspaceContentProps extends ConfirmModalProps {
|
|||||||
const NameWorkspaceContent = ({
|
const NameWorkspaceContent = ({
|
||||||
loading,
|
loading,
|
||||||
onConfirmName,
|
onConfirmName,
|
||||||
|
forcedCloud,
|
||||||
|
serverId,
|
||||||
...props
|
...props
|
||||||
}: NameWorkspaceContentProps) => {
|
}: NameWorkspaceContentProps) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const [workspaceName, setWorkspaceName] = useState('');
|
const [workspaceName, setWorkspaceName] = useState('');
|
||||||
const featureFlagService = useService(FeatureFlagService);
|
|
||||||
const enableLocalWorkspace = useLiveData(
|
const [enable, setEnable] = useState(!!forcedCloud);
|
||||||
featureFlagService.flags.enable_local_workspace.$
|
|
||||||
);
|
|
||||||
const [enable, setEnable] = useState(!enableLocalWorkspace);
|
|
||||||
const session = useService(AuthService).session;
|
const session = useService(AuthService).session;
|
||||||
const loginStatus = useLiveData(session.status$);
|
const loginStatus = useLiveData(session.status$);
|
||||||
|
|
||||||
@@ -62,8 +63,8 @@ const NameWorkspaceContent = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleCreateWorkspace = useCallback(() => {
|
const handleCreateWorkspace = useCallback(() => {
|
||||||
onConfirmName(workspaceName, enable ? 'affine-cloud' : 'local');
|
onConfirmName(workspaceName, enable ? serverId || 'affine-cloud' : 'local');
|
||||||
}, [enable, onConfirmName, workspaceName]);
|
}, [enable, onConfirmName, serverId, workspaceName]);
|
||||||
|
|
||||||
const onEnter = useCallback(() => {
|
const onEnter = useCallback(() => {
|
||||||
if (workspaceName) {
|
if (workspaceName) {
|
||||||
@@ -120,7 +121,7 @@ const NameWorkspaceContent = ({
|
|||||||
<Switch
|
<Switch
|
||||||
checked={enable}
|
checked={enable}
|
||||||
onChange={onSwitchChange}
|
onChange={onSwitchChange}
|
||||||
disabled={!enableLocalWorkspace}
|
disabled={forcedCloud}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.cardDescription}>
|
<div className={styles.cardDescription}>
|
||||||
@@ -131,7 +132,7 @@ const NameWorkspaceContent = ({
|
|||||||
<CloudSvg />
|
<CloudSvg />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!enableLocalWorkspace ? (
|
{forcedCloud ? (
|
||||||
<a
|
<a
|
||||||
className={styles.cloudTips}
|
className={styles.cloudTips}
|
||||||
href={BUILD_CONFIG.downloadUrl}
|
href={BUILD_CONFIG.downloadUrl}
|
||||||
@@ -147,9 +148,15 @@ const NameWorkspaceContent = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CreateWorkspaceDialog = ({
|
export const CreateWorkspaceDialog = ({
|
||||||
|
forcedCloud,
|
||||||
|
serverId,
|
||||||
close,
|
close,
|
||||||
}: DialogComponentProps<GLOBAL_DIALOG_SCHEMA['create-workspace']>) => {
|
}: DialogComponentProps<GLOBAL_DIALOG_SCHEMA['create-workspace']>) => {
|
||||||
const workspacesService = useService(WorkspacesService);
|
const workspacesService = useService(WorkspacesService);
|
||||||
|
const featureFlagService = useService(FeatureFlagService);
|
||||||
|
const enableLocalWorkspace = useLiveData(
|
||||||
|
featureFlagService.flags.enable_local_workspace.$
|
||||||
|
);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const onConfirmName = useAsyncCallback(
|
const onConfirmName = useAsyncCallback(
|
||||||
@@ -185,6 +192,8 @@ export const CreateWorkspaceDialog = ({
|
|||||||
<NameWorkspaceContent
|
<NameWorkspaceContent
|
||||||
loading={loading}
|
loading={loading}
|
||||||
open
|
open
|
||||||
|
serverId={serverId}
|
||||||
|
forcedCloud={forcedCloud || !enableLocalWorkspace}
|
||||||
onOpenChange={onOpenChange}
|
onOpenChange={onOpenChange}
|
||||||
onConfirmName={onConfirmName}
|
onConfirmName={onConfirmName}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import { cssVar } from '@toeverything/theme';
|
||||||
|
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const dialogContainer = style({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
color: cssVarV2('text/primary'),
|
||||||
|
padding: '16px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const mainIcon = style({
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
color: cssVarV2('icon/primary'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const mainTitle = style({
|
||||||
|
fontSize: '18px',
|
||||||
|
lineHeight: '26px',
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: '16px',
|
||||||
|
fontWeight: 600,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const desc = style({
|
||||||
|
textAlign: 'center',
|
||||||
|
color: cssVarV2('text/secondary'),
|
||||||
|
marginBottom: '20px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const mainButton = style({
|
||||||
|
width: '100%',
|
||||||
|
fontSize: '14px',
|
||||||
|
height: '42px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const modal = style({
|
||||||
|
maxWidth: '352px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const workspaceSelector = style({
|
||||||
|
margin: '0 -16px',
|
||||||
|
width: 'calc(100% + 32px)',
|
||||||
|
border: `1px solid ${cssVarV2('layer/insideBorder/border')}`,
|
||||||
|
padding: '0 16px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const root = style({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
|
gap: '20px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const textContainer = style({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
textAlign: 'center',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const title = style({
|
||||||
|
fontSize: cssVar('fontH6'),
|
||||||
|
fontWeight: 600,
|
||||||
|
lineHeight: '26px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const description = style({
|
||||||
|
fontSize: cssVar('fontBase'),
|
||||||
|
fontWeight: 400,
|
||||||
|
lineHeight: '24px',
|
||||||
|
color: cssVar('textSecondaryColor'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const serverSelector = style({
|
||||||
|
width: '100%',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const button = style({
|
||||||
|
width: '100%',
|
||||||
|
marginTop: '20px',
|
||||||
|
});
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
import { Button, Modal, notify } from '@affine/component';
|
||||||
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
|
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
||||||
|
import { ServerSelector } from '@affine/core/components/server-selector';
|
||||||
|
import {
|
||||||
|
AuthService,
|
||||||
|
type Server,
|
||||||
|
ServersService,
|
||||||
|
} from '@affine/core/modules/cloud';
|
||||||
|
import {
|
||||||
|
type DialogComponentProps,
|
||||||
|
type GLOBAL_DIALOG_SCHEMA,
|
||||||
|
GlobalDialogService,
|
||||||
|
} from '@affine/core/modules/dialogs';
|
||||||
|
import { useI18n } from '@affine/i18n';
|
||||||
|
import { CloudWorkspaceIcon } from '@blocksuite/icons/rc';
|
||||||
|
import {
|
||||||
|
FrameworkScope,
|
||||||
|
useLiveData,
|
||||||
|
useService,
|
||||||
|
WorkspacesService,
|
||||||
|
} from '@toeverything/infra';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
import * as styles from './dialog.css';
|
||||||
|
|
||||||
|
const Dialog = ({
|
||||||
|
workspaceId,
|
||||||
|
close,
|
||||||
|
selectedServer,
|
||||||
|
setSelectedServer,
|
||||||
|
serverList,
|
||||||
|
openPageId,
|
||||||
|
}: {
|
||||||
|
workspaceId: string;
|
||||||
|
serverList: Server[];
|
||||||
|
selectedServer: Server;
|
||||||
|
setSelectedServer: (server: Server) => void;
|
||||||
|
openPageId?: string;
|
||||||
|
serverId?: string;
|
||||||
|
close?: () => void;
|
||||||
|
}) => {
|
||||||
|
const t = useI18n();
|
||||||
|
const authService = useService(AuthService);
|
||||||
|
const account = useLiveData(authService.session.account$);
|
||||||
|
const loginStatus = useLiveData(useService(AuthService).session.status$);
|
||||||
|
const globalDialogService = useService(GlobalDialogService);
|
||||||
|
const workspacesService = useService(WorkspacesService);
|
||||||
|
const workspaceMeta = useLiveData(
|
||||||
|
workspacesService.list.workspace$(workspaceId)
|
||||||
|
);
|
||||||
|
const { workspace } = workspaceMeta
|
||||||
|
? workspacesService.open({ metadata: workspaceMeta })
|
||||||
|
: { workspace: undefined };
|
||||||
|
|
||||||
|
const { jumpToPage } = useNavigateHelper();
|
||||||
|
|
||||||
|
const enableCloud = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
if (!workspace) return;
|
||||||
|
if (!account) return;
|
||||||
|
|
||||||
|
const { id: newId } = await workspacesService.transformLocalToCloud(
|
||||||
|
workspace,
|
||||||
|
account.id,
|
||||||
|
selectedServer.id
|
||||||
|
);
|
||||||
|
jumpToPage(newId, openPageId || 'all');
|
||||||
|
close?.();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
notify.error({
|
||||||
|
title: t['com.affine.workspace.enable-cloud.failed'](),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
workspace,
|
||||||
|
account,
|
||||||
|
workspacesService,
|
||||||
|
selectedServer.id,
|
||||||
|
jumpToPage,
|
||||||
|
openPageId,
|
||||||
|
close,
|
||||||
|
t,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const openSignIn = useCallback(() => {
|
||||||
|
globalDialogService.open('sign-in', {
|
||||||
|
server: selectedServer.baseUrl,
|
||||||
|
});
|
||||||
|
}, [globalDialogService, selectedServer.baseUrl]);
|
||||||
|
|
||||||
|
const signInOrEnableCloud = useAsyncCallback(async () => {
|
||||||
|
// not logged in, open login modal
|
||||||
|
if (loginStatus === 'unauthenticated') {
|
||||||
|
openSignIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loginStatus === 'authenticated') {
|
||||||
|
await enableCloud();
|
||||||
|
}
|
||||||
|
}, [enableCloud, loginStatus, openSignIn]);
|
||||||
|
return (
|
||||||
|
<div className={styles.root}>
|
||||||
|
<CloudWorkspaceIcon width={'36px'} height={'36px'} />
|
||||||
|
<div className={styles.textContainer}>
|
||||||
|
<div className={styles.title}>
|
||||||
|
{t['com.affine.enableAffineCloudModal.custom-server.title']({
|
||||||
|
workspaceName: workspace?.name$.value || 'untitled',
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className={styles.description}>
|
||||||
|
{t['com.affine.enableAffineCloudModal.custom-server.description']()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.serverSelector}>
|
||||||
|
<ServerSelector
|
||||||
|
servers={serverList}
|
||||||
|
selectedSeverName={`${selectedServer.config$.value.serverName} (${selectedServer.baseUrl})`}
|
||||||
|
onSelect={setSelectedServer}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className={styles.button}
|
||||||
|
onClick={signInOrEnableCloud}
|
||||||
|
size="extraLarge"
|
||||||
|
variant="primary"
|
||||||
|
>
|
||||||
|
{t['com.affine.enableAffineCloudModal.custom-server.enable']()}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EnableCloudDialog = ({
|
||||||
|
workspaceId,
|
||||||
|
openPageId,
|
||||||
|
serverId,
|
||||||
|
close,
|
||||||
|
}: DialogComponentProps<GLOBAL_DIALOG_SCHEMA['enable-cloud']>) => {
|
||||||
|
const serversService = useService(ServersService);
|
||||||
|
const serverList = useLiveData(serversService.servers$);
|
||||||
|
const [selectedServer, setSelectedServer] = useState<Server>(serverList[0]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open
|
||||||
|
modal={true}
|
||||||
|
persistent
|
||||||
|
contentOptions={{
|
||||||
|
className: styles.modal,
|
||||||
|
}}
|
||||||
|
onOpenChange={() => close()}
|
||||||
|
>
|
||||||
|
<FrameworkScope key={selectedServer.id} scope={selectedServer.scope}>
|
||||||
|
<Dialog
|
||||||
|
workspaceId={workspaceId}
|
||||||
|
openPageId={openPageId}
|
||||||
|
serverId={serverId}
|
||||||
|
close={close}
|
||||||
|
serverList={serverList}
|
||||||
|
selectedServer={selectedServer}
|
||||||
|
setSelectedServer={setSelectedServer}
|
||||||
|
/>
|
||||||
|
</FrameworkScope>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -11,6 +11,7 @@ import { ChangePasswordDialog } from './change-password';
|
|||||||
import { CollectionEditorDialog } from './collection-editor';
|
import { CollectionEditorDialog } from './collection-editor';
|
||||||
import { CreateWorkspaceDialog } from './create-workspace';
|
import { CreateWorkspaceDialog } from './create-workspace';
|
||||||
import { DocInfoDialog } from './doc-info';
|
import { DocInfoDialog } from './doc-info';
|
||||||
|
import { EnableCloudDialog } from './enable-cloud';
|
||||||
import { ImportDialog } from './import';
|
import { ImportDialog } from './import';
|
||||||
import { ImportTemplateDialog } from './import-template';
|
import { ImportTemplateDialog } from './import-template';
|
||||||
import { ImportWorkspaceDialog } from './import-workspace';
|
import { ImportWorkspaceDialog } from './import-workspace';
|
||||||
@@ -30,6 +31,7 @@ const GLOBAL_DIALOGS = {
|
|||||||
'sign-in': SignInDialog,
|
'sign-in': SignInDialog,
|
||||||
'change-password': ChangePasswordDialog,
|
'change-password': ChangePasswordDialog,
|
||||||
'verify-email': VerifyEmailDialog,
|
'verify-email': VerifyEmailDialog,
|
||||||
|
'enable-cloud': EnableCloudDialog,
|
||||||
} satisfies {
|
} satisfies {
|
||||||
[key in keyof GLOBAL_DIALOG_SCHEMA]?: React.FC<
|
[key in keyof GLOBAL_DIALOG_SCHEMA]?: React.FC<
|
||||||
DialogComponentProps<GLOBAL_DIALOG_SCHEMA[key]>
|
DialogComponentProps<GLOBAL_DIALOG_SCHEMA[key]>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Modal } from '@affine/component';
|
import { Modal } from '@affine/component';
|
||||||
import { SignInPanel } from '@affine/core/components/sign-in';
|
import { SignInPanel, type SignInStep } from '@affine/core/components/sign-in';
|
||||||
import type {
|
import type {
|
||||||
DialogComponentProps,
|
DialogComponentProps,
|
||||||
GLOBAL_DIALOG_SCHEMA,
|
GLOBAL_DIALOG_SCHEMA,
|
||||||
@@ -7,6 +7,7 @@ import type {
|
|||||||
export const SignInDialog = ({
|
export const SignInDialog = ({
|
||||||
close,
|
close,
|
||||||
server: initialServerBaseUrl,
|
server: initialServerBaseUrl,
|
||||||
|
step,
|
||||||
}: DialogComponentProps<GLOBAL_DIALOG_SCHEMA['sign-in']>) => {
|
}: DialogComponentProps<GLOBAL_DIALOG_SCHEMA['sign-in']>) => {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -19,7 +20,11 @@ export const SignInDialog = ({
|
|||||||
style: { padding: '44px 40px 20px' },
|
style: { padding: '44px 40px 20px' },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SignInPanel onClose={close} server={initialServerBaseUrl} />
|
<SignInPanel
|
||||||
|
onClose={close}
|
||||||
|
server={initialServerBaseUrl}
|
||||||
|
initStep={step as SignInStep}
|
||||||
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export type SettingTab =
|
|||||||
| `workspace:${'preference' | 'properties'}`;
|
| `workspace:${'preference' | 'properties'}`;
|
||||||
|
|
||||||
export type GLOBAL_DIALOG_SCHEMA = {
|
export type GLOBAL_DIALOG_SCHEMA = {
|
||||||
'create-workspace': () => {
|
'create-workspace': (props: { serverId?: string; forcedCloud?: boolean }) => {
|
||||||
metadata: WorkspaceMetadata;
|
metadata: WorkspaceMetadata;
|
||||||
defaultDocId?: string;
|
defaultDocId?: string;
|
||||||
};
|
};
|
||||||
@@ -31,9 +31,14 @@ export type GLOBAL_DIALOG_SCHEMA = {
|
|||||||
workspaceMetadata?: WorkspaceMetadata | null;
|
workspaceMetadata?: WorkspaceMetadata | null;
|
||||||
scrollAnchor?: string;
|
scrollAnchor?: string;
|
||||||
}) => void;
|
}) => void;
|
||||||
'sign-in': (props: { server?: string; step?: 'sign-in' }) => void;
|
'sign-in': (props: { server?: string; step?: string }) => void;
|
||||||
'change-password': (props: { server?: string }) => void;
|
'change-password': (props: { server?: string }) => void;
|
||||||
'verify-email': (props: { server?: string; changeEmail?: boolean }) => void;
|
'verify-email': (props: { server?: string; changeEmail?: boolean }) => void;
|
||||||
|
'enable-cloud': (props: {
|
||||||
|
workspaceId: string;
|
||||||
|
openPageId?: string;
|
||||||
|
serverId?: string;
|
||||||
|
}) => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WORKSPACE_DIALOG_SCHEMA = {
|
export type WORKSPACE_DIALOG_SCHEMA = {
|
||||||
|
|||||||
@@ -20,5 +20,5 @@
|
|||||||
"sv-SE": 4,
|
"sv-SE": 4,
|
||||||
"ur": 3,
|
"ur": 3,
|
||||||
"zh-Hans": 99,
|
"zh-Hans": 99,
|
||||||
"zh-Hant": 99
|
"zh-Hant": 98
|
||||||
}
|
}
|
||||||
@@ -473,6 +473,9 @@
|
|||||||
"com.affine.empty.tags.title": "Tag management",
|
"com.affine.empty.tags.title": "Tag management",
|
||||||
"com.affine.emptyDesc": "There's no doc here yet",
|
"com.affine.emptyDesc": "There's no doc here yet",
|
||||||
"com.affine.enableAffineCloudModal.button.cancel": "Cancel",
|
"com.affine.enableAffineCloudModal.button.cancel": "Cancel",
|
||||||
|
"com.affine.enableAffineCloudModal.custom-server.title": "Enable Cloud for {{workspaceName}}",
|
||||||
|
"com.affine.enableAffineCloudModal.custom-server.description": "Choose an instance.",
|
||||||
|
"com.affine.enableAffineCloudModal.custom-server.enable": "Enable Cloud",
|
||||||
"com.affine.error.hide-error": "Hide error",
|
"com.affine.error.hide-error": "Hide error",
|
||||||
"com.affine.error.no-page-root.title": "Doc content is missing",
|
"com.affine.error.no-page-root.title": "Doc content is missing",
|
||||||
"com.affine.error.refetch": "Refetch",
|
"com.affine.error.refetch": "Refetch",
|
||||||
@@ -1432,6 +1435,7 @@
|
|||||||
"com.affine.workspaceList.addWorkspace.create-cloud": "Create cloud workspace",
|
"com.affine.workspaceList.addWorkspace.create-cloud": "Create cloud workspace",
|
||||||
"com.affine.workspaceList.workspaceListType.cloud": "Cloud sync",
|
"com.affine.workspaceList.workspaceListType.cloud": "Cloud sync",
|
||||||
"com.affine.workspaceList.workspaceListType.local": "Local storage",
|
"com.affine.workspaceList.workspaceListType.local": "Local storage",
|
||||||
|
"com.affine.workspaceList.addServer": "Add Server",
|
||||||
"com.affine.workspaceSubPath.all": "All docs",
|
"com.affine.workspaceSubPath.all": "All docs",
|
||||||
"com.affine.workspaceSubPath.trash": "Trash",
|
"com.affine.workspaceSubPath.trash": "Trash",
|
||||||
"com.affine.workspaceSubPath.trash.empty-description": "Deleted docs will appear here.",
|
"com.affine.workspaceSubPath.trash.empty-description": "Deleted docs will appear here.",
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { waitForEditorLoad } from '@affine-test/kit/utils/page-logic';
|
|||||||
import { clickUserInfoCard } from '@affine-test/kit/utils/setting';
|
import { clickUserInfoCard } from '@affine-test/kit/utils/setting';
|
||||||
import {
|
import {
|
||||||
clickSideBarAllPageButton,
|
clickSideBarAllPageButton,
|
||||||
clickSideBarCurrentWorkspaceBanner,
|
|
||||||
clickSideBarSettingButton,
|
clickSideBarSettingButton,
|
||||||
clickSideBarUseAvatar,
|
clickSideBarUseAvatar,
|
||||||
} from '@affine-test/kit/utils/sidebar';
|
} from '@affine-test/kit/utils/sidebar';
|
||||||
@@ -19,8 +18,7 @@ import { expect } from '@playwright/test';
|
|||||||
test('can open login modal in workspace list', async ({ page }) => {
|
test('can open login modal in workspace list', async ({ page }) => {
|
||||||
await openHomePage(page);
|
await openHomePage(page);
|
||||||
await waitForEditorLoad(page);
|
await waitForEditorLoad(page);
|
||||||
await clickSideBarCurrentWorkspaceBanner(page);
|
await page.getByTestId('sidebar-user-avatar').click({
|
||||||
await page.getByTestId('cloud-signin-button').click({
|
|
||||||
delay: 200,
|
delay: 200,
|
||||||
});
|
});
|
||||||
await expect(page.getByTestId('auth-modal')).toBeVisible();
|
await expect(page.getByTestId('auth-modal')).toBeVisible();
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ import {
|
|||||||
waitForAllPagesLoad,
|
waitForAllPagesLoad,
|
||||||
waitForEditorLoad,
|
waitForEditorLoad,
|
||||||
} from '@affine-test/kit/utils/page-logic';
|
} from '@affine-test/kit/utils/page-logic';
|
||||||
import {
|
import { clickSideBarSettingButton } from '@affine-test/kit/utils/sidebar';
|
||||||
clickSideBarCurrentWorkspaceBanner,
|
|
||||||
clickSideBarSettingButton,
|
|
||||||
} from '@affine-test/kit/utils/sidebar';
|
|
||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
import { hash } from '@node-rs/argon2';
|
import { hash } from '@node-rs/argon2';
|
||||||
import type { BrowserContext, Cookie, Page } from '@playwright/test';
|
import type { BrowserContext, Cookie, Page } from '@playwright/test';
|
||||||
@@ -239,8 +236,7 @@ export async function loginUser(
|
|||||||
await waitForEditorLoad(page);
|
await waitForEditorLoad(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
await clickSideBarCurrentWorkspaceBanner(page);
|
await page.getByTestId('sidebar-user-avatar').click({
|
||||||
await page.getByTestId('cloud-signin-button').click({
|
|
||||||
delay: 200,
|
delay: 200,
|
||||||
});
|
});
|
||||||
await loginUserDirectly(page, user, config);
|
await loginUserDirectly(page, user, config);
|
||||||
|
|||||||
Reference in New Issue
Block a user