diff --git a/apps/dcellar-web-ui/CHANGELOG.json b/apps/dcellar-web-ui/CHANGELOG.json
index a758872c..6600f228 100644
--- a/apps/dcellar-web-ui/CHANGELOG.json
+++ b/apps/dcellar-web-ui/CHANGELOG.json
@@ -1,6 +1,21 @@
{
"name": "dcellar-web-ui",
"entries": [
+ {
+ "version": "1.4.0",
+ "tag": "dcellar-web-ui_v1.4.0",
+ "date": "Thu, 16 May 2024 09:14:19 GMT",
+ "comments": {
+ "minor": [
+ {
+ "comment": "Introduce the activities feature for bucket, object and group"
+ },
+ {
+ "comment": "Introduce the stop upload feature"
+ }
+ ]
+ }
+ },
{
"version": "1.3.2",
"tag": "dcellar-web-ui_v1.3.2",
diff --git a/apps/dcellar-web-ui/CHANGELOG.md b/apps/dcellar-web-ui/CHANGELOG.md
index 41ff6340..1c0b8d7a 100644
--- a/apps/dcellar-web-ui/CHANGELOG.md
+++ b/apps/dcellar-web-ui/CHANGELOG.md
@@ -1,6 +1,14 @@
# Change Log - dcellar-web-ui
-This log was last generated on Sat, 11 May 2024 05:44:13 GMT and should not be manually modified.
+This log was last generated on Thu, 16 May 2024 09:14:19 GMT and should not be manually modified.
+
+## 1.4.0
+Thu, 16 May 2024 09:14:19 GMT
+
+### Minor changes
+
+- Introduce the activities feature for bucket, object and group
+- Introduce the stop upload feature
## 1.3.2
Sat, 11 May 2024 05:44:13 GMT
diff --git a/apps/dcellar-web-ui/package.json b/apps/dcellar-web-ui/package.json
index 15050e08..94210fef 100644
--- a/apps/dcellar-web-ui/package.json
+++ b/apps/dcellar-web-ui/package.json
@@ -1,6 +1,6 @@
{
"name": "dcellar-web-ui",
- "version": "1.3.2",
+ "version": "1.4.0",
"private": false,
"scripts": {
"dev": "node ./scripts/dev.js -p 3200",
diff --git a/apps/dcellar-web-ui/public/js/iconfont_v0.1.1.min.js b/apps/dcellar-web-ui/public/js/iconfont_v0.1.12.min.js
similarity index 98%
rename from apps/dcellar-web-ui/public/js/iconfont_v0.1.1.min.js
rename to apps/dcellar-web-ui/public/js/iconfont_v0.1.12.min.js
index 5057417d..1e4a3576 100644
--- a/apps/dcellar-web-ui/public/js/iconfont_v0.1.1.min.js
+++ b/apps/dcellar-web-ui/public/js/iconfont_v0.1.12.min.js
@@ -1 +1 @@
-!function(e){var t,n,d,o,i,a,r='';function c(){i||(i=!0,d())}t=function(){var e,t,n;(n=document.createElement("div")).innerHTML=r,r=null,(t=n.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",e=t,(n=document.body).firstChild?(t=n.firstChild).parentNode.insertBefore(e,t):n.appendChild(e))},document.addEventListener?["complete","loaded","interactive"].indexOf(document.readyState)>-1?setTimeout(t,0):(n=function(){document.removeEventListener("DOMContentLoaded",n,!1),t()},document.addEventListener("DOMContentLoaded",n,!1)):document.attachEvent&&(d=t,o=e.document,i=!1,(a=function(){try{o.documentElement.doScroll("left")}catch(e){return void setTimeout(a,50)}c()})(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,c())})}(window);
\ No newline at end of file
+!function(e){var t,n,d,o,i,a,r='';function c(){i||(i=!0,d())}t=function(){var e,t,n;(n=document.createElement("div")).innerHTML=r,r=null,(t=n.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",e=t,(n=document.body).firstChild?(t=n.firstChild).parentNode.insertBefore(e,t):n.appendChild(e))},document.addEventListener?["complete","loaded","interactive"].indexOf(document.readyState)>-1?setTimeout(t,0):(n=function(){document.removeEventListener("DOMContentLoaded",n,!1),t()},document.addEventListener("DOMContentLoaded",n,!1)):document.attachEvent&&(d=t,o=e.document,i=!1,(a=function(){try{o.documentElement.doScroll("left")}catch(e){return void setTimeout(a,50)}c()})(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,c())})}(window);
\ No newline at end of file
diff --git a/apps/dcellar-web-ui/src/components/Activities/index.tsx b/apps/dcellar-web-ui/src/components/Activities/index.tsx
new file mode 100644
index 00000000..3529f86d
--- /dev/null
+++ b/apps/dcellar-web-ui/src/components/Activities/index.tsx
@@ -0,0 +1,80 @@
+import { GREENFIELD_CHAIN_EXPLORER_URL } from '@/base/env';
+import { IconFont } from '@/components/IconFont';
+import { CopyText } from '@/components/common/CopyText';
+import { ListEmpty } from '@/components/common/DCTable/ListEmpty';
+import { Activity } from '@/store/slices/object';
+import { formatMsgType } from '@/utils/object';
+import { trimAddress } from '@/utils/string';
+import { formatFullTime } from '@/utils/time';
+import { Box, Center, Flex, Link, Loading, Text } from '@node-real/uikit';
+import { memo } from 'react';
+
+interface ActivitiesProps {
+ loading: boolean;
+ activities: Activity[];
+}
+
+export const Activities = memo(function Activities({ loading, activities }) {
+ if (loading) return ;
+ if (!activities.length)
+ return (
+
+ );
+
+ return (
+ <>
+ {activities.map((item, index) => (
+
+
+
+
+
+ {index < activities.length - 1 && (
+
+ )}
+
+
+
+
+ {formatMsgType(item.tx_result.type)}
+
+
+ Transaction Hash
+
+ (
+
+ 0x{trimAddress(item.hash, 28, 6, 5)}
+
+ )
+
+
+
+
+
+ {formatFullTime(item.time)}
+
+
+
+
+ ))}
+ {activities.length >= 100 && (
+
+ Only showing the latest 100 activities ~
+
+ )}
+ >
+ );
+});
diff --git a/apps/dcellar-web-ui/src/components/layout/GlobalManagements/GlobalObjectUploadManager.tsx b/apps/dcellar-web-ui/src/components/layout/GlobalManagements/GlobalObjectUploadManager.tsx
index d776f4fb..a94a817a 100644
--- a/apps/dcellar-web-ui/src/components/layout/GlobalManagements/GlobalObjectUploadManager.tsx
+++ b/apps/dcellar-web-ui/src/components/layout/GlobalManagements/GlobalObjectUploadManager.tsx
@@ -222,6 +222,7 @@ export const GlobalObjectUploadManager = memo(
} else {
axios
.put(url, task.waitObject.file, {
+ signal: task.abortController?.signal,
async onUploadProgress(progressEvent) {
const progress = progressEvent.total
? Math.floor((progressEvent.loaded / progressEvent.total) * 100)
@@ -265,6 +266,7 @@ export const GlobalObjectUploadManager = memo(
setupUploadTaskErrorMsg({
account: loginAccount,
task,
+ status: e?.code === 'ERR_CANCELED' ? 'CANCEL' : 'ERROR',
errorMsg: authExpired
? 'Authentication expired.'
: message || e?.message || 'upload error',
@@ -461,8 +463,28 @@ export const GlobalObjectUploadManager = memo(
// 3. upload
useAsyncEffect(async () => {
if (!uploadTasks.length) return;
- dispatch(updateUploadStatus({ ids: uploadTasks, status: 'UPLOAD', account: loginAccount }));
- const tasks = queue.filter((t) => uploadTasks.includes(t.id));
+ // Add abortController to each task
+ const extraFields: Record> = uploadTasks.reduce(
+ (acc, id) => {
+ acc[id] = {
+ abortController: new AbortController(),
+ };
+ return acc;
+ },
+ {} as Record>,
+ );
+ dispatch(
+ updateUploadStatus({
+ ids: uploadTasks,
+ status: 'UPLOAD',
+ account: loginAccount,
+ extraFields,
+ }),
+ );
+
+ const tasks = queue
+ .filter((t) => uploadTasks.includes(t.id))
+ .map((t) => ({ ...t, ...extraFields[t.id] }));
tasks.forEach(runUploadTask);
}, [uploadTasks.join('')]);
diff --git a/apps/dcellar-web-ui/src/components/layout/Nav/index.tsx b/apps/dcellar-web-ui/src/components/layout/Nav/index.tsx
index 1a262b66..37e96621 100644
--- a/apps/dcellar-web-ui/src/components/layout/Nav/index.tsx
+++ b/apps/dcellar-web-ui/src/components/layout/Nav/index.tsx
@@ -1,7 +1,7 @@
import { IconFont } from '@/components/IconFont';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
-import { Box, Text } from '@node-real/uikit';
+import { Box, Flex, Text, Tooltip } from '@node-real/uikit';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { memo } from 'react';
@@ -43,9 +43,21 @@ const ASIDE = [
{
link: 'https://docs.bnbchain.org/greenfield-docs/',
trackId: 'dc.main.nav.doc.click',
- icon: 'doc',
+ icon: 'book',
text: 'BNB Greenfield Docs',
},
+ {
+ link: 'https://docs.bnbchain.org/greenfield-docs/docs/release-notes/releaseNotes/',
+ trackId: 'dc.main.nav.release_note.click',
+ icon: 'doc',
+ text: 'Release Notes',
+ },
+ {
+ link: 'https://discord.com/invite/bnbchain',
+ trackId: 'dc.main.nav.discord.click',
+ icon: 'discord',
+ text: 'Discord',
+ },
{
link: 'https://docs.nodereal.io/docs/dcellar-get-started',
trackId: 'dc.main.nav.faq.click',
@@ -78,18 +90,38 @@ export const Nav = memo(function Nav() {
);
})}
-
+
{ASIDE.map((menu) => (
-
+
+
+
+
+
+
+
+
+
))}
-
+
);
});
@@ -114,6 +146,7 @@ const MenuItem = styled.li<{ $active?: boolean }>`
position: relative;
font-weight: 500;
transition: all 0.15s;
+ list-style-type: none;
a {
display: grid;
gap: 12px;
diff --git a/apps/dcellar-web-ui/src/facade/bucket.ts b/apps/dcellar-web-ui/src/facade/bucket.ts
index c842552b..1c52fb7d 100644
--- a/apps/dcellar-web-ui/src/facade/bucket.ts
+++ b/apps/dcellar-web-ui/src/facade/bucket.ts
@@ -10,7 +10,7 @@ import {
import { getClient } from '@/facade/index';
import { BroadcastResponse } from '@/facade/object';
import { signTypedDataCallback } from '@/facade/wallet';
-import { ObjectResource } from '@/store/slices/object';
+import { Activity, ObjectResource } from '@/store/slices/object';
import { parseError } from '@/utils/string';
import { getTimestampInSeconds } from '@/utils/time';
import {
@@ -447,3 +447,12 @@ export const updateBucketTags = async (params: UpdateBucketTagsParams, connector
return tx.broadcast(payload).then(resolve, broadcastFault);
};
+
+export const getBucketActivities = async (id: string): Promise => {
+ const url = `/api/tx/list/by_bucket/${id}`;
+
+ const [result] = await axios.get<{ result: Activity[] }>(url).then(resolve, commonFault);
+ if (!result) return [];
+
+ return result.data.result || [];
+};
diff --git a/apps/dcellar-web-ui/src/facade/group.ts b/apps/dcellar-web-ui/src/facade/group.ts
index 7beb6c8b..ed45b681 100644
--- a/apps/dcellar-web-ui/src/facade/group.ts
+++ b/apps/dcellar-web-ui/src/facade/group.ts
@@ -10,6 +10,7 @@ import { getClient } from '@/facade/index';
import { BroadcastResponse, DeliverResponse, xmlParser } from '@/facade/object';
import { signTypedDataCallback } from '@/facade/wallet';
import { GroupMember } from '@/store/slices/group';
+import { Activity } from '@/store/slices/object';
import {
MsgCreateGroup,
MsgDeleteGroup,
@@ -252,3 +253,12 @@ export const updateGroupTags = async (
return tx.broadcast(payload).then(resolve, broadcastFault);
};
+
+export const getGroupActivities = async (id: string): Promise => {
+ const url = `/api/tx/list/by_group/${id}`;
+
+ const [result] = await axios.get<{ result: Activity[] }>(url).then(resolve, commonFault);
+ if (!result) return [];
+
+ return result.data.result || [];
+};
diff --git a/apps/dcellar-web-ui/src/facade/object.ts b/apps/dcellar-web-ui/src/facade/object.ts
index d47760b0..cfcd6fcc 100644
--- a/apps/dcellar-web-ui/src/facade/object.ts
+++ b/apps/dcellar-web-ui/src/facade/object.ts
@@ -58,7 +58,7 @@ import { ObjectMeta } from '@bnb-chain/greenfield-js-sdk/dist/esm/types/sp/Commo
import axios from 'axios';
import { XMLParser } from 'fast-xml-parser';
import { Connector } from 'wagmi';
-import { ObjectVersion } from '@/store/slices/object';
+import { Activity, ObjectVersion } from '@/store/slices/object';
export type DeliverResponse = Awaited>;
@@ -653,6 +653,15 @@ export const getObjectVersions = async (id: string): Promise =>
return result.data.result || [];
};
+export const getObjectActivities = async (id: string): Promise => {
+ const url = `/api/tx/list/by_object/${id}`;
+
+ const [result] = await axios.get<{ result: Activity[] }>(url).then(resolve, commonFault);
+ if (!result) return [];
+
+ return result.data.result || [];
+};
+
export type UpdateObjectTagsParams = {
address: string;
bucketName: string;
diff --git a/apps/dcellar-web-ui/src/modules/bucket/components/DetailBucketOperation.tsx b/apps/dcellar-web-ui/src/modules/bucket/components/DetailBucketOperation.tsx
index 7c47b9ec..b19ec3ba 100644
--- a/apps/dcellar-web-ui/src/modules/bucket/components/DetailBucketOperation.tsx
+++ b/apps/dcellar-web-ui/src/modules/bucket/components/DetailBucketOperation.tsx
@@ -11,6 +11,7 @@ import {
setBucketEditQuota,
setupBucketQuota,
TBucket,
+ setupBucketActivity,
} from '@/store/slices/bucket';
import { selectBucketSp } from '@/store/slices/sp';
import { convertObjectKey } from '@/utils/common';
@@ -27,13 +28,19 @@ import {
QDrawerBody,
QDrawerFooter,
QDrawerHeader,
+ Tab,
+ TabList,
+ TabPanel,
+ TabPanels,
+ Tabs,
Text,
Tooltip,
} from '@node-real/uikit';
import dayjs from 'dayjs';
import { memo, PropsWithChildren, useEffect, useState } from 'react';
-import { useUnmount } from 'ahooks';
+import { useMount, useUnmount } from 'ahooks';
import { DEFAULT_TAG } from '@/components/common/ManageTags';
+import { Activities } from '@/components/Activities';
export const Label = ({ children }: PropsWithChildren) => (
@@ -41,6 +48,8 @@ export const Label = ({ children }: PropsWithChildren) => (
);
+const VERSION_TABS = ['General Info', 'Activities'];
+
interface DetailBucketOperationProps {
selectedBucketInfo: TBucket;
}
@@ -68,6 +77,7 @@ export const DetailBucketOperation = memo(function D
}) {
const dispatch = useAppDispatch();
const bucketQuotaRecords = useAppSelector((root) => root.bucket.bucketQuotaRecords);
+ const bucketActivityRecords = useAppSelector((root) => root.bucket.bucketActivityRecords);
const accountInfos = useAppSelector((root) => root.accounts.accountInfos);
const primarySp = useAppSelector(selectBucketSp(selectedBucketInfo))!;
@@ -76,6 +86,10 @@ export const DetailBucketOperation = memo(function D
const endDate = dayjs().utc?.().endOf('month').format('D MMM, YYYY');
const formattedQuota = formatQuota(bucketQuota);
+ const activityKey = selectedBucketInfo.BucketName;
+ const loading = !(activityKey in bucketActivityRecords);
+ const bucketActivities = bucketActivityRecords[activityKey];
+
const nullObjectMeta: ObjectMeta = {
...defaultNullObject,
ObjectInfo: {
@@ -367,6 +381,10 @@ export const DetailBucketOperation = memo(function D
dispatch(setupBucketQuota(selectedBucketInfo.BucketName));
}, [selectedBucketInfo.BucketName, dispatch]);
+ useMount(() => {
+ dispatch(setupBucketActivity(selectedBucketInfo.BucketName, selectedBucketInfo.Id));
+ });
+
useUnmount(() => dispatch(setBucketTagsEditData([DEFAULT_TAG])));
return (
@@ -432,10 +450,25 @@ export const DetailBucketOperation = memo(function D
-
- {getContent()}
-
-
+
+
+ {VERSION_TABS.map((tab) => (
+
+ {tab}
+
+ ))}
+
+
+
+ {getContent()}
+
+
+
+
+
+
+
+
diff --git a/apps/dcellar-web-ui/src/modules/group/components/DetailGroupOperation.tsx b/apps/dcellar-web-ui/src/modules/group/components/DetailGroupOperation.tsx
index 03a5b2f0..469a34a1 100644
--- a/apps/dcellar-web-ui/src/modules/group/components/DetailGroupOperation.tsx
+++ b/apps/dcellar-web-ui/src/modules/group/components/DetailGroupOperation.tsx
@@ -1,4 +1,5 @@
import { GREENFIELD_CHAIN_EXPLORER_URL } from '@/base/env';
+import { Activities } from '@/components/Activities';
import { Avatar } from '@/components/Avatar';
import { IconFont } from '@/components/IconFont';
import { CopyText } from '@/components/common/CopyText';
@@ -6,14 +7,33 @@ import { DCButton } from '@/components/common/DCButton';
import { DEFAULT_TAG } from '@/components/common/ManageTags';
import { LoadingAdaptor } from '@/modules/accounts/components/LoadingAdaptor';
import { useAppDispatch, useAppSelector } from '@/store';
-import { setGroupTagsEditData, setGroupOperation, setupGroupMembers } from '@/store/slices/group';
+import {
+ setGroupTagsEditData,
+ setGroupOperation,
+ setupGroupMembers,
+ setupGroupActivity,
+} from '@/store/slices/group';
import { GroupInfo } from '@bnb-chain/greenfield-cosmos-types/greenfield/storage/types';
import styled from '@emotion/styled';
-import { Box, Divider, Flex, QDrawerBody, QDrawerHeader, Text } from '@node-real/uikit';
+import {
+ Box,
+ Divider,
+ Flex,
+ QDrawerBody,
+ QDrawerHeader,
+ Tab,
+ TabList,
+ TabPanel,
+ TabPanels,
+ Tabs,
+ Text,
+} from '@node-real/uikit';
import { useAsyncEffect, useUnmount } from 'ahooks';
import { ethers } from 'ethers';
import { memo } from 'react';
+import { useMount } from 'react-use';
+const VERSION_TABS = ['General Info', 'Activities'];
interface DetailGroupOperationProps {
selectGroup: GroupInfo;
}
@@ -23,6 +43,7 @@ export const DetailGroupOperation = memo(function Gro
}) {
const dispatch = useAppDispatch();
const groupMemberListRecords = useAppSelector((root) => root.group.groupMemberListRecords);
+ const groupActivityRecords = useAppSelector((root) => root.group.groupActivityRecords);
const spRecords = useAppSelector((root) => root.sp.spRecords);
const specifiedSp = useAppSelector((root) => root.sp.specifiedSp);
@@ -36,6 +57,10 @@ export const DetailGroupOperation = memo(function Gro
32,
);
+ const activityKey = selectGroup.groupName;
+ const loadingActivity = !(activityKey in groupActivityRecords);
+ const bucketActivities = groupActivityRecords[activityKey];
+
const onEditTag = () => {
dispatch(setGroupTagsEditData(selectGroup?.tags?.tags ?? [DEFAULT_TAG]));
dispatch(setGroupOperation({ level: 1, operation: [selectGroup.id, 'update_tags'] }));
@@ -45,6 +70,10 @@ export const DetailGroupOperation = memo(function Gro
dispatch(setupGroupMembers(selectGroup.id, spRecords[specifiedSp].endpoint));
}, [dispatch, selectGroup]);
+ useMount(() => {
+ dispatch(setupGroupActivity(selectGroup.groupName, selectGroup.id));
+ });
+
useUnmount(() => dispatch(setGroupTagsEditData([DEFAULT_TAG])));
return (
@@ -80,99 +109,115 @@ export const DetailGroupOperation = memo(function Gro
-
-
-
- Group ID
-
-
- {selectGroup.id}
-
-
-
-
- Tags
-
-
- {selectGroup?.tags?.tags?.length || 0} tags
-
-
-
-
-
-
- Members
-
-
-
-
-
- {members.slice(0, 5).map((m) => {
- return (
-
-
-
- );
- })}
- {moreText && (
-
+
+ {VERSION_TABS.map((tab) => (
+
+ {tab}
+
+ ))}
+
+
+
+
+ {/* */}
+
+ Group ID
+
+
- {moreText}
-
- )}
+ {selectGroup.id}
+
+
-
-
-
- dispatch(setGroupOperation({ level: 1, operation: [selectGroup.id, 'add'] }))
- }
- >
- Manage Members
-
-
-
+
+ Tags
+
+
+ {selectGroup?.tags?.tags?.length || 0} tags
+
+
+
+
+
+
+ Members
+
+
+
+
+
+ {members.slice(0, 5).map((m) => {
+ return (
+
+
+
+ );
+ })}
+ {moreText && (
+
+ {moreText}
+
+ )}
+
+
+
+
+ dispatch(setGroupOperation({ level: 1, operation: [selectGroup.id, 'add'] }))
+ }
+ >
+ Manage Members
+
+
+
+
+
+
+
+
+
>
);
diff --git a/apps/dcellar-web-ui/src/modules/object/components/DetailObjectOperation.tsx b/apps/dcellar-web-ui/src/modules/object/components/DetailObjectOperation.tsx
index 9438a0cf..ee05d0ea 100644
--- a/apps/dcellar-web-ui/src/modules/object/components/DetailObjectOperation.tsx
+++ b/apps/dcellar-web-ui/src/modules/object/components/DetailObjectOperation.tsx
@@ -19,6 +19,7 @@ import {
ObjectActionType,
setObjectEditTagsData,
setObjectOperation,
+ setupObjectActivity,
setupObjectVersion,
} from '@/store/slices/object';
import { getSpOffChainData } from '@/store/slices/persist';
@@ -49,11 +50,10 @@ import { last } from 'lodash-es';
import { memo, useState } from 'react';
import { OBJECT_ERROR_TYPES, ObjectErrorType } from '../ObjectError';
import { setSignatureAction } from '@/store/slices/global';
-import { ListEmpty } from '@/components/common/DCTable/ListEmpty';
-import { TD, TH } from '@/modules/bucket/components/SPSelector/style';
import { VersionTable } from '@/modules/object/components/VersionTable';
+import { Activities } from '@/components/Activities';
-const VERSION_TABS = ['General Info', 'Versions'];
+const VERSION_TABS = ['General Info', 'Activities', 'Versions'];
interface DetailObjectOperationProps {
selectObjectInfo: ObjectMeta;
@@ -67,6 +67,7 @@ export const DetailObjectOperation = memo(
const { selectObjectInfo, selectBucket, bucketAccountDetail, primarySp } = props;
const dispatch = useAppDispatch();
const objectVersionRecords = useAppSelector((root) => root.object.objectVersionRecords);
+ const objectActivityRecords = useAppSelector((root) => root.object.objectActivityRecords);
const accountRecords = useAppSelector((root) => root.persist.accountRecords);
const loginAccount = useAppSelector((root) => root.persist.loginAccount);
const currentBucketName = useAppSelector((root) => root.object.currentBucketName);
@@ -82,6 +83,9 @@ export const DetailObjectOperation = memo(
const loading = !(versionKey in objectVersionRecords);
const objectVersions = objectVersionRecords[versionKey];
+ const loadingActivity = !(versionKey in objectActivityRecords);
+ const objectActivities = objectActivityRecords[versionKey];
+
const errorHandler = (type: string) => {
setAction('');
if (type === E_OFF_CHAIN_AUTH) {
@@ -150,6 +154,7 @@ export const DetailObjectOperation = memo(
useMount(() => {
dispatch(setupObjectVersion(objectInfo.ObjectName, objectInfo.Id));
+ dispatch(setupObjectActivity(objectInfo.ObjectName, objectInfo.Id));
});
useUnmount(() => dispatch(setObjectEditTagsData([DEFAULT_TAG])));
@@ -252,6 +257,9 @@ export const DetailObjectOperation = memo(
+
+
+
diff --git a/apps/dcellar-web-ui/src/modules/toolbox/components/Common.tsx b/apps/dcellar-web-ui/src/modules/toolbox/components/Common.tsx
index 38e17f07..99e96236 100644
--- a/apps/dcellar-web-ui/src/modules/toolbox/components/Common.tsx
+++ b/apps/dcellar-web-ui/src/modules/toolbox/components/Common.tsx
@@ -1,10 +1,21 @@
-import { Center, CircleProps, Flex, FlexProps, LinkProps, Tooltip } from '@node-real/uikit';
+import {
+ Center,
+ CircleProps,
+ Flex,
+ FlexProps,
+ LinkProps,
+ Tooltip,
+ useMediaQuery,
+} from '@node-real/uikit';
export const Card = ({ children, ...props }: FlexProps) => {
+ const [isNormal] = useMediaQuery('(max-width: 1440px)');
return (
{
return (
<>
{data.map((item, index) => (
-
+ t.name === 'Link')?.url || ''}
+ target="_blank"
+ rel="noopener noreferrer"
+ >
diff --git a/apps/dcellar-web-ui/src/modules/toolbox/config.ts b/apps/dcellar-web-ui/src/modules/toolbox/config.ts
index 7d596ead..41bc75f2 100644
--- a/apps/dcellar-web-ui/src/modules/toolbox/config.ts
+++ b/apps/dcellar-web-ui/src/modules/toolbox/config.ts
@@ -72,7 +72,7 @@ export const toolList = [
url: 'https://github.com/bnb-chain/greenfield-go-sdk',
},
],
- desc: 'Go sdk for Greenfield',
+ desc: 'Go SDK for Greenfield',
},
{
icon: 'cosmos',
@@ -86,7 +86,7 @@ export const toolList = [
url: 'https://github.com/bnb-chain/greenfield-cosmos-sdk',
},
],
- desc: 'A cosmos-sdk fork for greenfield',
+ desc: 'A cosmos-SDK fork for Greenfield',
},
{
icon: 'javascript',
@@ -142,7 +142,7 @@ export const toolList = [
url: 'https://github.com/bnb-chain/greenfield-python-sdk',
},
],
- desc: 'Python sdk for Greenfield',
+ desc: 'Python SDK for Greenfield',
},
{
icon: 'terminal',
diff --git a/apps/dcellar-web-ui/src/modules/upload/ObjectUploadStatus.tsx b/apps/dcellar-web-ui/src/modules/upload/ObjectUploadStatus.tsx
index 04d298e3..e0ab9a25 100644
--- a/apps/dcellar-web-ui/src/modules/upload/ObjectUploadStatus.tsx
+++ b/apps/dcellar-web-ui/src/modules/upload/ObjectUploadStatus.tsx
@@ -1,10 +1,14 @@
import { UploadObject } from '@/store/slices/global';
-import { Text } from '@node-real/uikit';
import { Loading } from '@/components/common/Loading';
import { UploadProgress } from './UploadProgress';
import { IconFont } from '@/components/IconFont';
+import { memo } from 'react';
-export const ObjectUploadStatus = ({ task }: { task: UploadObject }) => {
+export const ObjectUploadStatus = memo(function ObjectUploadStatus({
+ task,
+}: {
+ task: UploadObject;
+}) {
switch (task.status) {
case 'RETRY_CHECK':
case 'RETRY_CHECKING':
@@ -61,11 +65,11 @@ export const ObjectUploadStatus = ({ task }: { task: UploadObject }) => {
case 'CANCEL':
return (
<>
-
- Cancelled
+
+ Stopped
>
);
default:
return null;
}
-};
+});
diff --git a/apps/dcellar-web-ui/src/modules/upload/UploadActionButton.tsx b/apps/dcellar-web-ui/src/modules/upload/UploadActionButton.tsx
index 9721f0bc..aae1ce23 100644
--- a/apps/dcellar-web-ui/src/modules/upload/UploadActionButton.tsx
+++ b/apps/dcellar-web-ui/src/modules/upload/UploadActionButton.tsx
@@ -1,36 +1,51 @@
import { IconFont } from '@/components/IconFont';
import { DCButton } from '@/components/common/DCButton';
import { useAppDispatch, useAppSelector } from '@/store';
-import { clearUploadRecords, retryUploadTasks } from '@/store/slices/global';
-import React from 'react';
+import {
+ cancelUploadingRequests,
+ clearUploadRecords,
+ retryUploadTasks,
+ updateUploadStatus,
+} from '@/store/slices/global';
+import React, { useCallback } from 'react';
export type ActionButtonProps = {
- type: 'clear' | 'retry' | 'clear-all' | 'retry-all';
+ type: 'clear' | 'retry' | 'clear-all' | 'retry-all' | 'cancel' | 'cancel-all';
ids: number[];
text?: string;
};
const actionItems = [
{
- type: 'clear',
- text: 'Clear',
- icon: 'delete',
+ type: 'cancel',
+ text: 'Cancel',
+ icon: 'stop',
+ },
+ {
+ type: 'cancel-all',
+ text: 'Stop Uploading',
+ icon: 'stop',
},
{
type: 'retry',
text: 'Retry',
icon: 'retry',
},
- {
- type: 'clear-all',
- text: 'Clear All Records',
- icon: 'delete',
- },
{
type: 'retry-all',
text: 'Retry All',
icon: 'retry',
},
+ {
+ type: 'clear',
+ text: 'Clear',
+ icon: 'delete',
+ },
+ {
+ type: 'clear-all',
+ text: 'Clear All Records',
+ icon: 'delete',
+ },
];
export const UploadActionButton = React.memo(function UploadActionButton({
@@ -42,6 +57,19 @@ export const UploadActionButton = React.memo(function UploadActionButton({
const dispatch = useAppDispatch();
const actionItem = actionItems.find((item) => item.type === type);
+ const onCancel = useCallback(
+ (ids: number[]) => {
+ dispatch(
+ updateUploadStatus({
+ account: loginAccount,
+ ids,
+ status: 'CANCEL',
+ }),
+ );
+ dispatch(cancelUploadingRequests({ ids }));
+ },
+ [dispatch, loginAccount],
+ );
const onClear = (ids: number[]) => {
dispatch(clearUploadRecords({ ids, loginAccount }));
};
@@ -50,14 +78,18 @@ export const UploadActionButton = React.memo(function UploadActionButton({
};
const onClick = () => {
switch (type) {
- case 'clear':
- case 'clear-all':
- onClear(ids);
+ case 'cancel':
+ case 'cancel-all':
+ onCancel(ids);
break;
case 'retry':
case 'retry-all':
onRetry(ids);
break;
+ case 'clear':
+ case 'clear-all':
+ onClear(ids);
+ break;
default:
break;
}
diff --git a/apps/dcellar-web-ui/src/modules/upload/UploadingObjects.tsx b/apps/dcellar-web-ui/src/modules/upload/UploadingObjects.tsx
index 4b9cd11d..d1c33a5e 100644
--- a/apps/dcellar-web-ui/src/modules/upload/UploadingObjects.tsx
+++ b/apps/dcellar-web-ui/src/modules/upload/UploadingObjects.tsx
@@ -19,7 +19,7 @@ import { UploadingPanelKey, useTaskManagementTab } from './useTaskManagementTab'
import { IconFont } from '@/components/IconFont';
import { UploadingObjectsList } from './UploadingObjectsList';
-import { UploadObject } from '@/store/slices/global';
+import { UPLOADING_STATUSES, UPLOAD_FAILED_STATUSES, UploadObject } from '@/store/slices/global';
import { UploadActionButton } from './UploadActionButton';
interface UploadingObjectsProps {}
@@ -36,15 +36,20 @@ export const UploadingObjects = memo(function UploadingOb
panelKey: UploadingPanelKey;
data: UploadObject[];
}) => {
- if ([UploadingPanelKey.ALL, UploadingPanelKey.UPLOADING].includes(panelKey)) {
- return null;
- }
return (
{panelKey === UploadingPanelKey.COMPLETE && (
item.id)} />
)}
- {panelKey === UploadingPanelKey.FAILED && (
+ {(panelKey === UploadingPanelKey.UPLOADING || panelKey === UploadingPanelKey.ALL) && (
+ UPLOADING_STATUSES.includes(item.status))
+ .map((item) => item.id)}
+ />
+ )}
+ {(panelKey === UploadingPanelKey.FAILED || panelKey === UploadingPanelKey.STOPPED) && (
item.id)} />
item.id)} />
diff --git a/apps/dcellar-web-ui/src/modules/upload/UploadingObjectsList.tsx b/apps/dcellar-web-ui/src/modules/upload/UploadingObjectsList.tsx
index 191cf48a..22060ca0 100644
--- a/apps/dcellar-web-ui/src/modules/upload/UploadingObjectsList.tsx
+++ b/apps/dcellar-web-ui/src/modules/upload/UploadingObjectsList.tsx
@@ -1,10 +1,5 @@
import { DCTable } from '@/components/common/DCTable';
-import {
- UPLOADING_STATUSES,
- UPLOAD_FAILED_STATUSES,
- UPLOAD_SUCCESS_STATUS,
- UploadObject,
-} from '@/store/slices/global';
+import { UploadObject } from '@/store/slices/global';
import { ColumnProps } from 'antd/es/table';
import React, { useState } from 'react';
import { NameItem } from './NameItem';
@@ -34,7 +29,6 @@ export const UploadingObjectsList = ({ data }: { data: UploadObject[] }) => {
size={record.waitObject.size}
msg={record.msg}
status={record.status}
- w={234}
task={record}
/>
);
@@ -72,7 +66,8 @@ export const UploadingObjectsList = ({ data }: { data: UploadObject[] }) => {
title: 'Action',
width: 146,
render: (record) => {
- if (UPLOADING_STATUSES.includes(record.status)) {
+ const { status, id } = record;
+ if (['SEAL', 'SEALING'].includes(status)) {
return (
--
@@ -80,15 +75,29 @@ export const UploadingObjectsList = ({ data }: { data: UploadObject[] }) => {
);
}
- if (UPLOAD_SUCCESS_STATUS === record.status) {
- return ;
- } else if (UPLOAD_FAILED_STATUSES.includes(record.status)) {
- return (
-
-
-
-
- );
+ switch (status) {
+ case 'FINISH':
+ return ;
+
+ case 'CANCEL':
+ case 'ERROR':
+ return (
+
+
+
+
+ );
+
+ case 'WAIT':
+ case 'HASH':
+ case 'HASHED':
+ case 'SIGN':
+ case 'SIGNED':
+ case 'UPLOAD':
+ return ;
+
+ default:
+ return null;
}
},
},
diff --git a/apps/dcellar-web-ui/src/modules/upload/useTaskManagementTab.tsx b/apps/dcellar-web-ui/src/modules/upload/useTaskManagementTab.tsx
index 19bcb443..1d63ff10 100644
--- a/apps/dcellar-web-ui/src/modules/upload/useTaskManagementTab.tsx
+++ b/apps/dcellar-web-ui/src/modules/upload/useTaskManagementTab.tsx
@@ -1,14 +1,15 @@
import { useAppSelector } from '@/store';
-import { UploadObject } from '@/store/slices/global';
+import { UPLOADING_STATUSES, UploadObject } from '@/store/slices/global';
import { sortBy } from 'lodash-es';
import { useMemo, useState } from 'react';
export enum UploadingPanelKey {
ALL = 'ALL',
- UPLOADING = 'HASH-UPLOAD-SEAL',
+ UPLOADING = 'RETRY-WAIT-HASH-UPLOAD-SIGN-SEAL',
+ STOPPED = 'CANCEL',
COMPLETE = 'FINISH',
- FAILED = 'ERROR-CANCEL',
+ FAILED = 'ERROR',
}
export const useTaskManagementTab = () => {
@@ -17,14 +18,14 @@ export const useTaskManagementTab = () => {
const queue = sortBy(objectUploadQueue[loginAccount] || [], (o) => o.waitObject.time);
- const { uploadingQueue, completeQueue, errorQueue } = useMemo(() => {
- const uploadingQueue = queue?.filter((i) =>
- ['HASH', 'UPLOAD', 'SEAL', 'SEALING'].includes(i.status),
- );
+ const { uploadingQueue, stoppedQueue, completeQueue, errorQueue } = useMemo(() => {
+ const uploadingQueue = queue?.filter((i) => UPLOADING_STATUSES.includes(i.status));
const completeQueue = queue?.filter((i) => i.status === 'FINISH');
- const errorQueue = queue?.filter((i) => ['ERROR', 'CANCEL'].includes(i.status));
+ const stoppedQueue = queue?.filter((i) => i.status === 'CANCEL');
+ const errorQueue = queue?.filter((i) => ['ERROR'].includes(i.status));
return {
uploadingQueue,
+ stoppedQueue,
completeQueue,
errorQueue,
};
@@ -46,6 +47,11 @@ export const useTaskManagementTab = () => {
key: UploadingPanelKey.UPLOADING,
data: uploadingQueue,
},
+ {
+ title: 'Stopped',
+ key: UploadingPanelKey.STOPPED,
+ data: stoppedQueue,
+ },
{
title: 'Complete',
key: UploadingPanelKey.COMPLETE,
diff --git a/apps/dcellar-web-ui/src/modules/wallet/TransferIn/index.tsx b/apps/dcellar-web-ui/src/modules/wallet/TransferIn/index.tsx
index c1d1a1f3..30bd1de0 100644
--- a/apps/dcellar-web-ui/src/modules/wallet/TransferIn/index.tsx
+++ b/apps/dcellar-web-ui/src/modules/wallet/TransferIn/index.tsx
@@ -107,59 +107,57 @@ export const TransferIn = memo(function TransferIn() {
};
return (
- <>
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
- >
+
);
});
diff --git a/apps/dcellar-web-ui/src/pages/_document.tsx b/apps/dcellar-web-ui/src/pages/_document.tsx
index e45554c8..e5bed808 100644
--- a/apps/dcellar-web-ui/src/pages/_document.tsx
+++ b/apps/dcellar-web-ui/src/pages/_document.tsx
@@ -26,7 +26,7 @@ export default function Document() {
__html: `window.__ASSET_PREFIX = ${flatted.stringify(assetPrefix)}`,
}}
>
-
+
diff --git a/apps/dcellar-web-ui/src/pages/api/tx/[[...slug]].ts b/apps/dcellar-web-ui/src/pages/api/tx/[[...slug]].ts
new file mode 100644
index 00000000..fc05aaae
--- /dev/null
+++ b/apps/dcellar-web-ui/src/pages/api/tx/[[...slug]].ts
@@ -0,0 +1,18 @@
+import { BILLING_API_URL, EXPLORER_API_URL } from '@/base/env';
+import axios from 'axios';
+import { NextApiRequest, NextApiResponse } from 'next';
+
+const handler = async (req: NextApiRequest, res: NextApiResponse) => {
+ const { slug } = req.query;
+ const slugs = slug as string[];
+ const url = `${EXPLORER_API_URL}/greenfield/tx/${slugs.join('/')}?page=1&per_page=100`;
+ try {
+ const { data } = await axios.get(url, { data: {} });
+ res.json(data);
+ } catch (e) {
+ console.error('tx', e);
+ res.json({});
+ }
+};
+
+export default handler;
diff --git a/apps/dcellar-web-ui/src/store/slices/bucket.ts b/apps/dcellar-web-ui/src/store/slices/bucket.ts
index 63005755..a565d4f5 100644
--- a/apps/dcellar-web-ui/src/store/slices/bucket.ts
+++ b/apps/dcellar-web-ui/src/store/slices/bucket.ts
@@ -6,11 +6,18 @@ import { PayloadAction, createSlice } from '@reduxjs/toolkit';
import { find, isEmpty, omit } from 'lodash-es';
import { SpEntity, setupPrimarySpInfo, setPrimarySpInfos } from './sp';
import { DEFAULT_TAG } from '@/components/common/ManageTags';
-import { getBucketReadQuota, getUserBucketMeta, getUserBuckets } from '@/facade/bucket';
+import {
+ getBucketActivities,
+ getBucketReadQuota,
+ getUserBucketMeta,
+ getUserBuckets,
+} from '@/facade/bucket';
import { AppDispatch, AppState, GetState } from '@/store';
import { setAuthModalOpen } from '@/store/slices/global';
import { getSpOffChainData } from '@/store/slices/persist';
import { convertObjectKey } from '@/utils/common';
+import { Activity } from './object';
+import { numberToHex } from 'viem';
export type BucketOperationsType =
| 'detail'
@@ -45,6 +52,7 @@ export interface BucketState {
bucketEditQuota: string[];
bucketOperation: Record<0 | 1, [string, BucketOperationsType]>;
bucketEditTagsData: ResourceTags_Tag[];
+ bucketActivityRecords: Record;
}
const initialState: BucketState = {
@@ -59,6 +67,7 @@ const initialState: BucketState = {
bucketEditQuota: ['', ''],
bucketOperation: { 0: ['', ''], 1: ['', ''] },
bucketEditTagsData: [DEFAULT_TAG],
+ bucketActivityRecords: {},
};
export const bucketSlice = createSlice({
@@ -139,6 +148,13 @@ export const bucketSlice = createSlice({
const { bucketName, paymentAddress } = payload;
state.bucketRecords[bucketName]['PaymentAddress'] = paymentAddress;
},
+ setBucketActivity(
+ state,
+ { payload }: PayloadAction<{ activities: Activity[]; bucketName: string }>,
+ ) {
+ const { bucketName, activities } = payload;
+ state.bucketActivityRecords[bucketName] = activities;
+ },
},
});
@@ -155,6 +171,7 @@ export const {
setBucketTags,
setBucketTagsEditData,
setBucketPaymentAccount,
+ setBucketActivity,
} = bucketSlice.actions;
const defaultBucketList = Array();
@@ -258,4 +275,10 @@ export const setupBucketQuota =
dispatch(setBucketQuota({ bucketName, quota }));
};
+export const setupBucketActivity =
+ (bucketName: string, id: string) => async (dispatch: AppDispatch) => {
+ const activities = await getBucketActivities(numberToHex(Number(id), { size: 32 }));
+ dispatch(setBucketActivity({ bucketName, activities }));
+ };
+
export default bucketSlice.reducer;
diff --git a/apps/dcellar-web-ui/src/store/slices/global.ts b/apps/dcellar-web-ui/src/store/slices/global.ts
index 697906cc..2526221a 100644
--- a/apps/dcellar-web-ui/src/store/slices/global.ts
+++ b/apps/dcellar-web-ui/src/store/slices/global.ts
@@ -105,6 +105,7 @@ export type UploadObject = {
msg: string;
progress: number;
delegateUpload?: boolean;
+ abortController?: AbortController;
};
export interface GlobalState {
@@ -175,14 +176,32 @@ export const globalSlice = createSlice({
state,
{
payload,
- }: PayloadAction<{ account: string; ids: number[]; status: UploadObject['status'] }>,
+ }: PayloadAction<{
+ account: string;
+ ids: number[];
+ status: UploadObject['status'];
+ extraFields?: Record>;
+ }>,
) {
- const { account, ids, status } = payload;
+ const { account, ids, status, extraFields } = payload;
const isErrorStatus = UPLOAD_FAILED_STATUSES.includes(status);
const queue = state.objectUploadQueue[account] || [];
- state.objectUploadQueue[account] = queue.map((q) =>
- ids.includes(q.id) ? { ...q, status, msg: isErrorStatus ? q.msg : '' } : q,
- );
+ state.objectUploadQueue[account] = queue.map((item) => {
+ if (ids.includes(item.id)) {
+ const updatedItem = { ...item, ...extraFields?.[item.id], status };
+
+ if (status === 'RETRY_CHECK') {
+ return { ...updatedItem, msg: '', progress: 0 };
+ }
+ if (isErrorStatus) {
+ return { ...updatedItem, msg: item.msg };
+ }
+
+ return updatedItem;
+ }
+
+ return item;
+ });
if (status === 'SEAL') {
ids.forEach((id) => {
@@ -222,15 +241,17 @@ export const globalSlice = createSlice({
task.status = 'ERROR';
task.msg = msg;
},
- updateUploadTaskMsg(
+ updateUploadTaskErrorMsg(
state,
- { payload }: PayloadAction<{ account: string; id: number; msg: string }>,
+ {
+ payload,
+ }: PayloadAction<{ account: string; id: number; msg: string; status?: UploadObjectStatus }>,
) {
- const { id, msg } = payload;
+ const { id, msg, status } = payload;
const task = find(state.objectUploadQueue[payload.account], (t) => t.id === id);
if (!task) return;
- task.status = 'ERROR';
- task.msg = msg;
+ task.status = task.status !== 'CANCEL' ? status ?? 'ERROR' : 'CANCEL';
+ task.msg = task.status !== 'CANCEL' ? msg : '';
},
updateWaitObjectStatus(
state,
@@ -429,7 +450,7 @@ export const {
setBnbUsdtExchangeRate,
updateWaitObjectStatus,
updateWaitTaskMsg,
- updateUploadTaskMsg,
+ updateUploadTaskErrorMsg,
updateUploadChecksum,
addToUploadQueue,
updateUploadStatus,
@@ -697,14 +718,25 @@ export const addDelegatedTasksToUploadQueue =
};
export const setupUploadTaskErrorMsg =
- ({ account, task, errorMsg }: { account: string; task: UploadObject; errorMsg: string }) =>
+ ({
+ account,
+ task,
+ errorMsg,
+ status,
+ }: {
+ account: string;
+ task: UploadObject;
+ errorMsg: string;
+ status?: UploadObjectStatus;
+ }) =>
async (dispatch: AppDispatch) => {
// const isFolder = task.waitObject.name.endsWith('/');
dispatch(
- updateUploadTaskMsg({
+ updateUploadTaskErrorMsg({
account,
id: task.id,
msg: errorMsg || 'The object failed to be created.',
+ status,
}),
);
// isFolder && dispatch(cancelUploadFolder({ account, folderName: task.waitObject.name }));
@@ -738,4 +770,15 @@ export const retryUploadTasks =
});
};
+export const cancelUploadingRequests =
+ ({ ids }: { ids: number[] }) =>
+ async (dispatch: AppDispatch, getState: GetState) => {
+ const { loginAccount } = getState().persist;
+ const uploadQueue = getState().global.objectUploadQueue[loginAccount] || _emptyUploadQueue;
+ const tasks = uploadQueue.filter((task) => ids.includes(task.id));
+ tasks.forEach(async (task) => {
+ task.abortController && task.abortController.abort();
+ });
+ };
+
export default globalSlice.reducer;
diff --git a/apps/dcellar-web-ui/src/store/slices/group.ts b/apps/dcellar-web-ui/src/store/slices/group.ts
index de404803..9e38ff78 100644
--- a/apps/dcellar-web-ui/src/store/slices/group.ts
+++ b/apps/dcellar-web-ui/src/store/slices/group.ts
@@ -6,8 +6,10 @@ import {
import { toast } from '@node-real/uikit';
import { DEFAULT_TAG } from '@/components/common/ManageTags';
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
-import { getGroupMembers, getGroups } from '@/facade/group';
+import { getGroupActivities, getGroupMembers, getGroups } from '@/facade/group';
import { AppDispatch, AppState, GetState } from '@/store';
+import { Activity } from './object';
+import { numberToHex } from 'viem';
export type GroupMember = {
AccountId: string;
@@ -41,6 +43,7 @@ interface GroupInitialState {
groupSelectedMembers: string[];
editTags: [string, string];
groupEditTagsData: ResourceTags_Tag[];
+ groupActivityRecords: Record;
}
const defaultGroupInfo: GroupInfo = {
owner: '',
@@ -61,6 +64,7 @@ const initialState: GroupInitialState = {
groupSelectedMembers: [],
editTags: ['', ''],
groupEditTagsData: [DEFAULT_TAG],
+ groupActivityRecords: {},
};
export const groupSlice = createSlice({
@@ -116,6 +120,13 @@ export const groupSlice = createSlice({
setGroupTagsEditData(state, { payload }: PayloadAction) {
state.groupEditTagsData = payload;
},
+ setGroupActivity(
+ state,
+ { payload }: PayloadAction<{ activities: Activity[]; groupName: string }>,
+ ) {
+ const { groupName, activities } = payload;
+ state.groupActivityRecords[groupName] = activities;
+ },
},
});
@@ -130,6 +141,7 @@ export const {
setGroupSelectedMembers,
setGroupTagsEditData,
setGroupTags,
+ setGroupActivity,
} = groupSlice.actions;
const defaultGroupList = Array();
@@ -184,4 +196,11 @@ export const setupGroupMembers =
return members;
};
+export const setupGroupActivity =
+ (groupName: string, id: string) => async (dispatch: AppDispatch) => {
+ const activities = await getGroupActivities(numberToHex(Number(id), { size: 32 }));
+
+ dispatch(setGroupActivity({ groupName, activities }));
+ };
+
export default groupSlice.reducer;
diff --git a/apps/dcellar-web-ui/src/store/slices/object.ts b/apps/dcellar-web-ui/src/store/slices/object.ts
index e52e0c75..6f90df21 100644
--- a/apps/dcellar-web-ui/src/store/slices/object.ts
+++ b/apps/dcellar-web-ui/src/store/slices/object.ts
@@ -22,7 +22,12 @@ import { numberToHex } from 'viem';
import { DEFAULT_TAG } from '@/components/common/ManageTags';
import { getFolderPolicies, getObjectPolicies } from '@/facade/bucket';
import { ErrorResponse } from '@/facade/error';
-import { ListObjectsParams, getListObjects, getObjectVersions } from '@/facade/object';
+import {
+ ListObjectsParams,
+ getListObjects,
+ getObjectActivities,
+ getObjectVersions,
+} from '@/facade/object';
import { AppDispatch, AppState, GetState } from '@/store';
import { convertObjectKey } from '@/utils/common';
import { getMillisecond } from '@/utils/time';
@@ -96,6 +101,31 @@ export type ObjectVersion = {
Version: number;
};
+export type Activity = {
+ hash: string;
+ height: number;
+ index: number;
+ code: number;
+ proof: {
+ data: any;
+ proof: any;
+ tx: any;
+ };
+ tx_result: {
+ code: number;
+ gas_used: number;
+ gas_wanted: number;
+ fee: string;
+ log: string;
+ messages: string;
+ type: string;
+ module: string;
+ };
+ time: string;
+ log: string;
+ messages: string;
+};
+
export interface ObjectState {
currentBucketName: string;
pathSegments: string[];
@@ -105,6 +135,7 @@ export interface ObjectState {
objectListTruncated: Record;
objectRecords: Record;
objectVersionRecords: Record;
+ objectActivityRecords: Record;
objectListPageRecords: Record;
objectListPageRestored: boolean;
objectSelectedKeys: Key[];
@@ -133,6 +164,7 @@ const initialState: ObjectState = {
objectListRecords: {},
objectRecords: {},
objectVersionRecords: {},
+ objectActivityRecords: {},
objectListPageRecords: {},
objectListPageRestored: true,
objectSelectedKeys: [],
@@ -397,6 +429,14 @@ export const objectSlice = createSlice({
const key = [state.currentBucketName, objectName].join('/');
state.objectVersionRecords[key] = versions;
},
+ setObjectActivity(
+ state,
+ { payload }: PayloadAction<{ activities: Activity[]; objectName: string }>,
+ ) {
+ const { objectName, activities } = payload;
+ const key = [state.currentBucketName, objectName].join('/');
+ state.objectActivityRecords[key] = activities;
+ },
},
});
@@ -428,6 +468,7 @@ export const {
setObjectTags,
setObjectEditTagsData,
setObjectVersion,
+ setObjectActivity,
} = objectSlice.actions;
export const selectPathLoading = (root: AppState) => {
@@ -600,4 +641,10 @@ export const setupObjectVersion =
dispatch(setObjectVersion({ objectName, versions }));
};
+export const setupObjectActivity =
+ (objectName: string, id: number) => async (dispatch: AppDispatch) => {
+ const activities = await getObjectActivities(numberToHex(Number(id), { size: 32 }));
+ dispatch(setObjectActivity({ objectName, activities }));
+ };
+
export default objectSlice.reducer;
diff --git a/apps/dcellar-web-ui/src/utils/object/index.ts b/apps/dcellar-web-ui/src/utils/object/index.ts
index 57117243..96ca4876 100644
--- a/apps/dcellar-web-ui/src/utils/object/index.ts
+++ b/apps/dcellar-web-ui/src/utils/object/index.ts
@@ -104,3 +104,14 @@ export const waitUploadFilterFn = (item: WaitObject) => {
export const errorUploadFilterFn = (item: WaitObject) => {
return item.status === 'ERROR' && !isUploadObjectUpdate(item);
};
+
+export const formatMsgType = (type: string) => {
+ const msgText = (type.split('.').pop() || '')
+ .replace(/^Msg/, '')
+ .match(/[A-Z0-9][a-z0-9]*/g)
+ ?.filter((item) => item !== 'V2')
+ .join(' ');
+ console.log(msgText);
+
+ return msgText;
+};
diff --git a/apps/dcellar-web-ui/src/utils/time.ts b/apps/dcellar-web-ui/src/utils/time.ts
index fdde58b0..ab2af74e 100644
--- a/apps/dcellar-web-ui/src/utils/time.ts
+++ b/apps/dcellar-web-ui/src/utils/time.ts
@@ -58,7 +58,7 @@ export const formatTime = (utcZeroTimestamp = 0) => {
};
export const formatFullTime = (
- utcZeroTimestamp = 0,
+ utcZeroTimestamp: number | string | Date | Dayjs | undefined,
format?: 'MMM D, YYYY HH:mm A' | 'YYYY-MM-DD HH:mm:ss',
) => {
if (!utcZeroTimestamp) {