feat: add storage panel in setting (#4069)

This commit is contained in:
Qi
2023-08-31 20:36:05 +08:00
committed by GitHub
parent 4ef1425299
commit 260c25acf3
10 changed files with 221 additions and 6 deletions
@@ -2,15 +2,16 @@ import { FlexWrapper, Input } from '@affine/component';
import {
SettingHeader,
SettingRow,
StorageProgress,
} from '@affine/component/setting-components';
import { UserAvatar } from '@affine/component/user-avatar';
import { uploadAvatarMutation } from '@affine/graphql';
import { allBlobSizesQuery, uploadAvatarMutation } from '@affine/graphql';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useMutation } from '@affine/workspace/affine/gql';
import { useMutation, useQuery } from '@affine/workspace/affine/gql';
import { ArrowRightSmallIcon, CameraIcon, DoneIcon } from '@blocksuite/icons';
import { Button, IconButton } from '@toeverything/components/button';
import { useAtom } from 'jotai/index';
import { type FC, useCallback, useState } from 'react';
import { useAtom } from 'jotai';
import { type FC, Suspense, useCallback, useState } from 'react';
import { authAtom } from '../../../../atoms';
import { useCurrentUser } from '../../../../hooks/affine/use-current-user';
@@ -109,6 +110,30 @@ export const AvatarAndName = () => {
);
};
const StoragePanel = () => {
const t = useAFFiNEI18N();
const { data } = useQuery({
query: allBlobSizesQuery,
});
const onUpgrade = useCallback(() => {}, []);
return (
<SettingRow
name={t['com.affine.storage.title']()}
desc=""
spreadCol={false}
>
<StorageProgress
max={10737418240}
value={data.collectAllBlobSizes.size}
onUpgrade={onUpgrade}
/>
</SettingRow>
);
};
export const AccountSetting: FC = () => {
const t = useAFFiNEI18N();
const user = useCurrentUser();
@@ -154,7 +179,9 @@ export const AccountSetting: FC = () => {
: t['com.affine.settings.password.action.set']()}
</Button>
</SettingRow>
<Suspense>
<StoragePanel />
</Suspense>
<SettingRow
name={t[`Sign out`]()}
desc={t['com.affine.setting.sign.out.message']()}
@@ -613,6 +613,17 @@ export class WorkspaceResolver {
return this.storage.blobsSize(workspaceId).then(size => ({ size }));
}
@Query(() => WorkspaceBlobSizes)
async collectAllBlobSizes(@CurrentUser() user: User) {
const workspaces = await this.workspaces(user);
const size = (
await Promise.all(workspaces.map(({ id }) => this.storage.blobsSize(id)))
).reduce((prev, curr) => prev + curr, 0);
return { size };
}
@Mutation(() => String)
async setBlob(
@CurrentUser() user: UserType,
+1
View File
@@ -154,6 +154,7 @@ type Query {
"""List blobs of workspace"""
listBlobs(workspaceId: String!): [String!]!
collectBlobSizes(workspaceId: String!): WorkspaceBlobSizes!
collectAllBlobSizes: WorkspaceBlobSizes!
"""Get current user"""
currentUser: UserType!
@@ -1,6 +1,7 @@
export { SettingModal, type SettingModalProps } from './modal';
export { SettingHeader } from './setting-header';
export { SettingRow } from './setting-row';
export * from './storage-progess';
export * from './workspace-detail-skeleton';
export * from './workspace-list-skeleton';
export { SettingWrapper } from './wrapper';
@@ -92,3 +92,55 @@ globalStyle(`${settingRow} .right-col`, {
paddingLeft: '15px',
flexShrink: 0,
});
export const storageProgressContainer = style({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
});
export const storageProgressWrapper = style({
flexGrow: 1,
marginRight: '20px',
});
globalStyle(`${storageProgressWrapper} .storage-progress-desc`, {
fontSize: 'var(--affine-font-xs)',
color: 'var(--affine-text-secondary-color)',
height: '20px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 2,
});
globalStyle(`${storageProgressWrapper} .storage-progress-bar-wrapper`, {
height: '8px',
borderRadius: '4px',
backgroundColor: 'var(--affine-pure-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)',
});
@@ -0,0 +1,87 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Button } from '@toeverything/components/button';
import { Tooltip } from '@toeverything/components/tooltip';
import clsx from 'clsx';
import { useMemo, useRef } from 'react';
import * as styles from './share.css';
export interface StorageProgressProgress {
max: number;
value: number;
onUpgrade: () => void;
}
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,
}: StorageProgressProgress) => {
const t = useAFFiNEI18N();
const ref = useRef(null);
const percent = useMemo(
() => Math.round((value / upperLimit) * 100),
[upperLimit, value]
);
const used = useMemo(() => transformBytesToMB(value), [value]);
const max = useMemo(() => transformBytesToGB(upperLimit), [upperLimit]);
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']()}
portalOptions={{
container: ref.current,
}}
>
<div ref={ref}>
<Button disabled onClick={onUpgrade}>
{t['com.affine.storage.upgrade']()}
</Button>
</div>
</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}
</>
);
};
@@ -0,0 +1,5 @@
query allBlobSizes {
collectAllBlobSizes {
size
}
}
+13
View File
@@ -53,6 +53,19 @@ query blobSizes($workspaceId: String!) {
}`,
};
export const allBlobSizesQuery = {
id: 'allBlobSizesQuery' as const,
operationName: 'allBlobSizes',
definitionName: 'collectAllBlobSizes',
containsFile: false,
query: `
query allBlobSizes {
collectAllBlobSizes {
size
}
}`,
};
export const changeEmailMutation = {
id: 'changeEmailMutation' as const,
operationName: 'changeEmail',
+12
View File
@@ -82,6 +82,13 @@ export type BlobSizesQuery = {
collectBlobSizes: { __typename?: 'WorkspaceBlobSizes'; size: number };
};
export type AllBlobSizesQueryVariables = Exact<{ [key: string]: never }>;
export type AllBlobSizesQuery = {
__typename?: 'Query';
collectAllBlobSizes: { __typename?: 'WorkspaceBlobSizes'; size: number };
};
export type ChangeEmailMutationVariables = Exact<{
id: Scalars['String']['input'];
newEmail: Scalars['String']['input'];
@@ -448,6 +455,11 @@ export type Queries =
variables: BlobSizesQueryVariables;
response: BlobSizesQuery;
}
| {
name: 'allBlobSizesQuery';
variables: AllBlobSizesQueryVariables;
response: AllBlobSizesQuery;
}
| {
name: 'getCurrentUserQuery';
variables: GetCurrentUserQueryVariables;
+7 -1
View File
@@ -530,5 +530,11 @@
"com.affine.share-menu.ShareViaExportDescription": "Download a static copy of your page to share with others.",
"com.affine.share-menu.ShareWithLink": "Share with link",
"com.affine.share-menu.ShareWithLinkDescription": "Create a link you can easily share with anyone. The visitors will open your page in the form od a document",
"com.affine.share-menu.ShareMode": "Share mode"
"com.affine.share-menu.ShareMode": "Share mode",
"com.affine.storage.title": "AFFiNE Cloud Storage",
"com.affine.storage.upgrade": "Upgrade to Pro",
"com.affine.storage.disabled.hint": "AFFiNE Cloud is currently in early access phase and is not supported for upgrading, please be patient and wait for our pricing plan.",
"com.affine.storage.used.hint": "Space used",
"com.affine.storage.extend.hint": "The usage has reached its maximum capacity, AFFiNE Cloud is currently in early access phase and is not supported for upgrading, please be patient and wait for our pricing plan. ",
"com.affine.storage.extend.link": "To get more information click here."
}