mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-23 09:22:38 +03:00
feat(core): adapt storage progress to payment system (#4713)
This commit is contained in:
parent
eaa90c9fb6
commit
df69c908fe
@ -40,6 +40,7 @@
|
||||
"@toeverything/infra": "workspace:*",
|
||||
"@toeverything/theme": "^0.7.20",
|
||||
"@vanilla-extract/dynamic": "^2.0.3",
|
||||
"bytes": "^3.1.2",
|
||||
"check-password-strength": "^2.0.7",
|
||||
"clsx": "^2.0.0",
|
||||
"dayjs": "^1.11.10",
|
||||
@ -71,6 +72,7 @@
|
||||
"@storybook/jest": "^0.2.3",
|
||||
"@storybook/testing-library": "^0.2.2",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@types/bytes": "^3.1.3",
|
||||
"@types/react": "^18.2.28",
|
||||
"@types/react-datepicker": "^4.19.0",
|
||||
"@types/react-dnd": "^3.0.2",
|
||||
|
@ -115,31 +115,19 @@ globalStyle(`${storageProgressWrapper} .storage-progress-desc`, {
|
||||
globalStyle(`${storageProgressWrapper} .storage-progress-bar-wrapper`, {
|
||||
height: '8px',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: 'var(--affine-pure-black-10)',
|
||||
backgroundColor: 'var(--affine-black-10)',
|
||||
overflow: 'hidden',
|
||||
});
|
||||
export const storageProgressBar = style({
|
||||
height: '100%',
|
||||
backgroundColor: 'var(--affine-processing-color)',
|
||||
selectors: {
|
||||
'&.warning': {
|
||||
// Wait for design
|
||||
backgroundColor: '#FF7C09',
|
||||
},
|
||||
'&.danger': {
|
||||
backgroundColor: 'var(--affine-error-color)',
|
||||
},
|
||||
},
|
||||
});
|
||||
export const storageExtendHint = style({
|
||||
borderRadius: '4px',
|
||||
padding: '4px 8px',
|
||||
backgroundColor: 'var(--affine-background-secondary-color)',
|
||||
color: 'var(--affine-text-secondary-color)',
|
||||
fontSize: 'var(--affine-font-xs)',
|
||||
lineHeight: '20px',
|
||||
marginTop: 8,
|
||||
});
|
||||
globalStyle(`${storageExtendHint} a`, {
|
||||
color: 'var(--affine-link-color)',
|
||||
|
||||
export const storageButton = style({
|
||||
padding: '4px 12px',
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { Button } from '@toeverything/components/button';
|
||||
import { Tooltip } from '@toeverything/components/tooltip';
|
||||
import bytes from 'bytes';
|
||||
import clsx from 'clsx';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
@ -10,20 +11,14 @@ export interface StorageProgressProgress {
|
||||
max: number;
|
||||
value: number;
|
||||
onUpgrade: () => void;
|
||||
plan: string;
|
||||
}
|
||||
|
||||
const transformBytesToMB = (bytes: number) => {
|
||||
return (bytes / 1024 / 1024).toFixed(2);
|
||||
};
|
||||
|
||||
const transformBytesToGB = (bytes: number) => {
|
||||
return (bytes / 1024 / 1024 / 1024).toFixed(2);
|
||||
};
|
||||
|
||||
export const StorageProgress = ({
|
||||
max: upperLimit,
|
||||
value,
|
||||
onUpgrade,
|
||||
plan,
|
||||
}: StorageProgressProgress) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const percent = useMemo(
|
||||
@ -31,51 +26,53 @@ export const StorageProgress = ({
|
||||
[upperLimit, value]
|
||||
);
|
||||
|
||||
const used = useMemo(() => transformBytesToMB(value), [value]);
|
||||
const max = useMemo(() => transformBytesToGB(upperLimit), [upperLimit]);
|
||||
const used = useMemo(() => bytes.format(value), [value]);
|
||||
const max = useMemo(() => bytes.format(upperLimit), [upperLimit]);
|
||||
|
||||
const buttonType = useMemo(() => {
|
||||
if (plan === 'Free') {
|
||||
return 'primary';
|
||||
}
|
||||
return 'default';
|
||||
}, [plan]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.storageProgressContainer}>
|
||||
<div className={styles.storageProgressWrapper}>
|
||||
<div className="storage-progress-desc">
|
||||
<span>{t['com.affine.storage.used.hint']()}</span>
|
||||
<span>
|
||||
{used}MB/{max}GB
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="storage-progress-bar-wrapper">
|
||||
<div
|
||||
className={clsx(styles.storageProgressBar, {
|
||||
warning: percent > 80,
|
||||
danger: percent > 99,
|
||||
})}
|
||||
style={{ width: `${percent}%` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tooltip content={t['com.affine.storage.disabled.hint']()}>
|
||||
<span tabIndex={0}>
|
||||
<Button disabled onClick={onUpgrade}>
|
||||
{t['com.affine.storage.upgrade']()}
|
||||
</Button>
|
||||
<div className={styles.storageProgressContainer}>
|
||||
<div className={styles.storageProgressWrapper}>
|
||||
<div className="storage-progress-desc">
|
||||
<span>{t['com.affine.storage.used.hint']()}</span>
|
||||
<span>
|
||||
{used}/{max}
|
||||
{` (${plan} Plan)`}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{percent > 80 ? (
|
||||
<div className={styles.storageExtendHint}>
|
||||
{t['com.affine.storage.extend.hint']()}
|
||||
<a
|
||||
href="https://community.affine.pro/c/insider-general/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{t['com.affine.storage.extend.link']()}
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
|
||||
<div className="storage-progress-bar-wrapper">
|
||||
<div
|
||||
className={clsx(styles.storageProgressBar, {
|
||||
danger: percent > 80,
|
||||
})}
|
||||
style={{ width: `${percent > 100 ? '100' : percent}%` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tooltip
|
||||
options={{ hidden: percent < 100 }}
|
||||
content={
|
||||
'You have reached the maximum capacity limit for your current account'
|
||||
}
|
||||
>
|
||||
<span tabIndex={0}>
|
||||
<Button
|
||||
type={buttonType}
|
||||
onClick={onUpgrade}
|
||||
className={styles.storageButton}
|
||||
>
|
||||
{plan === 'Free' ? 'Upgrade' : 'Change'}
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -43,6 +43,7 @@
|
||||
"@react-hookz/web": "^23.1.0",
|
||||
"@toeverything/components": "^0.0.45",
|
||||
"async-call-rpc": "^6.3.1",
|
||||
"bytes": "^3.1.2",
|
||||
"css-spring": "^4.1.0",
|
||||
"cssnano": "^6.0.1",
|
||||
"graphql": "^16.8.1",
|
||||
@ -75,6 +76,7 @@
|
||||
"@sentry/webpack-plugin": "^2.8.0",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@swc/core": "^1.3.93",
|
||||
"@types/bytes": "^3.1.3",
|
||||
"@types/lodash-es": "^4.17.9",
|
||||
"@types/webpack-env": "^1.18.2",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
import {
|
||||
allBlobSizesQuery,
|
||||
removeAvatarMutation,
|
||||
SubscriptionPlan,
|
||||
uploadAvatarMutation,
|
||||
} from '@affine/graphql';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
@ -14,17 +15,24 @@ import { useMutation, useQuery } from '@affine/workspace/affine/gql';
|
||||
import { ArrowRightSmallIcon, CameraIcon } from '@blocksuite/icons';
|
||||
import { Avatar } from '@toeverything/components/avatar';
|
||||
import { Button } from '@toeverything/components/button';
|
||||
import bytes from 'bytes';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import {
|
||||
type FC,
|
||||
type MouseEvent,
|
||||
Suspense,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import { authAtom, openSignOutModalAtom } from '../../../../atoms';
|
||||
import {
|
||||
authAtom,
|
||||
openSettingModalAtom,
|
||||
openSignOutModalAtom,
|
||||
} from '../../../../atoms';
|
||||
import { useCurrentUser } from '../../../../hooks/affine/use-current-user';
|
||||
import { useUserSubscription } from '../../../../hooks/use-subscription';
|
||||
import { Upload } from '../../../pure/file-upload';
|
||||
import * as style from './style.css';
|
||||
|
||||
@ -124,6 +132,7 @@ export const AvatarAndName = () => {
|
||||
<Button
|
||||
data-testid="save-user-name"
|
||||
onClick={handleUpdateUserName}
|
||||
className={style.button}
|
||||
style={{
|
||||
marginLeft: '12px',
|
||||
}}
|
||||
@ -146,7 +155,20 @@ const StoragePanel = () => {
|
||||
query: allBlobSizesQuery,
|
||||
});
|
||||
|
||||
const onUpgrade = useCallback(() => {}, []);
|
||||
const [subscription] = useUserSubscription();
|
||||
const plan = subscription?.plan ?? SubscriptionPlan.Free;
|
||||
const maxLimit = useMemo(() => {
|
||||
return bytes.parse(plan === SubscriptionPlan.Free ? '10GB' : '100GB');
|
||||
}, [plan]);
|
||||
|
||||
const setSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||
const onUpgrade = useCallback(() => {
|
||||
setSettingModalAtom({
|
||||
open: true,
|
||||
activeTab: 'plans',
|
||||
workspaceId: null,
|
||||
});
|
||||
}, [setSettingModalAtom]);
|
||||
|
||||
return (
|
||||
<SettingRow
|
||||
@ -155,7 +177,8 @@ const StoragePanel = () => {
|
||||
spreadCol={false}
|
||||
>
|
||||
<StorageProgress
|
||||
max={10737418240}
|
||||
max={maxLimit}
|
||||
plan={plan}
|
||||
value={data.collectAllBlobSizes.size}
|
||||
onUpgrade={onUpgrade}
|
||||
/>
|
||||
@ -200,7 +223,7 @@ export const AccountSetting: FC = () => {
|
||||
/>
|
||||
<AvatarAndName />
|
||||
<SettingRow name={t['com.affine.settings.email']()} desc={user.email}>
|
||||
<Button onClick={onChangeEmail}>
|
||||
<Button onClick={onChangeEmail} className={style.button}>
|
||||
{t['com.affine.settings.email.action']()}
|
||||
</Button>
|
||||
</SettingRow>
|
||||
@ -208,7 +231,7 @@ export const AccountSetting: FC = () => {
|
||||
name={t['com.affine.settings.password']()}
|
||||
desc={t['com.affine.settings.password.message']()}
|
||||
>
|
||||
<Button onClick={onPasswordButtonClick}>
|
||||
<Button onClick={onPasswordButtonClick} className={style.button}>
|
||||
{user.hasPassword
|
||||
? t['com.affine.settings.password.action.change']()
|
||||
: t['com.affine.settings.password.action.set']()}
|
||||
|
@ -39,3 +39,7 @@ globalStyle(`${avatarWrapper} .camera-icon-wrapper`, {
|
||||
color: 'var(--affine-white)',
|
||||
fontSize: 'var(--affine-font-h-4)',
|
||||
});
|
||||
|
||||
export const button = style({
|
||||
padding: '4px 12px',
|
||||
});
|
||||
|
13
yarn.lock
13
yarn.lock
@ -223,12 +223,14 @@ __metadata:
|
||||
"@toeverything/hooks": "workspace:*"
|
||||
"@toeverything/infra": "workspace:*"
|
||||
"@toeverything/theme": "npm:^0.7.20"
|
||||
"@types/bytes": "npm:^3.1.3"
|
||||
"@types/react": "npm:^18.2.28"
|
||||
"@types/react-datepicker": "npm:^4.19.0"
|
||||
"@types/react-dnd": "npm:^3.0.2"
|
||||
"@types/react-dom": "npm:^18.2.13"
|
||||
"@vanilla-extract/css": "npm:^1.13.0"
|
||||
"@vanilla-extract/dynamic": "npm:^2.0.3"
|
||||
bytes: "npm:^3.1.2"
|
||||
check-password-strength: "npm:^2.0.7"
|
||||
clsx: "npm:^2.0.0"
|
||||
dayjs: "npm:^1.11.10"
|
||||
@ -327,9 +329,11 @@ __metadata:
|
||||
"@svgr/webpack": "npm:^8.1.0"
|
||||
"@swc/core": "npm:^1.3.93"
|
||||
"@toeverything/components": "npm:^0.0.45"
|
||||
"@types/bytes": "npm:^3.1.3"
|
||||
"@types/lodash-es": "npm:^4.17.9"
|
||||
"@types/webpack-env": "npm:^1.18.2"
|
||||
async-call-rpc: "npm:^6.3.1"
|
||||
bytes: "npm:^3.1.2"
|
||||
copy-webpack-plugin: "npm:^11.0.0"
|
||||
css-loader: "npm:^6.8.1"
|
||||
css-spring: "npm:^4.1.0"
|
||||
@ -12856,6 +12860,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/bytes@npm:^3.1.3":
|
||||
version: 3.1.3
|
||||
resolution: "@types/bytes@npm:3.1.3"
|
||||
checksum: c636b74d5a6f4925f1030382d8afcfe271e329da7c8103d175a874688118b60b0b1523e23477554e29456d024e5a180df1ba99d88c49b3a51433b714acffdac9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/cacheable-request@npm:^6.0.1":
|
||||
version: 6.0.3
|
||||
resolution: "@types/cacheable-request@npm:6.0.3"
|
||||
@ -16310,7 +16321,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bytes@npm:3.1.2":
|
||||
"bytes@npm:3.1.2, bytes@npm:^3.1.2":
|
||||
version: 3.1.2
|
||||
resolution: "bytes@npm:3.1.2"
|
||||
checksum: a10abf2ba70c784471d6b4f58778c0beeb2b5d405148e66affa91f23a9f13d07603d0a0354667310ae1d6dc141474ffd44e2a074be0f6e2254edb8fc21445388
|
||||
|
Loading…
Reference in New Issue
Block a user