fix(core): fix create workspace force cloud (#9382)

This commit is contained in:
EYHN
2024-12-27 13:33:05 +00:00
parent cbfe38b189
commit cff3a73db4
9 changed files with 165 additions and 130 deletions
@@ -1,12 +1,8 @@
import { Divider } from '@affine/component/ui/divider';
import { MenuItem } from '@affine/component/ui/menu'; import { MenuItem } from '@affine/component/ui/menu';
import { AuthService } from '@affine/core/modules/cloud'; import { AuthService } from '@affine/core/modules/cloud';
import { GlobalDialogService } from '@affine/core/modules/dialogs'; import { GlobalDialogService } from '@affine/core/modules/dialogs';
import { FeatureFlagService } from '@affine/core/modules/feature-flag'; import { FeatureFlagService } from '@affine/core/modules/feature-flag';
import { import { type WorkspaceMetadata } from '@affine/core/modules/workspace';
type WorkspaceMetadata,
WorkspacesService,
} from '@affine/core/modules/workspace';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
import { Logo1Icon } from '@blocksuite/icons/rc'; import { Logo1Icon } from '@blocksuite/icons/rc';
@@ -119,9 +115,6 @@ const UserWithWorkspaceListInner = ({
globalDialogService.open('sign-in', { step: 'addSelfhosted' }); globalDialogService.open('sign-in', { step: 'addSelfhosted' });
}, [globalDialogService]); }, [globalDialogService]);
const workspaceManager = useService(WorkspacesService);
const workspaces = useLiveData(workspaceManager.list.workspaces$);
return ( return (
<div className={styles.workspaceListWrapper}> <div className={styles.workspaceListWrapper}>
<AFFiNEWorkspaceList <AFFiNEWorkspaceList
@@ -130,7 +123,6 @@ const UserWithWorkspaceListInner = ({
showEnableCloudButton={showEnableCloudButton} showEnableCloudButton={showEnableCloudButton}
showSettingsButton={showSettingsButton} showSettingsButton={showSettingsButton}
/> />
{workspaces.length > 0 ? <Divider size="thinner" /> : null}
<AddWorkspace <AddWorkspace
onAddWorkspace={onAddWorkspace} onAddWorkspace={onAddWorkspace}
onNewWorkspace={onNewWorkspace} onNewWorkspace={onNewWorkspace}
@@ -138,17 +138,17 @@ const CloudWorkSpaceList = ({
items={[ items={[
server.id !== 'affine-cloud' && ( server.id !== 'affine-cloud' && (
<MenuItem key="delete-server" onClick={handleDeleteServer}> <MenuItem key="delete-server" onClick={handleDeleteServer}>
Delete Server {t['com.affine.server.delete']()}
</MenuItem> </MenuItem>
), ),
accountStatus === 'authenticated' && ( accountStatus === 'authenticated' && (
<MenuItem key="sign-out" onClick={handleSignOut}> <MenuItem key="sign-out" onClick={handleSignOut}>
Sign Out {t['com.affine.sign.out']()}
</MenuItem> </MenuItem>
), ),
accountStatus === 'unauthenticated' && ( accountStatus === 'unauthenticated' && (
<MenuItem key="sign-in" onClick={handleSignIn}> <MenuItem key="sign-in" onClick={handleSignIn}>
Sign In {t['com.affine.sign.in']()}
</MenuItem> </MenuItem>
), ),
]} ]}
@@ -206,6 +206,7 @@ const LocalWorkspaces = ({
onSettingClick={onClickWorkspaceSetting} onSettingClick={onClickWorkspaceSetting}
onEnableCloudClick={onClickEnableCloud} onEnableCloudClick={onClickEnableCloud}
/> />
<Divider size="thinner" />
</div> </div>
); );
}; };
@@ -1,8 +1,8 @@
import { Avatar, ConfirmModal, Input, Switch } from '@affine/component'; import { Avatar, ConfirmModal, Input, notify, Switch } from '@affine/component';
import type { ConfirmModalProps } from '@affine/component/ui/modal'; import type { ConfirmModalProps } from '@affine/component/ui/modal';
import { CloudSvg } from '@affine/core/components/affine/share-page-modal/cloud-svg'; import { CloudSvg } from '@affine/core/components/affine/share-page-modal/cloud-svg';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { AuthService } from '@affine/core/modules/cloud'; import { AuthService, ServersService } from '@affine/core/modules/cloud';
import { import {
type DialogComponentProps, type DialogComponentProps,
type GLOBAL_DIALOG_SCHEMA, type GLOBAL_DIALOG_SCHEMA,
@@ -12,7 +12,7 @@ import { FeatureFlagService } from '@affine/core/modules/feature-flag';
import { WorkspacesService } from '@affine/core/modules/workspace'; import { WorkspacesService } from '@affine/core/modules/workspace';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
import { useLiveData, useService } from '@toeverything/infra'; import { FrameworkScope, useLiveData, useService } from '@toeverything/infra';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { buildShowcaseWorkspace } from '../../../utils/first-app-data'; import { buildShowcaseWorkspace } from '../../../utils/first-app-data';
@@ -60,8 +60,18 @@ const NameWorkspaceContent = ({
); );
const handleCreateWorkspace = useCallback(() => { const handleCreateWorkspace = useCallback(() => {
if (loginStatus !== 'authenticated' && enable) {
return openSignInModal();
}
onConfirmName(workspaceName, enable ? serverId || 'affine-cloud' : 'local'); onConfirmName(workspaceName, enable ? serverId || 'affine-cloud' : 'local');
}, [enable, onConfirmName, serverId, workspaceName]); }, [
enable,
loginStatus,
onConfirmName,
openSignInModal,
serverId,
workspaceName,
]);
const onEnter = useCallback(() => { const onEnter = useCallback(() => {
if (workspaceName) { if (workspaceName) {
@@ -109,37 +119,41 @@ const NameWorkspaceContent = ({
size="large" size="large"
/> />
</div> </div>
<div className={styles.affineCloudWrapper}> {!serverId || serverId === 'affine-cloud' ? (
<div className={styles.subTitle}>{t['AFFiNE Cloud']()}</div> <div className={styles.affineCloudWrapper}>
<div className={styles.card}> <div className={styles.subTitle}>{t['AFFiNE Cloud']()}</div>
<div className={styles.cardText}> <div className={styles.card}>
<div className={styles.cardTitle}> <div className={styles.cardText}>
<span>{t['com.affine.nameWorkspace.affine-cloud.title']()}</span> <div className={styles.cardTitle}>
<Switch <span>
checked={enable} {t['com.affine.nameWorkspace.affine-cloud.title']()}
onChange={onSwitchChange} </span>
disabled={forcedCloud} <Switch
/> checked={enable}
onChange={onSwitchChange}
disabled={forcedCloud}
/>
</div>
<div className={styles.cardDescription}>
{t['com.affine.nameWorkspace.affine-cloud.description']()}
</div>
</div> </div>
<div className={styles.cardDescription}> <div className={styles.cloudSvgContainer}>
{t['com.affine.nameWorkspace.affine-cloud.description']()} <CloudSvg />
</div> </div>
</div> </div>
<div className={styles.cloudSvgContainer}> {forcedCloud && BUILD_CONFIG.isWeb ? (
<CloudSvg /> <a
</div> className={styles.cloudTips}
href={BUILD_CONFIG.downloadUrl}
target="_blank"
rel="noreferrer"
>
{t['com.affine.nameWorkspace.affine-cloud.web-tips']()}
</a>
) : null}
</div> </div>
{forcedCloud ? ( ) : null}
<a
className={styles.cloudTips}
href={BUILD_CONFIG.downloadUrl}
target="_blank"
rel="noreferrer"
>
{t['com.affine.nameWorkspace.affine-cloud.web-tips']()}
</a>
) : null}
</div>
</ConfirmModal> </ConfirmModal>
); );
}; };
@@ -150,10 +164,14 @@ export const CreateWorkspaceDialog = ({
close, close,
}: DialogComponentProps<GLOBAL_DIALOG_SCHEMA['create-workspace']>) => { }: DialogComponentProps<GLOBAL_DIALOG_SCHEMA['create-workspace']>) => {
const workspacesService = useService(WorkspacesService); const workspacesService = useService(WorkspacesService);
const serversService = useService(ServersService);
const featureFlagService = useService(FeatureFlagService); const featureFlagService = useService(FeatureFlagService);
const enableLocalWorkspace = useLiveData( const enableLocalWorkspace = useLiveData(
featureFlagService.flags.enable_local_workspace.$ featureFlagService.flags.enable_local_workspace.$
); );
const server = useLiveData(
serverId ? serversService.server$(serverId) : null
);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const onConfirmName = useAsyncCallback( const onConfirmName = useAsyncCallback(
@@ -164,14 +182,22 @@ export const CreateWorkspaceDialog = ({
// this will be the last step for web for now // this will be the last step for web for now
// fix me later // fix me later
const { meta, defaultDocId } = await buildShowcaseWorkspace( try {
workspacesService, const { meta, defaultDocId } = await buildShowcaseWorkspace(
workspaceFlavour, workspacesService,
name workspaceFlavour,
); name
);
close({ metadata: meta, defaultDocId }); close({ metadata: meta, defaultDocId });
setLoading(false); } catch (e) {
console.error(e);
notify.error({
title: 'Failed to create workspace',
message: 'please try again later.',
});
} finally {
setLoading(false);
}
}, },
[loading, workspacesService, close] [loading, workspacesService, close]
); );
@@ -186,13 +212,15 @@ export const CreateWorkspaceDialog = ({
); );
return ( return (
<NameWorkspaceContent <FrameworkScope scope={server?.scope}>
loading={loading} <NameWorkspaceContent
open loading={loading}
serverId={serverId} serverId={serverId}
forcedCloud={forcedCloud || !enableLocalWorkspace} open
onOpenChange={onOpenChange} forcedCloud={forcedCloud || !enableLocalWorkspace}
onConfirmName={onConfirmName} onOpenChange={onOpenChange}
/> onConfirmName={onConfirmName}
/>
</FrameworkScope>
); );
}; };
@@ -2,16 +2,21 @@ import { Loading, Scrollable } from '@affine/component';
import { WorkspaceDetailSkeleton } from '@affine/component/setting-components'; import { WorkspaceDetailSkeleton } from '@affine/component/setting-components';
import type { ModalProps } from '@affine/component/ui/modal'; import type { ModalProps } from '@affine/component/ui/modal';
import { Modal } from '@affine/component/ui/modal'; import { Modal } from '@affine/component/ui/modal';
import { AuthService } from '@affine/core/modules/cloud'; import {
AuthService,
DefaultServerService,
ServersService,
} from '@affine/core/modules/cloud';
import type { import type {
DialogComponentProps, DialogComponentProps,
GLOBAL_DIALOG_SCHEMA, GLOBAL_DIALOG_SCHEMA,
} from '@affine/core/modules/dialogs'; } from '@affine/core/modules/dialogs';
import type { SettingTab } from '@affine/core/modules/dialogs/constant'; import type { SettingTab } from '@affine/core/modules/dialogs/constant';
import { GlobalContextService } from '@affine/core/modules/global-context';
import type { WorkspaceMetadata } from '@affine/core/modules/workspace'; import type { WorkspaceMetadata } from '@affine/core/modules/workspace';
import { Trans } from '@affine/i18n'; import { Trans } from '@affine/i18n';
import { ContactWithUsIcon } from '@blocksuite/icons/rc'; import { ContactWithUsIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra'; import { FrameworkScope, useLiveData, useService } from '@toeverything/infra';
import { debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
import { import {
Suspense, Suspense,
@@ -57,7 +62,21 @@ const SettingModalInner = ({
activeWorkspaceMetadata: initialWorkspaceMetadata, activeWorkspaceMetadata: initialWorkspaceMetadata,
scrollAnchor: undefined, scrollAnchor: undefined,
}); });
const loginStatus = useLiveData(useService(AuthService).session.status$); const globalContextService = useService(GlobalContextService);
const currentServerId = useLiveData(
globalContextService.globalContext.serverId.$
);
console.log(currentServerId);
const serversService = useService(ServersService);
const defaultServerService = useService(DefaultServerService);
const currentServer =
useLiveData(
currentServerId ? serversService.server$(currentServerId) : null
) ?? defaultServerService.server;
const loginStatus = useLiveData(
currentServer.scope.get(AuthService).session.status$
);
const modalContentRef = useRef<HTMLDivElement>(null); const modalContentRef = useRef<HTMLDivElement>(null);
const modalContentWrapperRef = useRef<HTMLDivElement>(null); const modalContentWrapperRef = useRef<HTMLDivElement>(null);
@@ -120,7 +139,7 @@ const SettingModalInner = ({
}, [setOpenStarAFFiNEModal]); }, [setOpenStarAFFiNEModal]);
return ( return (
<> <FrameworkScope scope={currentServer.scope}>
<SettingSidebar <SettingSidebar
activeTab={settingState.activeTab} activeTab={settingState.activeTab}
onTabChange={onTabChange} onTabChange={onTabChange}
@@ -188,7 +207,7 @@ const SettingModalInner = ({
<Scrollable.Scrollbar /> <Scrollable.Scrollbar />
</Scrollable.Viewport> </Scrollable.Viewport>
</Scrollable.Root> </Scrollable.Root>
</> </FrameworkScope>
); );
}; };
@@ -5,6 +5,7 @@ import {
} from '@affine/component/setting-components'; } from '@affine/component/setting-components';
import { useWorkspace } from '@affine/core/components/hooks/use-workspace'; import { useWorkspace } from '@affine/core/components/hooks/use-workspace';
import { useWorkspaceInfo } from '@affine/core/components/hooks/use-workspace-info'; import { useWorkspaceInfo } from '@affine/core/components/hooks/use-workspace-info';
import { WorkspaceServerService } from '@affine/core/modules/cloud';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant'; import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { ArrowRightSmallIcon } from '@blocksuite/icons/rc'; import { ArrowRightSmallIcon } from '@blocksuite/icons/rc';
@@ -31,6 +32,7 @@ export const WorkspaceSettingDetail = ({
// useWorkspace hook is a vary heavy operation here, but we need syncing name and avatar changes here, // useWorkspace hook is a vary heavy operation here, but we need syncing name and avatar changes here,
// we don't have a better way to do this now // we don't have a better way to do this now
const workspace = useWorkspace(workspaceMetadata); const workspace = useWorkspace(workspaceMetadata);
const server = workspace?.scope.get(WorkspaceServerService).server;
const workspaceInfo = useWorkspaceInfo(workspaceMetadata); const workspaceInfo = useWorkspaceInfo(workspaceMetadata);
@@ -50,53 +52,55 @@ export const WorkspaceSettingDetail = ({
} }
return ( return (
<FrameworkScope scope={workspace.scope}> <FrameworkScope scope={server?.scope}>
<SettingHeader <FrameworkScope scope={workspace.scope}>
title={t[`Workspace Settings with name`]({ <SettingHeader
name: workspaceInfo?.name ?? UNTITLED_WORKSPACE_NAME, title={t[`Workspace Settings with name`]({
})} name: workspaceInfo?.name ?? UNTITLED_WORKSPACE_NAME,
subtitle={t['com.affine.settings.workspace.description']()} })}
/> subtitle={t['com.affine.settings.workspace.description']()}
<SettingWrapper title={t['Info']()}> />
<SettingRow <SettingWrapper title={t['Info']()}>
name={t['Workspace Profile']()} <SettingRow
desc={t['com.affine.settings.workspace.not-owner']()} name={t['Workspace Profile']()}
spreadCol={false} desc={t['com.affine.settings.workspace.not-owner']()}
> spreadCol={false}
<ProfilePanel /> >
<LabelsPanel /> <ProfilePanel />
</SettingRow> <LabelsPanel />
</SettingWrapper> </SettingRow>
<SettingWrapper title={t['com.affine.brand.affineCloud']()}>
<EnableCloudPanel onCloseSetting={onCloseSetting} />
<WorkspaceQuotaPanel />
<MembersPanel onChangeSettingState={onChangeSettingState} />
</SettingWrapper>
<SharingPanel />
{BUILD_CONFIG.isElectron && (
<SettingWrapper title={t['Storage and Export']()}>
<DesktopExportPanel
workspace={workspace}
workspaceMetadata={workspaceMetadata}
/>
</SettingWrapper> </SettingWrapper>
)} <SettingWrapper title={t['com.affine.brand.affineCloud']()}>
<SettingWrapper> <EnableCloudPanel onCloseSetting={onCloseSetting} />
<DeleteLeaveWorkspace onCloseSetting={onCloseSetting} /> <WorkspaceQuotaPanel />
<SettingRow <MembersPanel onChangeSettingState={onChangeSettingState} />
name={ </SettingWrapper>
<span style={{ color: 'var(--affine-text-secondary-color)' }}> <SharingPanel />
{t['com.affine.resetSyncStatus.button']()} {BUILD_CONFIG.isElectron && (
</span> <SettingWrapper title={t['Storage and Export']()}>
} <DesktopExportPanel
desc={t['com.affine.resetSyncStatus.description']()} workspace={workspace}
style={{ cursor: 'pointer' }} workspaceMetadata={workspaceMetadata}
onClick={handleResetSyncStatus} />
data-testid="reset-sync-status" </SettingWrapper>
> )}
<ArrowRightSmallIcon /> <SettingWrapper>
</SettingRow> <DeleteLeaveWorkspace onCloseSetting={onCloseSetting} />
</SettingWrapper> <SettingRow
name={
<span style={{ color: 'var(--affine-text-secondary-color)' }}>
{t['com.affine.resetSyncStatus.button']()}
</span>
}
desc={t['com.affine.resetSyncStatus.description']()}
style={{ cursor: 'pointer' }}
onClick={handleResetSyncStatus}
data-testid="reset-sync-status"
>
<ArrowRightSmallIcon />
</SettingRow>
</SettingWrapper>
</FrameworkScope>
</FrameworkScope> </FrameworkScope>
); );
}; };
@@ -1,6 +1,5 @@
import { NotificationCenter } from '@affine/component'; import { NotificationCenter } from '@affine/component';
import { DefaultServerService } from '@affine/core/modules/cloud'; import { DefaultServerService } from '@affine/core/modules/cloud';
import { GlobalContextService } from '@affine/core/modules/global-context';
import { FrameworkScope, useService } from '@toeverything/infra'; import { FrameworkScope, useService } from '@toeverything/infra';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Outlet } from 'react-router-dom'; import { Outlet } from 'react-router-dom';
@@ -11,7 +10,6 @@ import { FindInPageModal } from './find-in-page/find-in-page-modal';
export const RootWrapper = () => { export const RootWrapper = () => {
const defaultServerService = useService(DefaultServerService); const defaultServerService = useService(DefaultServerService);
const globalContextService = useService(GlobalContextService);
const [isServerReady, setIsServerReady] = useState(false); const [isServerReady, setIsServerReady] = useState(false);
useEffect(() => { useEffect(() => {
@@ -32,15 +30,6 @@ export const RootWrapper = () => {
}; };
}, [defaultServerService, isServerReady]); }, [defaultServerService, isServerReady]);
useEffect(() => {
globalContextService.globalContext.serverId.set(
defaultServerService.server.id
);
return () => {
globalContextService.globalContext.serverId.set(null);
};
}, [defaultServerService, globalContextService]);
return ( return (
<FrameworkScope scope={defaultServerService.server.scope}> <FrameworkScope scope={defaultServerService.server.scope}>
<GlobalDialogs /> <GlobalDialogs />
@@ -222,8 +222,8 @@ export const AFFINE_FLAGS = {
'com.affine.settings.workspace.experimental-features.enable-multiple-cloud-servers.name', 'com.affine.settings.workspace.experimental-features.enable-multiple-cloud-servers.name',
description: description:
'com.affine.settings.workspace.experimental-features.enable-multiple-cloud-servers.description', 'com.affine.settings.workspace.experimental-features.enable-multiple-cloud-servers.description',
configurable: isDesktopEnvironment, configurable: false,
defaultState: false, defaultState: isDesktopEnvironment,
}, },
enable_mobile_edgeless_editing: { enable_mobile_edgeless_editing: {
category: 'affine', category: 'affine',
@@ -7,7 +7,7 @@ export class GlobalContext extends Entity {
workspaceId = this.define<string>('workspaceId'); workspaceId = this.define<string>('workspaceId');
workspaceFlavour = this.define<string>('workspaceFlavour'); workspaceFlavour = this.define<string>('workspaceFlavour');
serverId = this.define<string>('serverId'); serverId = this.define<string>('serverId', 'affine-cloud');
/** /**
* is in doc page * is in doc page
@@ -39,12 +39,14 @@ export class GlobalContext extends Entity {
*/ */
isAllDocs = this.define<boolean>('isAllDocs'); isAllDocs = this.define<boolean>('isAllDocs');
define<T>(key: string) { define<T>(key: string, defaultValue: T | null = null) {
this.memento.set(key, null); this.memento.set(key, defaultValue);
const livedata$ = LiveData.from(this.memento.watch<T>(key), null); const livedata$ = LiveData.from(this.memento.watch<T>(key), defaultValue);
return { return {
get: () => this.memento.get(key) as T | null, get: () => this.memento.get(key) as T | null,
set: (value: T | null) => this.memento.set(key, value), set: (value: T | null) => {
this.memento.set(key, value);
},
$: livedata$, $: livedata$,
}; };
} }
+2 -2
View File
@@ -848,7 +848,6 @@
"com.affine.payment.billing-type-form.title": "Tell us your use case", "com.affine.payment.billing-type-form.title": "Tell us your use case",
"com.affine.payment.blob-limit.description.local": "The maximum file upload size for local workspaces is {{quota}}.", "com.affine.payment.blob-limit.description.local": "The maximum file upload size for local workspaces is {{quota}}.",
"com.affine.payment.blob-limit.description.member": "The maximum file upload size for this joined workspace is {{quota}}. You can contact the owner of this workspace.", "com.affine.payment.blob-limit.description.member": "The maximum file upload size for this joined workspace is {{quota}}. You can contact the owner of this workspace.",
"com.affine.payment.blob-limit.description.owner": "The maximum file upload size for this workspace is {{quota}}. To proceed, you can:", "com.affine.payment.blob-limit.description.owner": "The maximum file upload size for this workspace is {{quota}}. To proceed, you can:",
"com.affine.payment.blob-limit.description.owner.tips-1": "Upgrade your account for larger file upload limits", "com.affine.payment.blob-limit.description.owner.tips-1": "Upgrade your account for larger file upload limits",
"com.affine.payment.blob-limit.description.owner.tips-2": "Upgrade the workspace plan to increase storage for all member", "com.affine.payment.blob-limit.description.owner.tips-2": "Upgrade the workspace plan to increase storage for all member",
@@ -1617,5 +1616,6 @@
"com.affine.payment.sync-paused.member.both.description": "This workspace has exceeded both storage and member limits, causing synchronization to pause. Please contact your workspace owner to address these limits and resume syncing.", "com.affine.payment.sync-paused.member.both.description": "This workspace has exceeded both storage and member limits, causing synchronization to pause. Please contact your workspace owner to address these limits and resume syncing.",
"com.affine.payment.sync-paused.member.storage.description": "This workspace has exceeded its storage limit and synchronization has been paused. Please contact your workspace owner to either reduce storage usage or upgrade the plan to resume syncing.", "com.affine.payment.sync-paused.member.storage.description": "This workspace has exceeded its storage limit and synchronization has been paused. Please contact your workspace owner to either reduce storage usage or upgrade the plan to resume syncing.",
"com.affine.payment.sync-paused.member.member.description": "This workspace has reached its maximum member capacity and synchronization has been paused. Please contact your workspace owner to either adjust team membership or upgrade the plan to resume syncing.", "com.affine.payment.sync-paused.member.member.description": "This workspace has reached its maximum member capacity and synchronization has been paused. Please contact your workspace owner to either adjust team membership or upgrade the plan to resume syncing.",
"com.affine.payment.sync-paused.member.member.confirm": "Got It" "com.affine.payment.sync-paused.member.member.confirm": "Got It",
"com.affine.server.delete": "Delete Server"
} }