From d76751dd3e0b458a16a8b21329a268f0f35a7f6c Mon Sep 17 00:00:00 2001 From: aidencao Date: Thu, 21 Mar 2024 17:40:31 +0800 Subject: [PATCH] feat(dcellar-web-ui): support create on chain folder & share virtual path feat(dcellar-web-ui): fix direct share error chore(dcellar-web-ui): update changelog --- apps/dcellar-web-ui/CHANGELOG.json | 12 ++ apps/dcellar-web-ui/CHANGELOG.md | 9 +- apps/dcellar-web-ui/package.json | 2 +- .../components/common/DCTable/ListEmpty.tsx | 4 +- .../layout/Header/Account/index.tsx | 13 +- .../components/CreateFolderOperation.tsx | 22 +++- .../object/components/CreateObject.tsx | 2 +- .../components/DeleteObjectOperation.tsx | 3 +- .../components/DetailFolderOperation.tsx | 113 +++++++++++++++--- .../modules/object/components/ObjectList.tsx | 83 ++++++++----- .../object/components/ObjectOperations.tsx | 4 +- .../object/components/SharePermission.tsx | 6 +- .../src/modules/object/constant.ts | 42 +++++++ apps/dcellar-web-ui/src/pages/share/index.tsx | 13 +- apps/dcellar-web-ui/src/utils/string.ts | 7 +- 15 files changed, 268 insertions(+), 67 deletions(-) diff --git a/apps/dcellar-web-ui/CHANGELOG.json b/apps/dcellar-web-ui/CHANGELOG.json index d27e4eb3..fae71c3d 100644 --- a/apps/dcellar-web-ui/CHANGELOG.json +++ b/apps/dcellar-web-ui/CHANGELOG.json @@ -1,6 +1,18 @@ { "name": "dcellar-web-ui", "entries": [ + { + "version": "0.4.0", + "tag": "dcellar-web-ui_v0.4.0", + "date": "Mon, 25 Mar 2024 04:02:13 GMT", + "comments": { + "minor": [ + { + "comment": "Support create on chain folder & share virtual path" + } + ] + } + }, { "version": "0.3.1", "tag": "dcellar-web-ui_v0.3.1", diff --git a/apps/dcellar-web-ui/CHANGELOG.md b/apps/dcellar-web-ui/CHANGELOG.md index 2ef316e6..6360cf5c 100644 --- a/apps/dcellar-web-ui/CHANGELOG.md +++ b/apps/dcellar-web-ui/CHANGELOG.md @@ -1,6 +1,13 @@ # Change Log - dcellar-web-ui -This log was last generated on Wed, 20 Mar 2024 07:14:31 GMT and should not be manually modified. +This log was last generated on Mon, 25 Mar 2024 04:02:13 GMT and should not be manually modified. + +## 0.4.0 +Mon, 25 Mar 2024 04:02:13 GMT + +### Minor changes + +- Support create on chain folder & share virtual path ## 0.3.1 Wed, 20 Mar 2024 07:14:31 GMT diff --git a/apps/dcellar-web-ui/package.json b/apps/dcellar-web-ui/package.json index d3fa57d1..7728b3d8 100644 --- a/apps/dcellar-web-ui/package.json +++ b/apps/dcellar-web-ui/package.json @@ -1,6 +1,6 @@ { "name": "dcellar-web-ui", - "version": "0.3.1", + "version": "0.4.0", "private": false, "scripts": { "dev": "node ./scripts/dev.js -p 3200", diff --git a/apps/dcellar-web-ui/src/components/common/DCTable/ListEmpty.tsx b/apps/dcellar-web-ui/src/components/common/DCTable/ListEmpty.tsx index 4581654d..ef371c67 100644 --- a/apps/dcellar-web-ui/src/components/common/DCTable/ListEmpty.tsx +++ b/apps/dcellar-web-ui/src/components/common/DCTable/ListEmpty.tsx @@ -1,12 +1,12 @@ import { IconFont } from '@/components/IconFont'; import styled from '@emotion/styled'; import { Box, Flex, Text } from '@node-real/uikit'; -import { PropsWithChildren, memo } from 'react'; +import { PropsWithChildren, memo, ReactNode } from 'react'; interface ListEmptyProps extends PropsWithChildren { empty: boolean; title: string; - desc: string; + desc: ReactNode; type: string; h?: number; } diff --git a/apps/dcellar-web-ui/src/components/layout/Header/Account/index.tsx b/apps/dcellar-web-ui/src/components/layout/Header/Account/index.tsx index a982c54b..42292ecf 100644 --- a/apps/dcellar-web-ui/src/components/layout/Header/Account/index.tsx +++ b/apps/dcellar-web-ui/src/components/layout/Header/Account/index.tsx @@ -5,6 +5,7 @@ import { PopoverContent, PopoverContentProps, PopoverTrigger, + Portal, } from '@node-real/uikit'; import { TransferEntry } from './TransferEntry'; import { Address } from './Address'; @@ -21,11 +22,13 @@ export const Account = () => {
- - - - - + + + + + + + ); }; diff --git a/apps/dcellar-web-ui/src/modules/object/components/CreateFolderOperation.tsx b/apps/dcellar-web-ui/src/modules/object/components/CreateFolderOperation.tsx index c8af3f6c..44ce685b 100644 --- a/apps/dcellar-web-ui/src/modules/object/components/CreateFolderOperation.tsx +++ b/apps/dcellar-web-ui/src/modules/object/components/CreateFolderOperation.tsx @@ -64,7 +64,7 @@ import { } from '@node-real/uikit'; import { useAsyncEffect, useUnmount } from 'ahooks'; import BigNumber from 'bignumber.js'; -import { isEmpty } from 'lodash-es'; +import { isEmpty, last, trimEnd } from 'lodash-es'; import { ChangeEvent, memo, useCallback, useEffect, useMemo, useState } from 'react'; import { useAccount } from 'wagmi'; import { TotalFees } from './TotalFees'; @@ -73,6 +73,7 @@ interface CreateFolderOperationProps { selectBucket: TBucket; bucketAccountDetail: AccountInfo; primarySp: SpEntity; + chainFolder?: string; refetch?: (name?: string) => void; onClose?: () => void; } @@ -80,6 +81,7 @@ interface CreateFolderOperationProps { export const CreateFolderOperation = memo(function CreateFolderDrawer({ refetch = () => {}, onClose = () => {}, + chainFolder: chainFolderName, selectBucket: bucket, bucketAccountDetail: accountDetail, primarySp, @@ -102,7 +104,8 @@ export const CreateFolderOperation = memo(function C const { settlementFee } = useSettlementFee(PaymentAddress); const [balanceEnough, setBalanceEnough] = useState(true); const [loading, setLoading] = useState(false); - const [inputFolderName, setInputFolderName] = useState(''); + const initFolderName = last(trimEnd(chainFolderName || '', '/').split('/')); + const [inputFolderName, setInputFolderName] = useState(initFolderName || ''); const [formErrors, setFormErrors] = useState([]); const [usedNames, setUsedNames] = useState([]); @@ -288,7 +291,7 @@ export const CreateFolderOperation = memo(function C errors.push('Cannot consist of slash(/).'); } const folderNames = folderList.map((folder) => folder.name); - if (folderNames.includes(value)) { + if (folderNames.includes(value) && !chainFolderName) { errors.push('Folder name already exists.'); } setFormErrors(errors); @@ -382,10 +385,16 @@ export const CreateFolderOperation = memo(function C return ( <> - Create a Folder + {chainFolderName ? 'Create on chain folder' : 'Create a Folder'} - Use folders to group objects in your bucket. Folder names can't contain - "/". + {chainFolderName ? ( + 'Convert your existing path to an on chain folder to view detailed data on the chain and obtain additional features.' + ) : ( + <> + Use folders to group objects in your bucket. Folder names can't contain + "/". + + )} @@ -396,6 +405,7 @@ export const CreateFolderOperation = memo(function C Name e.key === 'Enter' && onCreateFolder()} value={inputFolderName} onChange={onFolderNameChange} diff --git a/apps/dcellar-web-ui/src/modules/object/components/CreateObject.tsx b/apps/dcellar-web-ui/src/modules/object/components/CreateObject.tsx index 6bef772d..f938b30e 100644 --- a/apps/dcellar-web-ui/src/modules/object/components/CreateObject.tsx +++ b/apps/dcellar-web-ui/src/modules/object/components/CreateObject.tsx @@ -98,7 +98,7 @@ export const CreateObject = memo(function NewObject({ ); - const folderExist = !objectCommonPrefix ? true : !!objectRecords[completeCommonPrefix + '/']; + const folderExist = !objectCommonPrefix || !!objectRecords[completeCommonPrefix + '/']; const invalidPath = pathSegments.some((name) => new Blob([name]).size > MAX_FOLDER_NAME_LEN) || !folderExist; diff --git a/apps/dcellar-web-ui/src/modules/object/components/DeleteObjectOperation.tsx b/apps/dcellar-web-ui/src/modules/object/components/DeleteObjectOperation.tsx index f3371afd..9083157e 100644 --- a/apps/dcellar-web-ui/src/modules/object/components/DeleteObjectOperation.tsx +++ b/apps/dcellar-web-ui/src/modules/object/components/DeleteObjectOperation.tsx @@ -117,7 +117,8 @@ export const DeleteObjectOperation = memo( ); return ( GfSpListObjectsByBucketNameResponse.KeyCount === '1' && - GfSpListObjectsByBucketNameResponse.Objects[0].ObjectInfo.ObjectName === objectName + // virtual path + GfSpListObjectsByBucketNameResponse.Objects[0]?.ObjectInfo.ObjectName === objectName ); }; diff --git a/apps/dcellar-web-ui/src/modules/object/components/DetailFolderOperation.tsx b/apps/dcellar-web-ui/src/modules/object/components/DetailFolderOperation.tsx index 3e61e283..da6b0e3d 100644 --- a/apps/dcellar-web-ui/src/modules/object/components/DetailFolderOperation.tsx +++ b/apps/dcellar-web-ui/src/modules/object/components/DetailFolderOperation.tsx @@ -16,10 +16,15 @@ import { convertObjectKey } from '@/utils/common'; import { formatId } from '@/utils/string'; import { formatFullTime } from '@/utils/time'; import { ResourceTags_Tag } from '@bnb-chain/greenfield-cosmos-types/greenfield/storage/types'; -import { Divider, Flex, QDrawerBody, QDrawerHeader, Text } from '@node-real/uikit'; +import { Box, Divider, Flex, QDrawerBody, QDrawerHeader, Text } from '@node-real/uikit'; import { useMount, useUnmount } from 'ahooks'; import { last } from 'lodash-es'; -import { memo } from 'react'; +import { memo, useMemo, useState } from 'react'; +import { MOCK_EMPTY_FOLDER_OBJECT } from '@/modules/object/constant'; +import { ObjectMeta } from '@bnb-chain/greenfield-js-sdk/dist/esm/types/sp/Common'; +import { DCLink } from '@/components/common/DCLink'; +import { Tips } from '@/components/common/Tips'; +import { DCButton } from '@/components/common/DCButton'; interface DetailFolderOperationProps { objectName: string; @@ -32,13 +37,28 @@ export const DetailFolderOperation = memo( const dispatch = useAppDispatch(); const completeCommonPrefix = useAppSelector((root) => root.object.completeCommonPrefix); const objectRecords = useAppSelector((root) => root.object.objectRecords); + // const objectOperation = useAppSelector((root) => root.object.objectOperation); + // const [id, operation, params] = objectOperation[1]; + // const preOperation = usePrevious(operation); + // const preObjectName = usePrevious(params?.objectName); - const selectObjectInfo = useModalValues( - objectRecords[[selectBucket.BucketName, objectName].join('/')] || {}, + const cacheKey = [selectBucket.BucketName, objectName].join('/'); + const mockMeta: ObjectMeta = useMemo( + () => ({ + ...MOCK_EMPTY_FOLDER_OBJECT, + ObjectInfo: { + ...MOCK_EMPTY_FOLDER_OBJECT.ObjectInfo, + ObjectName: objectName, + BucketName: selectBucket.BucketName, + }, + }), + [objectName, selectBucket.BucketName], ); + const selectObjectInfo = useModalValues(objectRecords[cacheKey] || mockMeta); const objectInfo = useModalValues(selectObjectInfo.ObjectInfo); + const [folderExist, setFolderExist] = useState(true); const folderName = last(objectName.replace(/\/$/, '').split('/')); - const loading = !objectInfo; + const loading = !(cacheKey in objectRecords) && folderExist; const onEditTags = () => { const lowerKeyTags = selectObjectInfo.ObjectInfo?.Tags?.Tags.map((item) => @@ -53,7 +73,7 @@ export const DetailFolderOperation = memo( ); }; - useMount(async () => { + const getFolderObjectList = async () => { const _query = new URLSearchParams(); _query.append('delimiter', '/'); _query.append('maxKeys', '2'); @@ -70,16 +90,24 @@ export const DetailFolderOperation = memo( const [res, error] = await getListObjects(params); // should never happen - if (error || !res || res.code !== 0) return false; + if (error || !res || res.code !== 0) return; const { GfSpListObjectsByBucketNameResponse } = res.body!; + const list = GfSpListObjectsByBucketNameResponse!; // 更新文件夹objectInfo - dispatch( - setObjectList({ - path: completeCommonPrefix, - list: GfSpListObjectsByBucketNameResponse || [], - infoOnly: true, - }), - ); + dispatch(setObjectList({ path: completeCommonPrefix, list, infoOnly: true })); + return list; + }; + + // useAsyncEffect(async () => { + // if (preObjectName !== objectInfo.ObjectName || preOperation !== 'create_folder') return; + // await getFolderObjectList(); + // }, [preOperation, preObjectName, objectInfo.ObjectName]); + + useMount(async () => { + const list = await getFolderObjectList(); + if (!list) return; + // virtual path + setFolderExist(list.Objects[0]?.ObjectInfo.ObjectName.endsWith('/') || false); }); useUnmount(() => dispatch(setObjectEditTagsData([DEFAULT_TAG]))); @@ -101,13 +129,64 @@ export const DetailFolderOperation = memo( > {folderName} - - -- + + {folderExist ? ( + '--' + ) : ( + + This is a folder simulated by a path.{' '} + + + { + "This path doesn't exist as an entity on the blockchain and lacks chain information." + } + + + Learn more + + + } + /> + + )} - + + {!folderExist && ( + + + dispatch( + setObjectOperation({ + operation: ['', 'create_folder', { objectName: objectInfo.ObjectName }], + }), + ) + } + position={'absolute'} + right={24} + top={70} + > + Create on chain folder + + + )} {renderPropRow( 'Date created', loading ? '' : formatFullTime(+objectInfo.CreateAt * 1000), diff --git a/apps/dcellar-web-ui/src/modules/object/components/ObjectList.tsx b/apps/dcellar-web-ui/src/modules/object/components/ObjectList.tsx index d64e193b..6082ac5d 100644 --- a/apps/dcellar-web-ui/src/modules/object/components/ObjectList.tsx +++ b/apps/dcellar-web-ui/src/modules/object/components/ObjectList.tsx @@ -50,13 +50,14 @@ import { formatBytes } from '@/utils/formatter'; import { pickAction, removeAction } from '@/utils/object'; import { apolloUrlTemplate } from '@/utils/string'; import { formatTime, getMillisecond } from '@/utils/time'; -import { Flex } from '@node-real/uikit'; +import { Box, Flex } from '@node-real/uikit'; import { useAsyncEffect, useUpdateEffect } from 'ahooks'; import { ColumnProps } from 'antd/es/table'; import dayjs from 'dayjs'; import { find, uniq, without, xor } from 'lodash-es'; import { memo, useCallback } from 'react'; import { OBJECT_ERROR_TYPES, ObjectErrorType } from '../ObjectError'; +import Link from 'next/link'; // import { ManageObjectTagsDrawer } from './ManageObjectTagsDrawer'; export type ObjectActionValueType = @@ -127,6 +128,7 @@ export const ObjectList = memo(function ObjectList({ shareMode const bucket = bucketRecords[currentBucketName]; const accountDetail = useAppSelector(selectAccount(bucket?.PaymentAddress)); + const currentPathExist = !objectCommonPrefix || !!objectRecords[completeCommonPrefix + '/']; const filtered = !!objectNameFilter.trim() || objectTypeFilter.length || @@ -197,33 +199,45 @@ export const ObjectList = memo(function ObjectList({ shareMode const empty = !loading && !sortedList.length; const loadingComponent = { spinning: loading, indicator: }; - const renderEmpty = useCallback( - () => ( - - {!filtered && !shareMode && } + const renderEmpty = useCallback(() => { + const type = isBucketDiscontinue && !filtered ? 'discontinue' : 'empty-object'; + const title = (() => { + if (filtered || shareMode) return 'No Results'; + if (isBucketDiscontinue) return 'Discontinue Notice'; + if (!currentPathExist && isBucketOwner) return 'No Objects Under This Path'; + return 'Upload Objects and Start Your Work Now'; + })(); + + const desc = (() => { + if (filtered || shareMode) return 'No results found. Please try different conditions.'; + if (isBucketDiscontinue) + return 'This bucket were marked as discontinued and will be deleted by SP soon. '; + if (!currentPathExist && isBucketOwner) + return ( + + The path no longer exists on DCellar. You can{' '} + return to the bucket list and + continue your work. + + ); + return `To avoid data loss during testnet phase, the file size should not exceed ${formatBytes( + SINGLE_OBJECT_MAX_SIZE, + )}.`; + })(); + return ( + + {!filtered && !shareMode && currentPathExist && } - ), - [isBucketDiscontinue, empty, filtered, shareMode], - ); + ); + }, [ + isBucketDiscontinue, + isBucketOwner, + empty, + filtered, + shareMode, + currentPathExist, + currentBucketName, + ]); const columns: ColumnProps[] = [ { @@ -481,6 +495,15 @@ export const ObjectList = memo(function ObjectList({ shareMode openLink(link); return; } + + const forVirtualPath = { + ObjectInfo: { + BucketName: record.bucketName, + ObjectName: record.objectName, + Visibility: record.visibility, + }, + }; + switch (menu) { case 'detail': case 'delete': @@ -503,7 +526,11 @@ export const ObjectList = memo(function ObjectList({ shareMode return dispatch( setObjectOperation({ level: 1, - operation: [`${record.bucketName}/${record.objectName}`, menu], + operation: [ + `${record.bucketName}/${record.objectName}`, + menu, + record.objectName.endsWith('/') ? { selectObjectInfo: forVirtualPath } : {}, + ], }), ); } diff --git a/apps/dcellar-web-ui/src/modules/object/components/ObjectOperations.tsx b/apps/dcellar-web-ui/src/modules/object/components/ObjectOperations.tsx index a5ef1cf7..1a0b296d 100644 --- a/apps/dcellar-web-ui/src/modules/object/components/ObjectOperations.tsx +++ b/apps/dcellar-web-ui/src/modules/object/components/ObjectOperations.tsx @@ -46,7 +46,8 @@ export const ObjectOperations = memo(function ObjectOpera const [id, operation, params] = objectOperation[level]; const bucketName = params?.bucketName || currentBucketName; const _operation = useModalValues(operation); - const selectObjectInfo = objectRecords[id] || {}; + // params?.selectObjectInfo for offchain folder mock data + const selectObjectInfo = objectRecords[id] || params?.selectObjectInfo || {}; const _selectObjectInfo = useModalValues(selectObjectInfo); const { BucketName } = _selectObjectInfo.ObjectInfo || {}; const selectBucket = useModalValues(bucketRecords[BucketName || bucketName] || {}); @@ -130,6 +131,7 @@ export const ObjectOperations = memo(function ObjectOpera case 'create_folder': return ( (function SharePermissi dispatch( setObjectOperation({ level: 1, - operation: [`${bucketName}/${objectInfo.ObjectName}`, 'share'], + operation: [ + `${bucketName}/${objectInfo.ObjectName}`, + 'share', + { selectObjectInfo }, + ], }), ); }} diff --git a/apps/dcellar-web-ui/src/modules/object/constant.ts b/apps/dcellar-web-ui/src/modules/object/constant.ts index b4eb8f13..71da7677 100644 --- a/apps/dcellar-web-ui/src/modules/object/constant.ts +++ b/apps/dcellar-web-ui/src/modules/object/constant.ts @@ -1,3 +1,5 @@ +import { ObjectMeta } from '@bnb-chain/greenfield-js-sdk/dist/esm/types/sp/Common'; + export const GAS_FEE_DOC = 'https://docs.nodereal.io/docs/dcellar-faq#fee-related'; export const PREPAID_FEE_DOC = 'https://docs.nodereal.io/docs/dcellar-faq#fee-related'; export const SETTLEMENT_FEE_DOC = 'https://docs.nodereal.io/docs/dcellar-faq#fee-related'; @@ -44,6 +46,46 @@ const AUTH_EXPIRED = 'Authentication Expired'; const WALLET_CONFIRM = 'Please confirm the transaction in your wallet.'; export const EMPTY_TX_HASH = '0x0000000000000000000000000000000000000000000000000000000000000000'; + +export const MOCK_EMPTY_FOLDER_OBJECT: ObjectMeta = { + ObjectInfo: { + Owner: '0xDB8040c64d24840BD1D6BcAC7112D2A143CC2EEa', + Creator: '0xDB8040c64d24840BD1D6BcAC7112D2A143CC2EEa', + BucketName: '', + ObjectName: '', + Id: 0, + LocalVirtualGroupId: 0, + PayloadSize: 0, + Visibility: 3, + ContentType: 'text/plain', + CreateAt: 1711002963, + ObjectStatus: 1, + RedundancyType: 0, + SourceType: 0, + Checksums: [ + 'Xfbg4nYTWdMKgnUFjimfzAOBU0VF9Vz0PkGYP11MlFY=', + 'Xfbg4nYTWdMKgnUFjimfzAOBU0VF9Vz0PkGYP11MlFY=', + 'Xfbg4nYTWdMKgnUFjimfzAOBU0VF9Vz0PkGYP11MlFY=', + 'Xfbg4nYTWdMKgnUFjimfzAOBU0VF9Vz0PkGYP11MlFY=', + 'Xfbg4nYTWdMKgnUFjimfzAOBU0VF9Vz0PkGYP11MlFY=', + 'Xfbg4nYTWdMKgnUFjimfzAOBU0VF9Vz0PkGYP11MlFY=', + 'Xfbg4nYTWdMKgnUFjimfzAOBU0VF9Vz0PkGYP11MlFY=', + ], + Tags: { + Tags: [], + }, + }, + LockedBalance: '0x0000000000000000000000000000000000000000000000000000000000000000', + Removed: false, + UpdateAt: 3075455, + DeleteAt: 0, + DeleteReason: '', + Operator: '0x0000000000000000000000000000000000000000', + CreateTxHash: '0x0000000000000000000000000000000000000000000000000000000000000000', + UpdateTxHash: '0x0000000000000000000000000000000000000000000000000000000000000000', + SealTxHash: '0x0000000000000000000000000000000000000000000000000000000000000000', +}; + export { AUTH_EXPIRED, BUTTON_GOT_IT, diff --git a/apps/dcellar-web-ui/src/pages/share/index.tsx b/apps/dcellar-web-ui/src/pages/share/index.tsx index b1c3ec83..b618acf5 100644 --- a/apps/dcellar-web-ui/src/pages/share/index.tsx +++ b/apps/dcellar-web-ui/src/pages/share/index.tsx @@ -108,7 +108,11 @@ const SharePage: NextPage = (props) => { }; if (!loginAccount) { - const objectInfo = await headObject(bucketName, objectName); + let objectInfo = await headObject(bucketName, objectName); + // for virtual path + if (objectName.endsWith('/') && !objectInfo) { + objectInfo = { bucketName, objectName } as ObjectInfo; + } setObjectInfo(objectInfo); setQuotaData({} as IQuotaProps); return; @@ -120,7 +124,12 @@ const SharePage: NextPage = (props) => { ) { logout(true); } - setObjectInfo(objectInfo); + let _objectInfo = objectInfo; + // for virtual path + if (objectName.endsWith('/') && !objectInfo) { + _objectInfo = { bucketName, objectName } as ObjectInfo; + } + setObjectInfo(_objectInfo); setQuotaData(quotaData || ({} as IQuotaProps)); }, [specifiedSp, walletConnected]); diff --git a/apps/dcellar-web-ui/src/utils/string.ts b/apps/dcellar-web-ui/src/utils/string.ts index 34668535..e8ac7ce9 100644 --- a/apps/dcellar-web-ui/src/utils/string.ts +++ b/apps/dcellar-web-ui/src/utils/string.ts @@ -60,6 +60,7 @@ export const trimAddress = ( return trimLongStr(formatAddress(address), maxLength, headLen, footLen); }; +// todo emoji characters encoding // encodeURIComponent() uses the same encoding algorithm as described in encodeURI(). It escapes all characters except: // A–Z a–z 0–9 - _ . ! ~ * ' ( ) export const encodeObjectName = (pathName: string) => { @@ -98,7 +99,11 @@ export const encodeObjectName = (pathName: string) => { encodedPathName += '%' + hexStr.toUpperCase(); } else { // others characters - encodedPathName += encodeURI(s); + try { + encodedPathName += encodeURI(s); + } catch (e) { + encodedPathName += s; + } } } }