diff --git a/apps/dcellar-web-ui/public/images/icons/refresh.svg b/apps/dcellar-web-ui/public/images/icons/refresh.svg
new file mode 100644
index 00000000..541365c5
--- /dev/null
+++ b/apps/dcellar-web-ui/public/images/icons/refresh.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/apps/dcellar-web-ui/src/modules/object/components/CreateFolder.tsx b/apps/dcellar-web-ui/src/modules/object/components/CreateFolder.tsx
index 2ecf95ea..6dc162da 100644
--- a/apps/dcellar-web-ui/src/modules/object/components/CreateFolder.tsx
+++ b/apps/dcellar-web-ui/src/modules/object/components/CreateFolder.tsx
@@ -54,7 +54,6 @@ import { useChecksumApi } from '@/modules/checksum';
import { resolve } from '@/facade/common';
import { DCDrawer } from '@/components/common/DCDrawer';
import { TStatusDetail, setEditCreate, setStatusDetail } from '@/store/slices/object';
-import { duplicateName } from '@/utils/object';
import { setupTmpAvailableBalance } from '@/store/slices/global';
import { useOffChainAuth } from '@/hooks/useOffChainAuth';
import { getObjectMeta } from '@/facade/object';
@@ -235,7 +234,8 @@ export const CreateFolder = memo(function CreateFolderDrawer({ refet
if (value.includes('/')) {
errors.push('Cannot consist of slash(/).');
}
- if (duplicateName(value, folderList)) {
+ const folderNames = folderList.map((folder) => folder.name);
+ if (folderNames.includes(value)) {
errors.push('Folder name already exists.');
}
setFormErrors(errors);
diff --git a/apps/dcellar-web-ui/src/modules/object/components/NewObject.tsx b/apps/dcellar-web-ui/src/modules/object/components/NewObject.tsx
index 4dd7b4b6..27dd1336 100644
--- a/apps/dcellar-web-ui/src/modules/object/components/NewObject.tsx
+++ b/apps/dcellar-web-ui/src/modules/object/components/NewObject.tsx
@@ -1,13 +1,24 @@
import React, { ChangeEvent, memo } from 'react';
import { useAppDispatch, useAppSelector } from '@/store';
import { GAClick } from '@/components/common/GATracker';
-import { Button, Flex, Menu, MenuButton, MenuItem, MenuList, Text, Tooltip } from '@totejs/uikit';
+import {
+ Box,
+ Button,
+ Flex,
+ Menu,
+ MenuButton,
+ MenuItem,
+ MenuList,
+ Text,
+ Tooltip,
+} from '@totejs/uikit';
import UploadIcon from '@/public/images/files/upload_transparency.svg';
-import { setEditCreate, setEditUploadStatus } from '@/store/slices/object';
+import { setEditCreate, setEditUploadStatus, setListRefreshing, setRestoreCurrent, setupListObjects } from '@/store/slices/object';
import { addToWaitQueue } from '@/store/slices/global';
import { getUtcZeroTimestamp } from '@bnb-chain/greenfield-chain-sdk';
import { MenuCloseIcon, MenuOpenIcon } from '@totejs/icons';
-
+import RefreshIcon from '@/public/images/icons/refresh.svg';
+import { getSpOffChainData } from '@/store/slices/persist';
interface NewObjectProps {
gaFolderClickName?: string;
gaUploadClickName?: string;
@@ -22,7 +33,10 @@ export const NewObject = memo(function NewObject({
}) {
const dispatch = useAppDispatch();
const { discontinue, owner } = useAppSelector((root) => root.bucket);
- const { folders, prefix, path, objectsInfo } = useAppSelector((root) => root.object);
+ const { folders, prefix, path, objectsInfo, bucketName} = useAppSelector((root) => root.object);
+ const { loginAccount } = useAppSelector((root) => root.persist);
+ const { primarySpInfo } = useAppSelector((root) => root.sp);
+ const primarySp = primarySpInfo[bucketName];
const onOpenCreateFolder = () => {
if (disabled) return;
dispatch(setEditCreate(true));
@@ -53,8 +67,27 @@ export const NewObject = memo(function NewObject({
e.target.value = '';
};
+ const refreshList = async () => {
+ const { seedString } = await dispatch(
+ getSpOffChainData(loginAccount, primarySp.operatorAddress),
+ );
+ const query = new URLSearchParams();
+ const params = {
+ seedString,
+ query,
+ endpoint: primarySp.endpoint,
+ };
+ dispatch(setListRefreshing(true));
+ dispatch(setRestoreCurrent(false));
+ await dispatch(setupListObjects(params));
+ dispatch(setListRefreshing(false));
+ }
+
return (
+ refreshList()} alignItems='center' height={40} marginRight={12} cursor='pointer'>
+
+
{
);
const selectedFiles = waitQueue;
const objectList = objects[path]?.filter((item) => !item.objectName.endsWith('/'));
+ const {uploadQueue} = useAppSelector((root) => root.global);
const [creating, setCreating] = useState(false);
const { tabOptions, activeKey, setActiveKey } = useTab();
@@ -110,7 +110,9 @@ export const UploadObjects = () => {
if (file.name.includes('//')) {
return E_OBJECT_NAME_CONTAINS_SLASH;
}
- if (duplicateName(file.name, objectList)) {
+ const objectListNames = objectList.map((item) => item.objectName);
+ const uploadingNames = (uploadQueue?.[loginAccount] || []).map((item) => item.file.name);
+ if ([...objectListNames, ...uploadingNames].includes(file.name)) {
return E_OBJECT_NAME_EXISTS;
}
return '';
diff --git a/apps/dcellar-web-ui/src/store/slices/global.ts b/apps/dcellar-web-ui/src/store/slices/global.ts
index a2c59479..281e70a5 100644
--- a/apps/dcellar-web-ui/src/store/slices/global.ts
+++ b/apps/dcellar-web-ui/src/store/slices/global.ts
@@ -301,7 +301,6 @@ export const setupPreLockFeeObjects = (primarySpAddress: string) => async (dispa
} = (storageParams && storageParams.versionedParams) || {};
const { params: paymentParams } = await client.payment.params();
const { reserveTime, validatorTaxRate } = paymentParams?.versionedParams || {};
-
const lockFeeParamsPayload = {
spStorageStorePrice: spStoragePrice?.storePrice || '',
secondarySpStorePrice: secondarySpStoragePrice?.storePrice || '',
diff --git a/apps/dcellar-web-ui/src/store/slices/object.ts b/apps/dcellar-web-ui/src/store/slices/object.ts
index 3fba49a9..83d68724 100644
--- a/apps/dcellar-web-ui/src/store/slices/object.ts
+++ b/apps/dcellar-web-ui/src/store/slices/object.ts
@@ -55,6 +55,7 @@ export interface ObjectState {
statusDetail: TStatusDetail;
editUpload: TEditUpload;
deletedObjects: Record;
+ refreshing: boolean;
}
const initialState: ObjectState = {
@@ -76,6 +77,7 @@ const initialState: ObjectState = {
statusDetail: {} as TStatusDetail,
editUpload: {} as TEditUpload,
deletedObjects: {},
+ refreshing: false,
};
export const objectSlice = createSlice({
@@ -242,6 +244,9 @@ export const objectSlice = createSlice({
state.objectsMeta[path] = omit(list, ['objects', 'common_prefixes']);
state.objects[path] = folders.concat(objects as ObjectItem[]);
},
+ setListRefreshing(state, { payload }: PayloadAction) {
+ state.refreshing = payload;
+ }
},
});
@@ -296,10 +301,6 @@ export const setupListObjects =
const { prefix, bucketName, path, restoreCurrent } = getState().object;
const { loginAccount: address } = getState().persist;
- dispatch(setRestoreCurrent(true));
- if (!restoreCurrent) {
- dispatch(setCurrentObjectPage({ path, current: 0 }));
- }
const _query = new URLSearchParams(params.query?.toString() || '');
_query.append('max-keys', '1000');
_query.append('delimiter', '/');
@@ -314,13 +315,18 @@ export const setupListObjects =
return;
}
dispatch(setObjectList({ path: _path || path, list: res! }));
+ dispatch(setRestoreCurrent(true));
+ if (!restoreCurrent) {
+ dispatch(setCurrentObjectPage({ path, current: 0 }));
+ }
};
+
export const closeStatusDetail = () => async (dispatch: AppDispatch) => {
dispatch(setStatusDetail({} as TStatusDetail));
};
export const selectPathLoading = (root: AppState) => {
- const { objects, path } = root.object;
- return !(path in objects);
+ const { objects, path, refreshing } = root.object;
+ return !(path in objects) || refreshing;
};
export const selectPathCurrent = (root: AppState) => {
@@ -352,6 +358,7 @@ export const {
setDummyFolder,
updateObjectVisibility,
addDeletedObject,
+ setListRefreshing,
} = objectSlice.actions;
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 2c7ec46b..1df7846a 100644
--- a/apps/dcellar-web-ui/src/utils/object/index.ts
+++ b/apps/dcellar-web-ui/src/utils/object/index.ts
@@ -6,10 +6,6 @@ export type TReverseVisibilityType = {
[K in number]: TKey;
};
-export const duplicateName = (name: string, objects: ObjectItem[]) => {
- return objects.some((item) => item.name === name);
-}
-
const StringIsNumber = (value: string) => isNaN(Number(value)) === false;
export const convertVisibility = () => {
diff --git a/apps/dcellar-web-ui/src/utils/sp/index.ts b/apps/dcellar-web-ui/src/utils/sp/index.ts
index e9e35da4..9e69c5cc 100644
--- a/apps/dcellar-web-ui/src/utils/sp/index.ts
+++ b/apps/dcellar-web-ui/src/utils/sp/index.ts
@@ -53,21 +53,15 @@ export const calPreLockFee = ({ size, preLockFeeObject }: { size: number; primar
} = preLockFeeObject;
const chargeSize = size >= minChargeSize ? size : minChargeSize;
- const lockedFeeRate = BigNumber(spStorageStorePrice)
- .plus(
- BigNumber(secondarySpStorePrice).times(
- redundantDataChunkNum + redundantParityChunkNum,
- ),
- )
- .times(BigNumber(chargeSize))
- .times(BigNumber(validatorTaxRate).dividedBy(Math.pow(10, 18)))
- .dividedBy(Math.pow(10, 18));
- const lockFeeInBNB = lockedFeeRate
- .times(BigNumber(reserveTime || 0))
- .dividedBy(Math.pow(10, 18));
+ const primarySpRate = BigNumber(spStorageStorePrice).dividedBy(Math.pow(10, 18)).times(BigNumber(chargeSize));
+ const secondarySpNum = redundantDataChunkNum + redundantParityChunkNum;
+ let secondarySpRate = BigNumber(secondarySpStorePrice).dividedBy(Math.pow(10, 18)).times(BigNumber(chargeSize));
+ secondarySpRate = secondarySpRate.times(secondarySpNum);
+ const validatorTax = BigNumber(validatorTaxRate).dividedBy(Math.pow(10, 18)).times(primarySpRate.plus(secondarySpRate));
+ const rate = primarySpRate.plus(secondarySpRate).plus(validatorTax);
+ const lockFeeInBNB = rate.times(BigNumber(reserveTime || 0)).dividedBy(Math.pow(10, 18));
return lockFeeInBNB.toString()
-
}
const checkZkWasm = (attempts: number = 5): Promise=> {
return new Promise((resolve) => {