Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dcellar-web-ui): introduce the migrate bucket feature #379

Merged
merged 7 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions apps/dcellar-web-ui/CHANGELOG.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
{
"name": "dcellar-web-ui",
"entries": [
{
"version": "1.5.0",
"tag": "dcellar-web-ui_v1.5.0",
"date": "Thu, 23 May 2024 03:42:29 GMT",
"comments": {
"minor": [
{
"comment": "Support sponsor payment account"
},
{
"comment": "Introduce the migrate bucket feature"
}
]
}
},
{
"version": "1.4.0",
"tag": "dcellar-web-ui_v1.4.0",
Expand Down
10 changes: 9 additions & 1 deletion apps/dcellar-web-ui/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# Change Log - dcellar-web-ui

This log was last generated on Thu, 16 May 2024 09:14:19 GMT and should not be manually modified.
This log was last generated on Thu, 23 May 2024 03:42:29 GMT and should not be manually modified.

## 1.5.0
Thu, 23 May 2024 03:42:29 GMT

### Minor changes

- Support sponsor payment account
- Introduce the migrate bucket feature

## 1.4.0
Thu, 16 May 2024 09:14:19 GMT
Expand Down
4 changes: 2 additions & 2 deletions apps/dcellar-web-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dcellar-web-ui",
"version": "1.4.0",
"version": "1.5.0",
"private": false,
"scripts": {
"dev": "node ./scripts/dev.js -p 3200",
Expand All @@ -19,7 +19,7 @@
"antd": "5.11.0",
"ahooks": "3.7.7",
"hash-wasm": "4.10.0",
"@bnb-chain/greenfield-js-sdk": "2.0.0-alpha.7",
"@bnb-chain/greenfield-js-sdk": "2.1.0-alpha.0",
"@bnb-chain/greenfield-cosmos-types": "0.4.0-alpha.32",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@ import { Flex, Text } from '@node-real/uikit';
import NextLink from 'next/link';
import { useEffect, useState } from 'react';

export type InsufficientBalanceProps = {
export type InsufficientBalancesProps = {
loginAccount: string;
accounts: { address: string }[];
accounts: string[];
};

export const InsufficientBalance = ({ loginAccount, accounts }: InsufficientBalanceProps) => {
export const InsufficientBalances = ({ loginAccount, accounts }: InsufficientBalancesProps) => {
const [activeWays, setActiveWays] = useState<{ link: string; text: string }[]>([]);

useEffect(() => {
const ways = accounts.map((account) => {
const isOwnerAccount = account.address === loginAccount;
const isOwnerAccount = account === loginAccount;
return isOwnerAccount
? {
link: InternalRoutePaths.transfer_in,
text: 'Transfer in',
}
: {
link: `${InternalRoutePaths.send}&from=${loginAccount}&to=${account.address}`,
link: `${InternalRoutePaths.send}&from=${loginAccount}&to=${account}`,
text: 'Send',
};
});
Expand Down
9 changes: 8 additions & 1 deletion apps/dcellar-web-ui/src/components/layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface LayoutProps extends PropsWithChildren {}
export const Layout = memo<LayoutProps>(function Layout({ children }) {
const dispatch = useAppDispatch();
const isBucketDiscontinue = useAppSelector((root) => root.bucket.isBucketDiscontinue);
const isBucketMigrating = useAppSelector((root) => root.bucket.isBucketMigrating);
const isBucketOwner = useAppSelector((root) => root.bucket.isBucketOwner);
const bucketRecords = useAppSelector((root) => root.bucket.bucketRecords);
const currentBucketName = useAppSelector((root) => root.object.currentBucketName);
Expand Down Expand Up @@ -49,7 +50,13 @@ export const Layout = memo<LayoutProps>(function Layout({ children }) {
},
hover() {
if (pathname !== '/buckets/[...path]') return;
if (isBucketDiscontinue || !isBucketOwner || accountDetail.clientFrozen || !folderExist)
if (
isBucketDiscontinue ||
isBucketMigrating ||
!isBucketOwner ||
accountDetail.clientFrozen ||
!folderExist
)
return;
dispatch(setObjectOperation({ operation: ['', 'upload'] }));
},
Expand Down
29 changes: 28 additions & 1 deletion apps/dcellar-web-ui/src/facade/bucket.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { resolve } from '@/facade/common';
import { DeliverTxResponse, broadcastTx, resolve } from '@/facade/common';
import {
ErrorResponse,
broadcastFault,
Expand All @@ -18,6 +18,7 @@ import {
QueryQuoteUpdateTimeResponse,
} from '@bnb-chain/greenfield-cosmos-types/greenfield/storage/query';
import {
MsgCancelMigrateBucket,
MsgCreateBucket,
MsgUpdateBucketInfo,
} from '@bnb-chain/greenfield-cosmos-types/greenfield/storage/tx';
Expand All @@ -30,6 +31,7 @@ import {
IQuotaProps,
ISimulateGasFee,
Long,
MigrateBucketApprovalRequest,
ReadQuotaRequest,
SpResponse,
TxResponse,
Expand Down Expand Up @@ -456,3 +458,28 @@ export const getBucketActivities = async (id: string): Promise<Activity[]> => {

return result.data.result || [];
};

export const migrateBucket = async (
params: MigrateBucketApprovalRequest,
authType: AuthType,
connector: Connector,
): Promise<ErrorResponse | [DeliverTxResponse, null]> => {
const client = await getClient();
const [tx, error1] = await client.bucket
.migrateBucket(params, authType)
.then(resolve, createTxFault);
if (!tx) return [null, error1];

return broadcastTx({ tx: tx, address: params.operator, connector });
};

export const cancelMigrateBucket = async (
params: MsgCancelMigrateBucket,
connector: Connector,
): Promise<ErrorResponse | [DeliverTxResponse, null]> => {
const client = await getClient();
const [tx, error1] = await client.bucket.cancelMigrateBucket(params).then(resolve, createTxFault);
if (!tx) return [null, error1];

return broadcastTx({ tx: tx, address: params.operator, connector });
};
13 changes: 10 additions & 3 deletions apps/dcellar-web-ui/src/facade/object.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GROUP_ID } from '@/constants/legacy';
import { quotaRemains } from '@/facade/bucket';
import { getObjectInfoAndBucketQuota, resolve, resolveSpRequest } from '@/facade/common';
import { getObjectInfoAndBucketQuota, resolve } from '@/facade/common';
import {
E_NOT_FOUND,
E_NO_QUOTA,
Expand Down Expand Up @@ -41,6 +41,7 @@ import {
} from '@bnb-chain/greenfield-cosmos-types/greenfield/storage/types';
import {
AuthType,
DelegateCreateFolderRepsonse,
DelegatedCreateFolderRequest,
GRNToString,
IQuotaProps,
Expand Down Expand Up @@ -713,8 +714,14 @@ export const updateObjectTags = async (params: UpdateObjectTagsParams, connector
export const delegateCreateFolder = async (
request: DelegatedCreateFolderRequest,
auth: AuthType,
) => {
): Promise<ErrorResponse | [DelegateCreateFolderRepsonse, null]> => {
const client = await getClient();
const [result, error] = await client.object
.delegateCreateFolder(request, auth)
.then(resolve, commonFault);

if (!result || error) return [null, error];
if (result.code !== 0 || !result.body) return [null, result.message ?? ''];

return client.object.delegateCreateFolder(request, auth).then(resolveSpRequest, commonFault);
return [result.body, null];
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { setObjectListPage } from '@/store/slices/object';
import { formatFullTime } from '@/utils/time';
import styled from '@emotion/styled';
import Link from 'next/link';
import { memo } from 'react';
import { DiscontinueNotice } from './DiscontinueNotice';
import { memo, useMemo } from 'react';
import { BucketStatus as BucketStatusEnum } from '@bnb-chain/greenfield-js-sdk';
import { BucketStatusNotice } from './BucketStatusNotice';

interface BucketNameColumnProps {
item: BucketEntity;
Expand All @@ -16,14 +17,33 @@ export const BucketNameColumn = memo<BucketNameColumnProps>(function BucketNameC
const dispatch = useAppDispatch();
const bucketRecords = useAppSelector((root) => root.bucket.bucketRecords);
const { DeleteAt, BucketStatus, BucketName, OffChainStatus } = bucketRecords[item.BucketName];
const discontinue = BucketStatus === 1;
const estimateTime = formatFullTime(
+DeleteAt * 1000 + 7 * 24 * 60 * 60 * 1000,
'YYYY-MM-DD HH:mm:ss',
);
const more = 'https://docs.nodereal.io/docs/dcellar-faq#question-what-is-discontinue';
const content = `This item will be deleted by SP with an estimated time of ${estimateTime}. Please backup your data in time.`;
const isFlowRateLimit = ['1', '3'].includes(OffChainStatus);
const bucketStatusReason = useMemo(() => {
switch (BucketStatus) {
case BucketStatusEnum.BUCKET_STATUS_DISCONTINUED: {
const estimateTime = formatFullTime(
+DeleteAt * 1000 + 7 * 24 * 60 * 60 * 1000,
'YYYY-MM-DD HH:mm:ss',
);
return {
icon: 'colored-error2',
title: 'Discontinue Notice',
link: 'https://docs.nodereal.io/docs/dcellar-faq#question-what-is-discontinue',
desc: `This item will be deleted by SP with an estimated time of ${estimateTime}. Please backup your data in time.`,
show: true,
};
}
case BucketStatusEnum.BUCKET_STATUS_MIGRATING:
return {
icon: 'migrate',
title: 'Data Migrating',
desc: 'This bucket, in the process of data migration to another provider, supports only downloads, quota modifications, deletions, and sharing. It does not support uploads.',
show: true,
};
default:
return null;
}
}, [BucketStatus, DeleteAt]);

return (
<Container>
Expand All @@ -37,12 +57,10 @@ export const BucketNameColumn = memo<BucketNameColumnProps>(function BucketNameC
<IconFont type="bucket-thumbnail" w={20} />
<span title={item.BucketName}>{item.BucketName}</span>
</Link>
{(discontinue || isFlowRateLimit) && (
<DiscontinueNotice
discontinue={discontinue}
{(bucketStatusReason || isFlowRateLimit) && (
<BucketStatusNotice
bucketStatusReason={bucketStatusReason}
flowRateLimit={isFlowRateLimit}
content={content}
learnMore={more}
/>
)}
</Container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { EditBucketTagsOperation } from './EditBucketTagsOperation';
import { PaymentAccountOperation } from './PaymentAccountOperation';
import { UpdateBucketTagsOperation } from './UpdateBucketTagsOperation';
import { useRouter } from 'next/router';
import { MigrateBucketOperation } from './MigrateBucketOperation';

interface BucketOperationsProps {
level?: 0 | 1;
Expand All @@ -41,6 +42,7 @@ export const BucketOperations = memo<BucketOperationsProps>(function BucketOpera
'tags',
'edit_tags',
'update_tags',
'migrate',
].includes(operation);
const isModal = ['delete'].includes(operation);
const _operation = useModalValues<BucketOperationsType>(operation);
Expand Down Expand Up @@ -78,6 +80,8 @@ export const BucketOperations = memo<BucketOperationsProps>(function BucketOpera
return <EditBucketTagsOperation onClose={onClose} />;
case 'update_tags':
return <UpdateBucketTagsOperation bucket={_selectBucketInfo} onClose={onClose} />;
case 'migrate':
return <MigrateBucketOperation bucket={_selectBucketInfo} onClose={onClose} />;
default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,36 @@ import { IconFont } from '@/components/IconFont';
import { DCLink } from '@/components/common/DCLink';
import { Box, Divider, Flex, Menu, MenuButton, MenuList, Text } from '@node-real/uikit';

export const DiscontinueNotice = ({
content,
learnMore,
type InvalidStatusReason = {
title: string;
desc: string;
link?: string;
icon: string;
show: boolean;
};
export const BucketStatusNotice = ({
flowRateLimit = false,
discontinue = true,
bucketStatusReason,
}: {
content: string;
learnMore: string;
flowRateLimit?: boolean;
discontinue?: boolean;
bucketStatusReason: {
title: string;
desc: string;
link?: string;
icon: string;
show: boolean;
} | null;
}) => {
const account = discontinue && flowRateLimit ? 2 : 0;
const discontinueReasons = [
const account = bucketStatusReason && flowRateLimit ? 2 : 0;
const discontinueReasons: InvalidStatusReason[] = [
{
icon: 'colored-error2',
title: 'Flow rate exceeds limit',
desc: "The bucket's flow rate exceeds the payment account limit. Contact the account owner or switch accounts to increase it.",
link: 'https://docs.nodereal.io/docs/dcellar-faq#question-why-is-my-bucket-flow-rate-limited',
show: flowRateLimit,
},
{ title: 'Discontinue Notice', desc: content, link: learnMore, show: discontinue },
bucketStatusReason || { title: '', desc: '', icon: '', show: false },
].filter((i) => i.show);

return (
Expand All @@ -35,7 +45,14 @@ export const DiscontinueNotice = ({
alignItems={'center'}
fontWeight={600}
>
<IconFont type="colored-error2" w={16} />
<IconFont
type={
flowRateLimit
? 'colored-error2'
: (bucketStatusReason && bucketStatusReason.icon) || 'colored-error2'
}
w={16}
/>
{!!account && <>{account}</>}
</MenuButton>
<MenuList>
Expand All @@ -48,9 +65,11 @@ export const DiscontinueNotice = ({
</Text>
<Text color={'readable.secondary'}>{desc}</Text>
<Flex justifyContent={'right'}>
<DCLink href={link} target="_blank" onClick={(e) => e.stopPropagation()}>
Learn More
</DCLink>
{link && (
<DCLink href={link} target="_blank" onClick={(e) => e.stopPropagation()}>
Learn More
</DCLink>
)}
</Flex>
{index !== discontinueReasons.length - 1 && <Divider my={12} />}
</Box>
Expand Down
Loading
Loading