fix(core): remove quota modal (#12586)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Removed Features**
  - Removed all quota-reached modal dialogs, including both cloud and local storage quota notifications.
  - Users will no longer see modal alerts when storage limits are reached in workspaces.
- **User Interface**
  - Quota-reached modals and related styles have been removed from workspace layouts on both desktop and mobile.
- **Other Changes**
  - Quota notification logic and related settings have been eliminated from the application.
  - Maximum blob size enforcement and related callbacks have been removed from blob management.
  - Localization entries related to file upload size limits and quota tips have been removed.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
EYHN
2025-05-28 08:04:22 +00:00
parent 274319dd6c
commit b847de4980
10 changed files with 3 additions and 274 deletions

View File

@@ -36,11 +36,6 @@ export class BlobFrontend {
async set(blob: BlobRecord) {
await this.waitForConnected();
if (blob.data.byteLength > this.maxBlobSize) {
for (const cb of this.onReachedMaxBlobSizeCallbacks) {
cb(blob.data.byteLength);
}
}
await using lock = await this.lock.lock('blob', blob.key);
await this.storage.set(blob);
await lock[Symbol.asyncDispose]();
@@ -77,20 +72,6 @@ export class BlobFrontend {
return this.sync.fullDownload(peerId, signal);
}
private maxBlobSize = 1024 * 1024 * 100; // 100MB
private readonly onReachedMaxBlobSizeCallbacks: Set<
(byteSize: number) => void
> = new Set();
setMaxBlobSize(max: number) {
this.maxBlobSize = max;
}
onReachedMaxBlobSize(cb: (byteSize: number) => void): () => void {
this.onReachedMaxBlobSizeCallbacks.add(cb);
return () => this.onReachedMaxBlobSizeCallbacks.delete(cb);
}
private waitForConnected(signal?: AbortSignal) {
return this.storage.connection.waitForConnected(signal);
}

View File

@@ -1,27 +0,0 @@
import { cssVar } from '@toeverything/theme';
import { cssVarV2 } from '@toeverything/theme/v2';
import { style } from '@vanilla-extract/css';
export const ulStyle = style({
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
gap: '8px',
marginTop: '12px',
});
export const liStyle = style({
display: 'flex',
flexDirection: 'row',
alignItems: 'start',
fontSize: cssVar('fontBase'),
});
export const prefixDot = style({
background: cssVarV2('icon/activated'),
width: '5px',
height: '5px',
borderRadius: '50%',
marginRight: '12px',
marginTop: '10px',
});

View File

@@ -1,123 +0,0 @@
import { ConfirmModal } from '@affine/component/ui/modal';
import { openQuotaModalAtom } from '@affine/core/components/atoms';
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
import { WorkspaceService } from '@affine/core/modules/workspace';
import { type I18nString, useI18n } from '@affine/i18n';
import { track } from '@affine/track';
import { useLiveData, useService } from '@toeverything/infra';
import { useAtom } from 'jotai';
import { useCallback, useEffect, useMemo } from 'react';
import { useAsyncCallback } from '../../hooks/affine-async-hooks';
import * as styles from './cloud-quota-modal.css';
export const CloudQuotaModal = () => {
const t = useI18n();
const currentWorkspace = useService(WorkspaceService).workspace;
const [open, setOpen] = useAtom(openQuotaModalAtom);
const workspaceQuotaService = useService(WorkspaceQuotaService);
useEffect(() => {
workspaceQuotaService.quota.revalidate();
}, [workspaceQuotaService]);
const workspaceQuota = useLiveData(workspaceQuotaService.quota.quota$);
const permissionService = useService(WorkspacePermissionService);
const isOwner = useLiveData(permissionService.permission.isOwner$);
useEffect(() => {
// revalidate permission
permissionService.permission.revalidate();
}, [permissionService]);
const workspaceDialogService = useService(WorkspaceDialogService);
const handleUpgradeConfirm = useCallback(() => {
workspaceDialogService.open('setting', {
activeTab: 'plans',
scrollAnchor: 'cloudPricingPlan',
});
track.$.paywall.storage.viewPlans();
setOpen(false);
}, [workspaceDialogService, setOpen]);
const description = useMemo(() => {
if (!workspaceQuota) {
return null;
}
if (isOwner) {
return (
<OwnerDescription quota={workspaceQuota.humanReadable.blobLimit} />
);
}
return t['com.affine.payment.blob-limit.description.member']({
quota: workspaceQuota.humanReadable.blobLimit,
});
}, [isOwner, workspaceQuota, t]);
const onAbortLargeBlob = useAsyncCallback(
async (byteSize: number) => {
// wait for quota revalidation
await workspaceQuotaService.quota.waitForRevalidation();
if (
byteSize > (workspaceQuotaService.quota.quota$.value?.blobLimit ?? 0)
) {
setOpen(true);
}
},
[setOpen, workspaceQuotaService]
);
useEffect(() => {
if (!workspaceQuota) {
return;
}
currentWorkspace.engine.blob.setMaxBlobSize(workspaceQuota.blobLimit);
const disposable =
currentWorkspace.engine.blob.onReachedMaxBlobSize(onAbortLargeBlob);
return () => {
disposable();
};
}, [currentWorkspace.engine.blob, onAbortLargeBlob, workspaceQuota]);
return (
<ConfirmModal
open={open}
title={t['com.affine.payment.blob-limit.title']()}
onOpenChange={setOpen}
description={description}
onConfirm={handleUpgradeConfirm}
confirmText={t['com.affine.payment.upgrade']()}
confirmButtonOptions={{
variant: 'primary',
}}
/>
);
};
const tips: I18nString[] = [
'com.affine.payment.blob-limit.description.owner.tips-1',
'com.affine.payment.blob-limit.description.owner.tips-2',
'com.affine.payment.blob-limit.description.owner.tips-3',
];
const OwnerDescription = ({ quota }: { quota: string }) => {
const t = useI18n();
return (
<div>
{t['com.affine.payment.blob-limit.description.owner']({
quota: quota,
})}
<ul className={styles.ulStyle}>
{tips.map((tip, index) => (
<li className={styles.liStyle} key={index}>
<div className={styles.prefixDot} />
{t.t(tip)}
</li>
))}
</ul>
</div>
);
};

View File

@@ -1,2 +0,0 @@
export * from './cloud-quota-modal';
export * from './local-quota-modal';

View File

@@ -1,45 +0,0 @@
import { ConfirmModal } from '@affine/component/ui/modal';
import { openQuotaModalAtom } from '@affine/core/components/atoms';
import { WorkspaceService } from '@affine/core/modules/workspace';
import { useI18n } from '@affine/i18n';
import { useService } from '@toeverything/infra';
import { useAtom } from 'jotai';
import { useCallback, useEffect } from 'react';
export const LocalQuotaModal = () => {
const t = useI18n();
const currentWorkspace = useService(WorkspaceService).workspace;
const [open, setOpen] = useAtom(openQuotaModalAtom);
const onConfirm = useCallback(() => {
setOpen(false);
}, [setOpen]);
useEffect(() => {
const disposable = currentWorkspace.engine.blob.onReachedMaxBlobSize(() => {
setOpen(true);
});
return () => {
disposable();
};
}, [currentWorkspace.engine.blob, setOpen]);
return (
<ConfirmModal
open={open}
title={t['com.affine.payment.blob-limit.title']()}
description={t['com.affine.payment.blob-limit.description.local']({
quota: '100MB',
})}
onOpenChange={setOpen}
cancelButtonOptions={{
hidden: true,
}}
onConfirm={onConfirm}
confirmText={t['Got it']()}
confirmButtonOptions={{
variant: 'primary',
}}
/>
);
};

View File

@@ -1,10 +1,5 @@
import { uniReactRoot } from '@affine/component';
// import { WorkspaceAIOnboarding } from '@affine/core/components/affine/ai-onboarding';
import { AiLoginRequiredModal } from '@affine/core/components/affine/auth/ai-login-required';
import {
CloudQuotaModal,
LocalQuotaModal,
} from '@affine/core/components/affine/quota-reached-modal';
import { useResponsiveSidebar } from '@affine/core/components/hooks/use-responsive-siedebar';
import { SWRConfigProvider } from '@affine/core/components/providers/swr-config-provider';
import { WorkspaceSideEffects } from '@affine/core/components/providers/workspace-side-effects';
@@ -29,13 +24,8 @@ export const WorkspaceLayout = function WorkspaceLayout({
{/* ---- some side-effect components ---- */}
{currentWorkspace?.flavour !== 'local' ? (
<>
<CloudQuotaModal />
<QuotaCheck workspaceMeta={currentWorkspace.meta} />
</>
) : (
<LocalQuotaModal />
)}
<QuotaCheck workspaceMeta={currentWorkspace.meta} />
) : null}
<AiLoginRequiredModal />
<WorkspaceSideEffects />
<PeekViewManagerModal />

View File

@@ -1,10 +1,6 @@
import { uniReactRoot } from '@affine/component';
import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary';
import { AiLoginRequiredModal } from '@affine/core/components/affine/auth/ai-login-required';
import {
CloudQuotaModal,
LocalQuotaModal,
} from '@affine/core/components/affine/quota-reached-modal';
import { SWRConfigProvider } from '@affine/core/components/providers/swr-config-provider';
import { WorkspaceSideEffects } from '@affine/core/components/providers/workspace-side-effects';
import {
@@ -146,11 +142,6 @@ export const WorkspaceLayout = ({
{/* ---- some side-effect components ---- */}
<PeekViewManagerModal />
{workspace?.flavour !== 'local' ? (
<CloudQuotaModal />
) : (
<LocalQuotaModal />
)}
<AiLoginRequiredModal />
<uniReactRoot.Root />
<WorkspaceSideEffects />

View File

@@ -14,7 +14,7 @@
"it-IT": 100,
"it": 1,
"ja": 100,
"ko": 53,
"ko": 54,
"pl": 100,
"pt-BR": 100,
"ru": 100,

View File

@@ -3622,36 +3622,6 @@ export function useAFFiNEI18N(): {
* `Tell us your use case`
*/
["com.affine.payment.billing-type-form.title"](): string;
/**
* `The maximum file upload size for local workspaces is {{quota}}.`
*/
["com.affine.payment.blob-limit.description.local"](options: {
readonly quota: string;
}): string;
/**
* `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"](options: {
readonly quota: string;
}): string;
/**
* `The maximum file upload size for this workspace is {{quota}}. To proceed, you can:`
*/
["com.affine.payment.blob-limit.description.owner"](options: {
readonly quota: string;
}): string;
/**
* `Upgrade your account for larger file upload limits`
*/
["com.affine.payment.blob-limit.description.owner.tips-1"](): string;
/**
* `Upgrade the workspace plan to increase storage for all member`
*/
["com.affine.payment.blob-limit.description.owner.tips-2"](): string;
/**
* `Compress your file and upload again`
*/
["com.affine.payment.blob-limit.description.owner.tips-3"](): string;
/**
* `You have reached the limit`
*/

View File

@@ -903,12 +903,6 @@
"com.affine.payment.billing-type-form.description": "Please tell us more about your use case, to make AFFiNE better.",
"com.affine.payment.billing-type-form.go": "Go",
"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.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.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-3": "Compress your file and upload again",
"com.affine.payment.blob-limit.title": "You have reached the limit",
"com.affine.payment.book-a-demo": "Book a demo",
"com.affine.payment.buy-pro": "Buy Pro",