From 62447b41607a4049a2cf5a71c50e221022e42e88 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Tue, 21 May 2024 16:54:21 +0300 Subject: [PATCH 01/15] add a custom network input --- src/appConstants/index.ts | 1 + src/assets/scss/components/_components.scss | 1 + .../CustomNetwork/CustomNetworkInput.tsx | 69 +++++++++++++++++++ .../CustomNetwork/CustomNetworkMenu.tsx | 24 +++++++ .../CustomNetwork/customNetwork.styles.scss | 9 +++ src/components/CustomNetwork/index.ts | 2 + src/components/index.ts | 1 + .../helpers/getStorageCustomNetworks.ts | 23 +++++++ src/config/helpers/index.ts | 1 + src/helpers/storage.ts | 6 +- 10 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 src/components/CustomNetwork/CustomNetworkInput.tsx create mode 100644 src/components/CustomNetwork/CustomNetworkMenu.tsx create mode 100644 src/components/CustomNetwork/customNetwork.styles.scss create mode 100644 src/components/CustomNetwork/index.ts create mode 100644 src/config/helpers/getStorageCustomNetworks.ts diff --git a/src/appConstants/index.ts b/src/appConstants/index.ts index 5d13d1832..ce39d6946 100644 --- a/src/appConstants/index.ts +++ b/src/appConstants/index.ts @@ -24,6 +24,7 @@ export const AUCTION_LIST_MIN_DISPLAY_ROW_COUNT = 6; export const LEGACY_DELEGATION_NODES_IDENTITY = 'multiversx'; export const HEROTAG_SUFFIX = '.elrond'; export const TEMP_LOCAL_NOTIFICATION_DISMISSED = 'tempNotificationDismissed2'; +export const LOCAL_STORAGE_CUSTOM_NETWORK = 'customNetwork'; export const NEW_VERSION_NOTIFICATION = 'newExplorerVersion'; export const SC_INIT_CHARACTERS_LENGTH = 13; diff --git a/src/assets/scss/components/_components.scss b/src/assets/scss/components/_components.scss index 862d01f48..22e6191aa 100644 --- a/src/assets/scss/components/_components.scss +++ b/src/assets/scss/components/_components.scss @@ -2,6 +2,7 @@ @import '../../../components/BlocksTable/blocksTable.styles.scss'; @import '../../../components/Cards/cards.styles.scss'; @import '../../../components/Chart/chart.styles.scss'; +@import '../../../components/CustomNetwork/customNetwork.styles.scss'; @import '../../../components/DataDecode/dataDecode.styles.scss'; @import '../../../components/DetailItem/detailItem.styles.scss'; @import '../../../components/FormatValue/formatValue.styles.scss'; diff --git a/src/components/CustomNetwork/CustomNetworkInput.tsx b/src/components/CustomNetwork/CustomNetworkInput.tsx new file mode 100644 index 000000000..d8f2c20f7 --- /dev/null +++ b/src/components/CustomNetwork/CustomNetworkInput.tsx @@ -0,0 +1,69 @@ +import React, { useEffect, useState } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import classNames from 'classnames'; +import { useNavigate, useLocation } from 'react-router-dom'; + +import { faCircleNotch, faCheck } from 'icons/regular'; +import { WithClassnameType } from 'types'; + +export const CustomNetworkInput = ({ className }: WithClassnameType) => { + const [customNetworkHash, setcustomNetworkHash] = useState(''); + const [isSaving, setIsSaving] = useState(false); + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.preventDefault(); + // customNetwork(); + } + }; + + const handleChange = (e: React.ChangeEvent) => { + setcustomNetworkHash(e.target.value); + }; + + return ( +
+
+ + +
+
+ ); +}; diff --git a/src/components/CustomNetwork/CustomNetworkMenu.tsx b/src/components/CustomNetwork/CustomNetworkMenu.tsx new file mode 100644 index 000000000..350615025 --- /dev/null +++ b/src/components/CustomNetwork/CustomNetworkMenu.tsx @@ -0,0 +1,24 @@ +import { forwardRef } from 'react'; +import classNames from 'classnames'; +import { Dropdown } from 'react-bootstrap'; + +import { CustomNetworkInput } from 'components'; + +export const CustomNetworkMenu = forwardRef( + ({ children, className, style }: any, ref: any) => { + return ( +
+ {children} + +
+ Custom Network API Address + +
+
+ ); + } +); diff --git a/src/components/CustomNetwork/customNetwork.styles.scss b/src/components/CustomNetwork/customNetwork.styles.scss new file mode 100644 index 000000000..01cb4a70f --- /dev/null +++ b/src/components/CustomNetwork/customNetwork.styles.scss @@ -0,0 +1,9 @@ +.custom-network-menu { + width: 14rem; + --dropdown-divider-bg: var(--neutral-700); + .input-group-seamless { + .form-control { + padding-right: 2.5rem; + } + } +} diff --git a/src/components/CustomNetwork/index.ts b/src/components/CustomNetwork/index.ts new file mode 100644 index 000000000..58118c65a --- /dev/null +++ b/src/components/CustomNetwork/index.ts @@ -0,0 +1,2 @@ +export * from './CustomNetworkInput'; +export * from './CustomNetworkMenu'; diff --git a/src/components/index.ts b/src/components/index.ts index bff33bfa1..5e640ab7e 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -10,6 +10,7 @@ export * from './Cards'; export * from './CollapsibleArrows'; export * from './CollectionBlock'; export * from './CopyButton'; +export * from './CustomNetwork'; export * from './DataDecode'; export * from './DetailItem'; export * from './Filters'; diff --git a/src/config/helpers/getStorageCustomNetworks.ts b/src/config/helpers/getStorageCustomNetworks.ts new file mode 100644 index 000000000..4f5e249ea --- /dev/null +++ b/src/config/helpers/getStorageCustomNetworks.ts @@ -0,0 +1,23 @@ +import { NetworkType } from 'types/network.types'; + +export const getStorageCustomNetworks = (): NetworkType[] => { + try { + const decodedNetworks = ''; + + const parsedNetworks = JSON.parse(decodedNetworks); + if (parsedNetworks && parsedNetworks.length > 0) { + return parsedNetworks.map((network: NetworkType) => { + return { + ...network, + ...(!network?.adapter ? { adapter: 'api' } : {}), + ...(!network?.egldLabel ? { egldLabel: 'xEGLD' } : {}), + ...(!network?.chainId ? { chainId: 'T' } : {}) + }; + }); + } + } catch { + return []; + } + + return []; +}; diff --git a/src/config/helpers/index.ts b/src/config/helpers/index.ts index 0aa015e6e..8cb7c6a5e 100644 --- a/src/config/helpers/index.ts +++ b/src/config/helpers/index.ts @@ -1,2 +1,3 @@ export * from './getInternalLinks'; export * from './getInternalNetworks'; +export * from './getStorageCustomNetworks'; diff --git a/src/helpers/storage.ts b/src/helpers/storage.ts index d2e7d1de4..c3faafc4c 100644 --- a/src/helpers/storage.ts +++ b/src/helpers/storage.ts @@ -1,9 +1,13 @@ import moment from 'moment'; -import { TEMP_LOCAL_NOTIFICATION_DISMISSED } from 'appConstants'; +import { + TEMP_LOCAL_NOTIFICATION_DISMISSED, + LOCAL_STORAGE_CUSTOM_NETWORK +} from 'appConstants'; type KeyType = | 'theme' | 'accessToken' + | typeof LOCAL_STORAGE_CUSTOM_NETWORK | typeof TEMP_LOCAL_NOTIFICATION_DISMISSED; export const storage = { From 572215930e9a11041c02527580467c4f0165f967 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Thu, 22 Aug 2024 17:41:22 +0300 Subject: [PATCH 02/15] basse setup for the custom network functionality --- src/assets/scss/_shared-styles.scss | 6 + .../CustomNetwork/CustomNetworkInput.tsx | 36 ++++-- .../IdentityCard/IdentityCard.tsx | 22 ++-- src/config/config.multiple.ts | 8 +- src/config/config.testnet.ts | 4 + src/config/helpers/getInternalNetworks.ts | 4 +- .../helpers/getStorageCustomNetworks.ts | 10 +- src/hooks/adapter/adapter.ts | 9 +- src/hooks/adapter/useAdapterConfig.ts | 3 +- src/hooks/index.ts | 1 + src/hooks/useCustomNetwork.ts | 107 +++++++++++++++++ .../Header/components/Switcher/Switcher.tsx | 109 +++++++++--------- src/redux/slices/networks.ts | 4 +- src/types/adapter.types.ts | 5 + src/types/network.types.ts | 19 ++- 15 files changed, 262 insertions(+), 85 deletions(-) create mode 100644 src/hooks/useCustomNetwork.ts diff --git a/src/assets/scss/_shared-styles.scss b/src/assets/scss/_shared-styles.scss index 0f200afbf..6922cb874 100644 --- a/src/assets/scss/_shared-styles.scss +++ b/src/assets/scss/_shared-styles.scss @@ -32,6 +32,7 @@ border-bottom-right-radius: $input-border-radius-lg !important; } } + &.input-group-search { width: auto; .form-control { @@ -55,6 +56,11 @@ border-top-right-radius: $input-border-radius-sm !important; border-bottom-right-radius: $input-border-radius-sm !important; } + &.has-validation { + .input-group-text { + height: 2.063rem; + } + } } &.has-validation { .input-group-text { diff --git a/src/components/CustomNetwork/CustomNetworkInput.tsx b/src/components/CustomNetwork/CustomNetworkInput.tsx index d8f2c20f7..6bfffb5a7 100644 --- a/src/components/CustomNetwork/CustomNetworkInput.tsx +++ b/src/components/CustomNetwork/CustomNetworkInput.tsx @@ -1,35 +1,43 @@ import React, { useEffect, useState } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import classNames from 'classnames'; -import { useNavigate, useLocation } from 'react-router-dom'; +import { useCustomNetwork } from 'hooks'; import { faCircleNotch, faCheck } from 'icons/regular'; import { WithClassnameType } from 'types'; export const CustomNetworkInput = ({ className }: WithClassnameType) => { - const [customNetworkHash, setcustomNetworkHash] = useState(''); - const [isSaving, setIsSaving] = useState(false); + const [customNetworkUrl, setcustomNetworkUrl] = useState(''); + const [generalError, setGeneralError] = useState(''); + const { setCustomNetwork, customNetworkConfig, isSaving, errors } = + useCustomNetwork(customNetworkUrl); const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { e.preventDefault(); - // customNetwork(); + setCustomNetwork(); } }; const handleChange = (e: React.ChangeEvent) => { - setcustomNetworkHash(e.target.value); + setGeneralError(''); + setcustomNetworkUrl(e.target.value); }; + useEffect(() => { + if (errors?.apiAddress) { + setGeneralError(errors.apiAddress); + } + }, [errors]); + return (
-
+
{ name='requestType' data-testid='customNetwork' required - value={customNetworkHash} + value={customNetworkUrl} onChange={handleChange} onKeyDown={handleKeyDown} aria-label='API Address' @@ -48,7 +56,7 @@ export const CustomNetworkInput = ({ className }: WithClassnameType) => { className='input-group-text' onClick={(e) => { e.preventDefault(); - // customNetwork(); + setCustomNetwork(); }} data-testid='customNetworkButton' aria-label='customNetwork' @@ -60,9 +68,17 @@ export const CustomNetworkInput = ({ className }: WithClassnameType) => { className='me-1 text-primary' /> ) : ( - + )} + {generalError && ( +
{generalError}
+ )}
); diff --git a/src/components/SharedIdentity/IdentityCard/IdentityCard.tsx b/src/components/SharedIdentity/IdentityCard/IdentityCard.tsx index 2338dd27b..3d370ea4c 100644 --- a/src/components/SharedIdentity/IdentityCard/IdentityCard.tsx +++ b/src/components/SharedIdentity/IdentityCard/IdentityCard.tsx @@ -187,16 +187,18 @@ export const IdentityCard = ({ identity }: { identity: IdentityType }) => { className='detail-card' /> - + {walletAddress && ( + + )}
diff --git a/src/config/config.multiple.ts b/src/config/config.multiple.ts index a33b3681b..1ef411c0e 100644 --- a/src/config/config.multiple.ts +++ b/src/config/config.multiple.ts @@ -1,10 +1,16 @@ import { NetworkType } from 'types/network.types'; -import { getInternalNetworks, getInternalLinks } from './helpers'; +import { + getInternalNetworks, + getStorageCustomNetworks, + getInternalLinks +} from './helpers'; import { allApps, schema } from './sharedConfig'; export * from './sharedConfig'; export const networks: NetworkType[] = [ + ...getStorageCustomNetworks(), + { default: true, id: 'testnet', diff --git a/src/config/config.testnet.ts b/src/config/config.testnet.ts index 7e74adad2..8c8350b79 100644 --- a/src/config/config.testnet.ts +++ b/src/config/config.testnet.ts @@ -1,8 +1,12 @@ import { NetworkType } from 'types/network.types'; + +import { getStorageCustomNetworks } from './helpers'; import { allApps, schema } from './sharedConfig'; export * from './sharedConfig'; export const networks: NetworkType[] = [ + ...getStorageCustomNetworks(), + { default: true, id: 'testnet', diff --git a/src/config/helpers/getInternalNetworks.ts b/src/config/helpers/getInternalNetworks.ts index c2756d844..e68ba05ee 100644 --- a/src/config/helpers/getInternalNetworks.ts +++ b/src/config/helpers/getInternalNetworks.ts @@ -1,4 +1,4 @@ -import { NetworkType } from 'types/network.types'; +import { NetworkAdapterEnum, NetworkType } from 'types'; export const getInternalNetworks = (): NetworkType[] => { if (process.env.VITE_APP_INTERNAL_NETWORKS) { @@ -12,7 +12,7 @@ export const getInternalNetworks = (): NetworkType[] => { return parsedNetworks.map((network: NetworkType) => { return { ...network, - ...(!network?.adapter ? { adapter: 'api' } : {}), + ...(!network?.adapter ? { adapter: NetworkAdapterEnum.api } : {}), ...(!network?.egldLabel ? { egldLabel: 'xEGLD' } : {}), ...(!network?.chainId ? { chainId: 'T' } : {}) }; diff --git a/src/config/helpers/getStorageCustomNetworks.ts b/src/config/helpers/getStorageCustomNetworks.ts index 4f5e249ea..c3e5736d9 100644 --- a/src/config/helpers/getStorageCustomNetworks.ts +++ b/src/config/helpers/getStorageCustomNetworks.ts @@ -1,15 +1,17 @@ -import { NetworkType } from 'types/network.types'; +import { LOCAL_STORAGE_CUSTOM_NETWORK } from 'appConstants'; +import { storage } from 'helpers/storage'; +import { NetworkAdapterEnum, NetworkType } from 'types'; export const getStorageCustomNetworks = (): NetworkType[] => { try { - const decodedNetworks = ''; + const storageNetworks = storage.getFromLocal(LOCAL_STORAGE_CUSTOM_NETWORK); + const parsedNetworks = JSON.parse(storageNetworks); - const parsedNetworks = JSON.parse(decodedNetworks); if (parsedNetworks && parsedNetworks.length > 0) { return parsedNetworks.map((network: NetworkType) => { return { ...network, - ...(!network?.adapter ? { adapter: 'api' } : {}), + ...(!network?.adapter ? { adapter: NetworkAdapterEnum.api } : {}), ...(!network?.egldLabel ? { egldLabel: 'xEGLD' } : {}), ...(!network?.chainId ? { chainId: 'T' } : {}) }; diff --git a/src/hooks/adapter/adapter.ts b/src/hooks/adapter/adapter.ts index 6b619de34..2207e898c 100644 --- a/src/hooks/adapter/adapter.ts +++ b/src/hooks/adapter/adapter.ts @@ -739,6 +739,13 @@ export const useAdapter = () => { provider({ baseUrl: `${growthApi}/explorer/widgets`, url }), getGrowthHeaders: (url: string) => - provider({ baseUrl: `${growthApi}/explorer/headers`, url }) + provider({ baseUrl: `${growthApi}/explorer/headers`, url }), + + // Network Config + getNetworkConfig: (baseUrl: string) => + provider({ + baseUrl, + url: '/dapp/config' + }) }; }; diff --git a/src/hooks/adapter/useAdapterConfig.ts b/src/hooks/adapter/useAdapterConfig.ts index f33076d51..33575cba1 100644 --- a/src/hooks/adapter/useAdapterConfig.ts +++ b/src/hooks/adapter/useAdapterConfig.ts @@ -3,6 +3,7 @@ import { useSelector } from 'react-redux'; import { METACHAIN_SHARD_ID, TIMEOUT } from 'appConstants'; import { activeNetworkSelector } from 'redux/selectors'; import { + NetworkAdapterEnum, AdapterProviderPropsType, ApiAdapterResponseType } from 'types/adapter.types'; @@ -56,7 +57,7 @@ export const useAdapterConfig = () => { } }; - const adapter: 'api' | 'elastic' = networkAdapter as any; + const adapter = networkAdapter as NetworkAdapterEnum; const { provider, diff --git a/src/hooks/index.ts b/src/hooks/index.ts index fc4aa4f87..9d4e65c90 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -5,6 +5,7 @@ export * from './pageStats'; export * from './urlFilters'; export * from './useActiveRoute'; export * from './useCheckVersion'; +export * from './useCustomNetwork'; export * from './useDebounce'; export * from './useGetExplorerTitle'; export * from './useGetHash'; diff --git a/src/hooks/useCustomNetwork.ts b/src/hooks/useCustomNetwork.ts new file mode 100644 index 000000000..f1dba97e1 --- /dev/null +++ b/src/hooks/useCustomNetwork.ts @@ -0,0 +1,107 @@ +import { useEffect, useState } from 'react'; +import moment from 'moment'; + +import { LOCAL_STORAGE_CUSTOM_NETWORK } from 'appConstants'; +import { storage } from 'helpers'; +import { useAdapter } from 'hooks'; +import { DappNetworkConfigType, NetworkType, NetworkAdapterEnum } from 'types'; + +export interface CustomNetworkErrorType { + apiAddress?: string; + chainId?: string; + adapter?: string; + egldLabel?: string; + explorerAddress?: string; +} + +const validateUrl = (url: string) => { + if (!url) { + return 'Required'; + } + + try { + new URL(url); + return ''; + } catch (err) { + return 'Invalid Url'; + } +}; + +export const useCustomNetwork = (customUrl: string) => { + const [isSaving, setIsSaving] = useState(); + const [errors, setErrors] = useState(); + const [customNetworkConfig, setCustomNetworkConfig] = useState< + NetworkType | undefined + >(); + + const { getNetworkConfig } = useAdapter(); + + const setCustomNetwork = async () => { + setIsSaving(true); + setErrors(undefined); + const urlError = validateUrl(customUrl); + if (urlError) { + setErrors((errors) => { + return { ...errors, apiAddress: urlError }; + }); + setIsSaving(false); + return; + } + + const apiAddress = new URL(customUrl).toString().replace(/\/+$/, ''); + + const { data, success } = await getNetworkConfig(apiAddress); + if (data && success) { + const { chainId, egldLabel, explorerAddress, walletAddress, name } = + data as DappNetworkConfigType; + + if (chainId && egldLabel && walletAddress && explorerAddress) { + const customNetwork = { + id: 'custom-network', + name: `Custom ${name ?? 'Network'}`, + adapter: NetworkAdapterEnum.api, + theme: 'testnet', + apiAddress, + chainId, + egldLabel, + walletAddress, + explorerAddress + }; + + try { + const in30Days = new Date(moment().add(30, 'days').toDate()); + storage.saveToLocal({ + key: LOCAL_STORAGE_CUSTOM_NETWORK, + data: JSON.stringify([customNetwork]), + expirationDate: in30Days + }); + } catch (error) { + console.error('Unable to Save Custom Network: ', error); + setErrors((errors) => { + return { ...errors, apiAddress: 'Unable to Save Custom Network' }; + }); + setIsSaving(false); + return; + } + + setCustomNetworkConfig(customNetwork); + setIsSaving(false); + + return; + } + } + + setErrors((errors) => { + return { ...errors, apiAddress: 'Invalid API Config' }; + }); + setIsSaving(false); + }; + + useEffect(() => { + if (customUrl) { + setIsSaving(false); + } + }, [customUrl]); + + return { setCustomNetwork, isSaving, customNetworkConfig, errors }; +}; diff --git a/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx b/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx index f50b9fb14..7ce4fc654 100644 --- a/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx +++ b/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx @@ -3,6 +3,7 @@ import classNames from 'classnames'; import { Anchor, Dropdown } from 'react-bootstrap'; import { useSelector } from 'react-redux'; +import { CustomNetworkMenu } from 'components'; import { networks, links } from 'config'; import { getSubdomainNetwork } from 'helpers'; import { faAngleDown } from 'icons/solid'; @@ -52,59 +53,61 @@ export const Switcher = () => { role='menu' aria-labelledby='network-switch' > - {links.length > 0 ? ( - <> - {links.map((link) => ( - - {link.name} - - ))} - - ) : ( - <> - {isSubSubdomain && window?.location?.hostname ? ( - <> - {networkLinks.map((link) => ( - - {link.name} - - ))} - - ) : ( - <> - {networkLinks.map((link) => ( - - {link.name} - - ))} - - )} - - )} + + {links.length > 0 ? ( + <> + {links.map((link) => ( + + {link.name} + + ))} + + ) : ( + <> + {isSubSubdomain && window?.location?.hostname ? ( + <> + {networkLinks.map((link) => ( + + {link.name} + + ))} + + ) : ( + <> + {networkLinks.map((link) => ( + + {link.name} + + ))} + + )} + + )} + ); diff --git a/src/redux/slices/networks.ts b/src/redux/slices/networks.ts index c20ca3987..db5c7db3a 100644 --- a/src/redux/slices/networks.ts +++ b/src/redux/slices/networks.ts @@ -1,13 +1,13 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { networks } from 'config'; import { getSubdomainNetwork } from 'helpers'; -import { NetworkType } from 'types/network.types'; +import { NetworkAdapterEnum, NetworkType } from 'types'; export const emptyNetwork: NetworkType = { default: false, id: 'not-configured', name: 'NOT CONFIGURED', - adapter: 'api', + adapter: NetworkAdapterEnum.api, theme: '', egldLabel: '', walletAddress: '', diff --git a/src/types/adapter.types.ts b/src/types/adapter.types.ts index 60efbf683..edbf71c78 100644 --- a/src/types/adapter.types.ts +++ b/src/types/adapter.types.ts @@ -1,5 +1,10 @@ import { SortOrderEnum, TransactionInPoolTypeEnum } from 'types'; +export enum NetworkAdapterEnum { + api = 'api', + elastic = 'elastic' +} + export interface BaseApiType { page?: number; size?: number; diff --git a/src/types/network.types.ts b/src/types/network.types.ts index 86e3c3dee..e9ff40b08 100644 --- a/src/types/network.types.ts +++ b/src/types/network.types.ts @@ -1,7 +1,8 @@ import { NetworkType as NetworkConfigType } from '@multiversx/sdk-dapp/types/network.types'; +import { NetworkAdapterEnum } from './adapter.types'; export interface NetworkType extends Partial { - adapter: 'api' | 'elastic'; + adapter: NetworkAdapterEnum | string; // temporary, will be restricted on a future network adapter overhaul theme?: string; default?: boolean; accessToken?: boolean; @@ -18,3 +19,19 @@ export interface NetworkUrlType { name: string; url: string; } + +export interface DappNetworkConfigType { + id: string | number; + name: string; + egldLabel: string; + decimals: string; + egldDenomination: string; + gasPerDataByte: string; + apiTimeout: string; + walletConnectDeepLink: string; + walletConnectBridgeAddresses: string[]; + walletAddress: string; + apiAddress: string; + explorerAddress: string; + chainId: string; +} From 4af4a773a58a3c9fbbc0843af11c6e0829f453ac Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Fri, 23 Aug 2024 15:20:35 +0300 Subject: [PATCH 03/15] display the custom network in the input, not on the menu --- src/appConstants/index.ts | 2 +- .../CustomNetwork/CustomNetworkInput.tsx | 17 ++++++++++-- .../helpers/getStorageCustomNetworks.ts | 7 ++--- src/helpers/storage.ts | 4 +-- src/hooks/useCustomNetwork.ts | 26 ++++++++++++++---- .../Header/components/Switcher/Switcher.tsx | 27 ++++++++++--------- src/types/network.types.ts | 1 + 7 files changed, 58 insertions(+), 26 deletions(-) diff --git a/src/appConstants/index.ts b/src/appConstants/index.ts index f40a3f4b7..50cf0f4b0 100644 --- a/src/appConstants/index.ts +++ b/src/appConstants/index.ts @@ -27,7 +27,7 @@ export const AUCTION_LIST_MIN_DISPLAY_ROW_COUNT = 6; export const LEGACY_DELEGATION_NODES_IDENTITY = 'multiversx'; export const HEROTAG_SUFFIX = '.elrond'; export const TEMP_LOCAL_NOTIFICATION_DISMISSED = 'tempNotificationDismissed2'; -export const LOCAL_STORAGE_CUSTOM_NETWORK = 'customNetwork'; +export const CUSTOM_NETWORK_ID = 'custom-network'; export const NEW_VERSION_NOTIFICATION = 'newExplorerVersion'; export const SC_INIT_CHARACTERS_LENGTH = 13; diff --git a/src/components/CustomNetwork/CustomNetworkInput.tsx b/src/components/CustomNetwork/CustomNetworkInput.tsx index 6bfffb5a7..60575844b 100644 --- a/src/components/CustomNetwork/CustomNetworkInput.tsx +++ b/src/components/CustomNetwork/CustomNetworkInput.tsx @@ -1,13 +1,26 @@ import React, { useEffect, useState } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import classNames from 'classnames'; +import { useSelector } from 'react-redux'; +import { networks } from 'config'; import { useCustomNetwork } from 'hooks'; import { faCircleNotch, faCheck } from 'icons/regular'; +import { activeNetworkSelector } from 'redux/selectors'; import { WithClassnameType } from 'types'; export const CustomNetworkInput = ({ className }: WithClassnameType) => { - const [customNetworkUrl, setcustomNetworkUrl] = useState(''); + const activeNetwork = useSelector(activeNetworkSelector); + const { isCustom: activeNetworkIsCustom } = activeNetwork; + + const configCustomNetwork = networks.filter((network) => network.isCustom)[0]; + const existingNetwork = activeNetworkIsCustom + ? activeNetwork + : configCustomNetwork; + + const [customNetworkUrl, setcustomNetworkUrl] = useState( + existingNetwork?.apiAddress ?? '' + ); const [generalError, setGeneralError] = useState(''); const { setCustomNetwork, customNetworkConfig, isSaving, errors } = useCustomNetwork(customNetworkUrl); @@ -71,7 +84,7 @@ export const CustomNetworkInput = ({ className }: WithClassnameType) => { )} diff --git a/src/config/helpers/getStorageCustomNetworks.ts b/src/config/helpers/getStorageCustomNetworks.ts index c3e5736d9..950c8da28 100644 --- a/src/config/helpers/getStorageCustomNetworks.ts +++ b/src/config/helpers/getStorageCustomNetworks.ts @@ -1,10 +1,10 @@ -import { LOCAL_STORAGE_CUSTOM_NETWORK } from 'appConstants'; +import { CUSTOM_NETWORK_ID } from 'appConstants'; import { storage } from 'helpers/storage'; import { NetworkAdapterEnum, NetworkType } from 'types'; export const getStorageCustomNetworks = (): NetworkType[] => { try { - const storageNetworks = storage.getFromLocal(LOCAL_STORAGE_CUSTOM_NETWORK); + const storageNetworks = storage.getFromLocal(CUSTOM_NETWORK_ID); const parsedNetworks = JSON.parse(storageNetworks); if (parsedNetworks && parsedNetworks.length > 0) { @@ -13,7 +13,8 @@ export const getStorageCustomNetworks = (): NetworkType[] => { ...network, ...(!network?.adapter ? { adapter: NetworkAdapterEnum.api } : {}), ...(!network?.egldLabel ? { egldLabel: 'xEGLD' } : {}), - ...(!network?.chainId ? { chainId: 'T' } : {}) + ...(!network?.chainId ? { chainId: 'T' } : {}), + isCustom: true }; }); } diff --git a/src/helpers/storage.ts b/src/helpers/storage.ts index c3faafc4c..4ca8428cc 100644 --- a/src/helpers/storage.ts +++ b/src/helpers/storage.ts @@ -1,13 +1,13 @@ import moment from 'moment'; import { TEMP_LOCAL_NOTIFICATION_DISMISSED, - LOCAL_STORAGE_CUSTOM_NETWORK + CUSTOM_NETWORK_ID } from 'appConstants'; type KeyType = | 'theme' | 'accessToken' - | typeof LOCAL_STORAGE_CUSTOM_NETWORK + | typeof CUSTOM_NETWORK_ID | typeof TEMP_LOCAL_NOTIFICATION_DISMISSED; export const storage = { diff --git a/src/hooks/useCustomNetwork.ts b/src/hooks/useCustomNetwork.ts index f1dba97e1..f78c26e31 100644 --- a/src/hooks/useCustomNetwork.ts +++ b/src/hooks/useCustomNetwork.ts @@ -1,9 +1,12 @@ import { useEffect, useState } from 'react'; import moment from 'moment'; +import { useSelector } from 'react-redux'; -import { LOCAL_STORAGE_CUSTOM_NETWORK } from 'appConstants'; +import { CUSTOM_NETWORK_ID } from 'appConstants'; +import { networks } from 'config'; import { storage } from 'helpers'; import { useAdapter } from 'hooks'; +import { activeNetworkSelector } from 'redux/selectors'; import { DappNetworkConfigType, NetworkType, NetworkAdapterEnum } from 'types'; export interface CustomNetworkErrorType { @@ -28,13 +31,22 @@ const validateUrl = (url: string) => { }; export const useCustomNetwork = (customUrl: string) => { + const activeNetwork = useSelector(activeNetworkSelector); + const { getNetworkConfig } = useAdapter(); + const { isCustom: activeNetworkIsCustom } = activeNetwork; + const configCustomNetwork = networks.filter((network) => network.isCustom)[0]; + + const existingNetwork = activeNetworkIsCustom + ? activeNetwork + : configCustomNetwork; + const [isSaving, setIsSaving] = useState(); const [errors, setErrors] = useState(); const [customNetworkConfig, setCustomNetworkConfig] = useState< NetworkType | undefined - >(); + >(existingNetwork); - const { getNetworkConfig } = useAdapter(); + console.log('------activeNetworkName', activeNetwork); const setCustomNetwork = async () => { setIsSaving(true); @@ -57,10 +69,11 @@ export const useCustomNetwork = (customUrl: string) => { if (chainId && egldLabel && walletAddress && explorerAddress) { const customNetwork = { - id: 'custom-network', + id: CUSTOM_NETWORK_ID, name: `Custom ${name ?? 'Network'}`, adapter: NetworkAdapterEnum.api, theme: 'testnet', + isCustom: true, apiAddress, chainId, egldLabel, @@ -71,7 +84,7 @@ export const useCustomNetwork = (customUrl: string) => { try { const in30Days = new Date(moment().add(30, 'days').toDate()); storage.saveToLocal({ - key: LOCAL_STORAGE_CUSTOM_NETWORK, + key: CUSTOM_NETWORK_ID, data: JSON.stringify([customNetwork]), expirationDate: in30Days }); @@ -87,6 +100,9 @@ export const useCustomNetwork = (customUrl: string) => { setCustomNetworkConfig(customNetwork); setIsSaving(false); + // we want to reset the whole state, react router's navigate might lead to unwanted innacuracies + window.location.href = `/${CUSTOM_NETWORK_ID}`; + return; } } diff --git a/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx b/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx index 7ce4fc654..67013234b 100644 --- a/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx +++ b/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx @@ -7,7 +7,6 @@ import { CustomNetworkMenu } from 'components'; import { networks, links } from 'config'; import { getSubdomainNetwork } from 'helpers'; import { faAngleDown } from 'icons/solid'; - import { activeNetworkSelector, defaultNetworkSelector } from 'redux/selectors'; export const Switcher = () => { @@ -17,18 +16,20 @@ export const Switcher = () => { const { id: defaultNetworkId } = useSelector(defaultNetworkSelector); const { isSubSubdomain } = getSubdomainNetwork(); - const networkLinks = networks.map(({ name, id }) => { - let url = id === defaultNetworkId ? '/' : `/${id}`; - if (isSubSubdomain && window?.location?.hostname) { - const [_omit, ...rest] = window.location.hostname.split('.'); - url = `https://${[id, ...rest].join('.')}`; - } - return { - name, - url, - id - }; - }); + const networkLinks = networks + .filter((network) => !network.isCustom) + .map(({ name, id }) => { + let url = id === defaultNetworkId ? '/' : `/${id}`; + if (isSubSubdomain && window?.location?.hostname) { + const [_omit, ...rest] = window.location.hostname.split('.'); + url = `https://${[id, ...rest].join('.')}`; + } + return { + name, + url, + id + }; + }); return ( diff --git a/src/types/network.types.ts b/src/types/network.types.ts index e9ff40b08..bf28bb6f0 100644 --- a/src/types/network.types.ts +++ b/src/types/network.types.ts @@ -12,6 +12,7 @@ export interface NetworkType extends Partial { proxyUrl?: string; nftExplorerAddress?: string; isSovereign?: boolean; + isCustom?: boolean; } export interface NetworkUrlType { From 457bbf0852f06122a74c66d4720d71a3ec400477 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Fri, 23 Aug 2024 17:53:55 +0300 Subject: [PATCH 04/15] show custom network details --- .../CollapsibleArrows/CollapsibleArrows.tsx | 2 +- .../CustomNetwork/CustomNetworkDetails.tsx | 155 ++++++++++++++++++ .../CustomNetwork/CustomNetworkInput.tsx | 12 +- .../CustomNetwork/CustomNetworkMenu.tsx | 5 +- .../CustomNetwork/customNetwork.styles.scss | 15 +- src/components/CustomNetwork/index.ts | 1 + src/helpers/index.ts | 1 + src/helpers/scrollToElement.ts | 12 ++ src/hooks/useCustomNetwork.ts | 2 - src/icons/regular/fontawesomeFree.ts | 2 + src/icons/regular/fontawesomePro.ts | 2 + src/icons/solid/fontawesomeFree.ts | 2 + src/icons/solid/fontawesomePro.ts | 2 + .../EcosystemMenu/ecosystemMenu.styles.scss | 1 - .../components/Header/header.styles.scss | 3 +- src/layouts/Layout/layout.styles.scss | 2 +- 16 files changed, 205 insertions(+), 14 deletions(-) create mode 100644 src/components/CustomNetwork/CustomNetworkDetails.tsx create mode 100644 src/helpers/scrollToElement.ts diff --git a/src/components/CollapsibleArrows/CollapsibleArrows.tsx b/src/components/CollapsibleArrows/CollapsibleArrows.tsx index 936656235..325bef04c 100644 --- a/src/components/CollapsibleArrows/CollapsibleArrows.tsx +++ b/src/components/CollapsibleArrows/CollapsibleArrows.tsx @@ -15,7 +15,7 @@ export const CollapsibleArrows = ({ }: CollapsibleArrowsPropsType) => { return ( diff --git a/src/components/CustomNetwork/CustomNetworkDetails.tsx b/src/components/CustomNetwork/CustomNetworkDetails.tsx new file mode 100644 index 000000000..3f5bf4919 --- /dev/null +++ b/src/components/CustomNetwork/CustomNetworkDetails.tsx @@ -0,0 +1,155 @@ +import React, { useState } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import classNames from 'classnames'; +import { Collapse } from 'react-bootstrap'; +import { useSelector } from 'react-redux'; + +import { CUSTOM_NETWORK_ID } from 'appConstants'; +import { CollapsibleArrows, CopyButton } from 'components'; +import { networks } from 'config'; +import { storage, scrollToElement } from 'helpers'; +import { faTrash, faCheck } from 'icons/regular'; +import { activeNetworkSelector } from 'redux/selectors'; +import { WithClassnameType } from 'types'; + +const NetworkDetail = ({ + title, + description +}: { + title: string; + description: React.ReactNode; +}) => { + return ( +
+
{title}:
+
{description}
+
+ ); +}; + +export const CustomNetworkDetails = ({ className }: WithClassnameType) => { + const activeNetwork = useSelector(activeNetworkSelector); + const { isCustom: activeNetworkIsCustom } = activeNetwork; + + const configCustomNetwork = networks.filter((network) => network.isCustom)[0]; + const existingCustomNetwork = activeNetworkIsCustom + ? activeNetwork + : configCustomNetwork; + + const [open, setOpen] = useState(false); + + const isSavedCustomNetworkActive = + configCustomNetwork?.id === activeNetwork?.id; + + const removeNetwork = () => { + storage.removeFromLocal(CUSTOM_NETWORK_ID); + window.location.href = '/'; + }; + + const applyNetwork = () => { + window.location.href = `/${CUSTOM_NETWORK_ID}`; + }; + + if (!existingCustomNetwork) { + return null; + } + + return ( +
+ + { + scrollToElement('.custom-network-details', 50); + }} + > +
+
+ {!isSavedCustomNetworkActive && ( + <> +
Saved Custom Network Config
+ false} + /> + + )} + {existingCustomNetwork.name && ( + + )} + {existingCustomNetwork.apiAddress && ( + + {existingCustomNetwork.apiAddress}{' '} + +
+ } + /> + )} + {existingCustomNetwork.chainId && ( + + {existingCustomNetwork.chainId} + + } + /> + )} + {existingCustomNetwork.egldLabel && ( + + )} +
+ {!isSavedCustomNetworkActive && ( + + )} + +
+
+
+ +
+ ); +}; diff --git a/src/components/CustomNetwork/CustomNetworkInput.tsx b/src/components/CustomNetwork/CustomNetworkInput.tsx index 60575844b..4387ac2b5 100644 --- a/src/components/CustomNetwork/CustomNetworkInput.tsx +++ b/src/components/CustomNetwork/CustomNetworkInput.tsx @@ -5,7 +5,8 @@ import { useSelector } from 'react-redux'; import { networks } from 'config'; import { useCustomNetwork } from 'hooks'; -import { faCircleNotch, faCheck } from 'icons/regular'; +import { faCircleNotch } from 'icons/regular'; +import { faCheck } from 'icons/solid'; import { activeNetworkSelector } from 'redux/selectors'; import { WithClassnameType } from 'types'; @@ -14,15 +15,15 @@ export const CustomNetworkInput = ({ className }: WithClassnameType) => { const { isCustom: activeNetworkIsCustom } = activeNetwork; const configCustomNetwork = networks.filter((network) => network.isCustom)[0]; - const existingNetwork = activeNetworkIsCustom + const existingCustomNetwork = activeNetworkIsCustom ? activeNetwork : configCustomNetwork; const [customNetworkUrl, setcustomNetworkUrl] = useState( - existingNetwork?.apiAddress ?? '' + existingCustomNetwork?.apiAddress ?? '' ); const [generalError, setGeneralError] = useState(''); - const { setCustomNetwork, customNetworkConfig, isSaving, errors } = + const { setCustomNetwork, isSaving, errors } = useCustomNetwork(customNetworkUrl); const handleKeyDown = (e: React.KeyboardEvent) => { @@ -61,6 +62,7 @@ export const CustomNetworkInput = ({ className }: WithClassnameType) => { value={customNetworkUrl} onChange={handleChange} onKeyDown={handleKeyDown} + disabled={isSaving} aria-label='API Address' aria-describedby='customNetwork-addon' /> @@ -84,7 +86,7 @@ export const CustomNetworkInput = ({ className }: WithClassnameType) => { )} diff --git a/src/components/CustomNetwork/CustomNetworkMenu.tsx b/src/components/CustomNetwork/CustomNetworkMenu.tsx index 350615025..eecf153de 100644 --- a/src/components/CustomNetwork/CustomNetworkMenu.tsx +++ b/src/components/CustomNetwork/CustomNetworkMenu.tsx @@ -2,7 +2,7 @@ import { forwardRef } from 'react'; import classNames from 'classnames'; import { Dropdown } from 'react-bootstrap'; -import { CustomNetworkInput } from 'components'; +import { CustomNetworkInput, CustomNetworkDetails } from 'components'; export const CustomNetworkMenu = forwardRef( ({ children, className, style }: any, ref: any) => { @@ -12,11 +12,12 @@ export const CustomNetworkMenu = forwardRef( className={classNames('custom-network-menu', className)} style={style} > - {children} +
{children}
Custom Network API Address +
); diff --git a/src/components/CustomNetwork/customNetwork.styles.scss b/src/components/CustomNetwork/customNetwork.styles.scss index 01cb4a70f..a6849b035 100644 --- a/src/components/CustomNetwork/customNetwork.styles.scss +++ b/src/components/CustomNetwork/customNetwork.styles.scss @@ -1,9 +1,22 @@ .custom-network-menu { - width: 14rem; + width: 15rem; + min-height: 20rem; + max-height: calc(100dvh - #{$header-navbar-height} - 6rem); + overflow-y: scroll; + @include media-breakpoint-up(lg) { + max-height: calc(100dvh - #{$header-navbar-height} - 1rem); + } --dropdown-divider-bg: var(--neutral-700); .input-group-seamless { .form-control { padding-right: 2.5rem; } } + .network-list { + @include media-breakpoint-up(lg) { + min-height: 14rem; + max-height: calc(100dvh - #{$header-navbar-height} - 8.25rem); + overflow-y: scroll; + } + } } diff --git a/src/components/CustomNetwork/index.ts b/src/components/CustomNetwork/index.ts index 58118c65a..509059c09 100644 --- a/src/components/CustomNetwork/index.ts +++ b/src/components/CustomNetwork/index.ts @@ -1,2 +1,3 @@ +export * from './CustomNetworkDetails'; export * from './CustomNetworkInput'; export * from './CustomNetworkMenu'; diff --git a/src/helpers/index.ts b/src/helpers/index.ts index 056f1094a..52edc6d9c 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -18,6 +18,7 @@ export * from './isUtf8'; export * from './parseAmount'; export * from './parseJwt'; export * from './partitionBy'; +export * from './scrollToElement'; export * from './processData'; export * from './storage'; export * from './stringIsFloat'; diff --git a/src/helpers/scrollToElement.ts b/src/helpers/scrollToElement.ts new file mode 100644 index 000000000..1f6c066eb --- /dev/null +++ b/src/helpers/scrollToElement.ts @@ -0,0 +1,12 @@ +export const scrollToElement = (selector: string, timeout?: number) => { + const activeElement = document.querySelector(selector); + setTimeout(() => { + if (activeElement) { + activeElement.scrollIntoView({ + behavior: 'smooth', + block: 'start', + inline: 'start' + }); + } + }, timeout); +}; diff --git a/src/hooks/useCustomNetwork.ts b/src/hooks/useCustomNetwork.ts index f78c26e31..e28b96d73 100644 --- a/src/hooks/useCustomNetwork.ts +++ b/src/hooks/useCustomNetwork.ts @@ -46,8 +46,6 @@ export const useCustomNetwork = (customUrl: string) => { NetworkType | undefined >(existingNetwork); - console.log('------activeNetworkName', activeNetwork); - const setCustomNetwork = async () => { setIsSaving(true); setErrors(undefined); diff --git a/src/icons/regular/fontawesomeFree.ts b/src/icons/regular/fontawesomeFree.ts index f4383e9f4..cd69fa66e 100644 --- a/src/icons/regular/fontawesomeFree.ts +++ b/src/icons/regular/fontawesomeFree.ts @@ -110,6 +110,7 @@ import { faStream, faSync, faTimes, + faTrash, faTrophy, faUpLong as faUp, faUserCheck, @@ -228,6 +229,7 @@ export { faSync, faTerminal, faTimes, + faTrash, faTrophy, faUp, faUpRight, diff --git a/src/icons/regular/fontawesomePro.ts b/src/icons/regular/fontawesomePro.ts index 3f6d4c58c..4213aaa2f 100644 --- a/src/icons/regular/fontawesomePro.ts +++ b/src/icons/regular/fontawesomePro.ts @@ -108,6 +108,7 @@ import { faSync, faTerminal, faTimes, + faTrash, faTrophy, faUp, faUpRight, @@ -230,6 +231,7 @@ export { faSync, faTerminal, faTimes, + faTrash, faTrophy, faUp, faUpRight, diff --git a/src/icons/solid/fontawesomeFree.ts b/src/icons/solid/fontawesomeFree.ts index 3bdd499b0..2ce86b230 100644 --- a/src/icons/solid/fontawesomeFree.ts +++ b/src/icons/solid/fontawesomeFree.ts @@ -107,6 +107,7 @@ import { faStream, faSync, faTimes, + faTrash, faTrophy, faUpLong as faUp, faUser, @@ -226,6 +227,7 @@ export { faSync, faTerminal, faTimes, + faTrash, faTrophy, faUp, faUpRight, diff --git a/src/icons/solid/fontawesomePro.ts b/src/icons/solid/fontawesomePro.ts index b43b034b6..6b699d5ca 100644 --- a/src/icons/solid/fontawesomePro.ts +++ b/src/icons/solid/fontawesomePro.ts @@ -108,6 +108,7 @@ import { faSync, faTerminal, faTimes, + faTrash, faTrophy, faUp, faUpRight, @@ -230,6 +231,7 @@ export { faSync, faTerminal, faTimes, + faTrash, faTrophy, faUp, faUpRight, diff --git a/src/layouts/Layout/components/Header/components/EcosystemMenu/ecosystemMenu.styles.scss b/src/layouts/Layout/components/Header/components/EcosystemMenu/ecosystemMenu.styles.scss index 0946a3c59..0b373b0ef 100644 --- a/src/layouts/Layout/components/Header/components/EcosystemMenu/ecosystemMenu.styles.scss +++ b/src/layouts/Layout/components/Header/components/EcosystemMenu/ecosystemMenu.styles.scss @@ -2,7 +2,6 @@ display: flex; flex-direction: column; margin-top: 0; - overflow: hidden; &-item { display: block; diff --git a/src/layouts/Layout/components/Header/header.styles.scss b/src/layouts/Layout/components/Header/header.styles.scss index f29a1afa1..9f3a5f42e 100644 --- a/src/layouts/Layout/components/Header/header.styles.scss +++ b/src/layouts/Layout/components/Header/header.styles.scss @@ -239,6 +239,8 @@ &.active { transform: translateX(0%); + overflow-y: auto; + overflow-x: hidden; } } @@ -260,7 +262,6 @@ } .ecosystem-menu-wrapper { - overflow: hidden; @include media-breakpoint-up(lg) { position: absolute; right: calc(((100vw - 960px) / 2) + 0.75rem); diff --git a/src/layouts/Layout/layout.styles.scss b/src/layouts/Layout/layout.styles.scss index 1ba32ea04..72db17405 100644 --- a/src/layouts/Layout/layout.styles.scss +++ b/src/layouts/Layout/layout.styles.scss @@ -7,7 +7,7 @@ > .main-content-container { @include media-breakpoint-up(md) { - min-height: calc(100vh - #{$footer-height + $header-navbar-height}); + min-height: calc(100dvh - #{$footer-height + $header-navbar-height}); } } From 1883911ecad6b76f9a168d0c67d5a6d1937ae90a Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Fri, 23 Aug 2024 18:26:44 +0300 Subject: [PATCH 05/15] updated styling --- .../CustomNetwork/CustomNetworkMenu.tsx | 2 +- .../CustomNetwork/customNetwork.styles.scss | 13 -- src/config/config.devnet.ts | 5 + src/config/config.multiple.ts | 3 + src/config/config.testnet.ts | 1 + .../helpers/getStorageCustomNetworks.ts | 5 + src/config/sharedConfig.ts | 2 + .../Header/components/Switcher/Switcher.tsx | 123 ++++++++++-------- .../components/Switcher/switcher.styles.scss | 18 +++ 9 files changed, 103 insertions(+), 69 deletions(-) diff --git a/src/components/CustomNetwork/CustomNetworkMenu.tsx b/src/components/CustomNetwork/CustomNetworkMenu.tsx index eecf153de..295e7e8cd 100644 --- a/src/components/CustomNetwork/CustomNetworkMenu.tsx +++ b/src/components/CustomNetwork/CustomNetworkMenu.tsx @@ -12,7 +12,7 @@ export const CustomNetworkMenu = forwardRef( className={classNames('custom-network-menu', className)} style={style} > -
{children}
+ {children}
Custom Network API Address diff --git a/src/components/CustomNetwork/customNetwork.styles.scss b/src/components/CustomNetwork/customNetwork.styles.scss index a6849b035..02c9d4632 100644 --- a/src/components/CustomNetwork/customNetwork.styles.scss +++ b/src/components/CustomNetwork/customNetwork.styles.scss @@ -1,22 +1,9 @@ .custom-network-menu { width: 15rem; - min-height: 20rem; - max-height: calc(100dvh - #{$header-navbar-height} - 6rem); - overflow-y: scroll; - @include media-breakpoint-up(lg) { - max-height: calc(100dvh - #{$header-navbar-height} - 1rem); - } --dropdown-divider-bg: var(--neutral-700); .input-group-seamless { .form-control { padding-right: 2.5rem; } } - .network-list { - @include media-breakpoint-up(lg) { - min-height: 14rem; - max-height: calc(100dvh - #{$header-navbar-height} - 8.25rem); - overflow-y: scroll; - } - } } diff --git a/src/config/config.devnet.ts b/src/config/config.devnet.ts index a119d982b..a3ac68bbe 100644 --- a/src/config/config.devnet.ts +++ b/src/config/config.devnet.ts @@ -1,8 +1,13 @@ import { NetworkType } from 'types/network.types'; + +import { getStorageCustomNetworks } from './helpers'; import { allApps, schema } from './sharedConfig'; export * from './sharedConfig'; export const networks: NetworkType[] = [ + // Saved Custom Network Configs + ...getStorageCustomNetworks(), + { default: true, id: 'devnet', diff --git a/src/config/config.multiple.ts b/src/config/config.multiple.ts index 1ef411c0e..297afff97 100644 --- a/src/config/config.multiple.ts +++ b/src/config/config.multiple.ts @@ -8,7 +8,10 @@ import { allApps, schema } from './sharedConfig'; export * from './sharedConfig'; +export const hasExtraNetworks = true; + export const networks: NetworkType[] = [ + // Saved Custom Network Configs ...getStorageCustomNetworks(), { diff --git a/src/config/config.testnet.ts b/src/config/config.testnet.ts index 8c8350b79..a215800b7 100644 --- a/src/config/config.testnet.ts +++ b/src/config/config.testnet.ts @@ -5,6 +5,7 @@ import { allApps, schema } from './sharedConfig'; export * from './sharedConfig'; export const networks: NetworkType[] = [ + // Saved Custom Network Configs ...getStorageCustomNetworks(), { diff --git a/src/config/helpers/getStorageCustomNetworks.ts b/src/config/helpers/getStorageCustomNetworks.ts index 950c8da28..c26ec1ca3 100644 --- a/src/config/helpers/getStorageCustomNetworks.ts +++ b/src/config/helpers/getStorageCustomNetworks.ts @@ -1,8 +1,13 @@ import { CUSTOM_NETWORK_ID } from 'appConstants'; +import { hasExtraNetworks } from 'config'; import { storage } from 'helpers/storage'; import { NetworkAdapterEnum, NetworkType } from 'types'; export const getStorageCustomNetworks = (): NetworkType[] => { + if (!hasExtraNetworks) { + return []; + } + try { const storageNetworks = storage.getFromLocal(CUSTOM_NETWORK_ID); const parsedNetworks = JSON.parse(storageNetworks); diff --git a/src/config/sharedConfig.ts b/src/config/sharedConfig.ts index 83c36a81f..0d2a97901 100644 --- a/src/config/sharedConfig.ts +++ b/src/config/sharedConfig.ts @@ -31,6 +31,8 @@ export const SHARE_PREFIX = process.env.VITE_APP_SHARE_PREFIX ? process.env.VITE_APP_SHARE_PREFIX.replace('-', '') : ''; +export const hasExtraNetworks = false; + export const links: NetworkUrlType[] = [ { id: 'mainnet', diff --git a/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx b/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx index 67013234b..d6941d112 100644 --- a/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx +++ b/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx @@ -4,7 +4,7 @@ import { Anchor, Dropdown } from 'react-bootstrap'; import { useSelector } from 'react-redux'; import { CustomNetworkMenu } from 'components'; -import { networks, links } from 'config'; +import { networks, links, hasExtraNetworks } from 'config'; import { getSubdomainNetwork } from 'helpers'; import { faAngleDown } from 'icons/solid'; import { activeNetworkSelector, defaultNetworkSelector } from 'redux/selectors'; @@ -31,6 +31,66 @@ export const Switcher = () => { }; }); + const LinksList = () => { + return ( +
+ {links.length > 0 ? ( + <> + {links.map((link) => ( + + {link.name} + + ))} + + ) : ( + <> + {isSubSubdomain && window?.location?.hostname ? ( + <> + {networkLinks.map((link) => ( + + {link.name} + + ))} + + ) : ( + <> + {networkLinks.map((link) => ( + + {link.name} + + ))} + + )} + + )} +
+ ); + }; + return ( { size='lg' /> - - - {links.length > 0 ? ( - <> - {links.map((link) => ( - - {link.name} - - ))} - +
+ {hasExtraNetworks ? ( + + + ) : ( - <> - {isSubSubdomain && window?.location?.hostname ? ( - <> - {networkLinks.map((link) => ( - - {link.name} - - ))} - - ) : ( - <> - {networkLinks.map((link) => ( - - {link.name} - - ))} - - )} - + )} - +
); diff --git a/src/layouts/Layout/components/Header/components/Switcher/switcher.styles.scss b/src/layouts/Layout/components/Header/components/Switcher/switcher.styles.scss index 55b81da30..2a91284ed 100644 --- a/src/layouts/Layout/components/Header/components/Switcher/switcher.styles.scss +++ b/src/layouts/Layout/components/Header/components/Switcher/switcher.styles.scss @@ -52,6 +52,24 @@ } .dropdown-menu { + .network-switch-list { + min-height: 20rem; + max-height: calc(100dvh - #{$header-navbar-height} - 6rem); + overflow-y: scroll; + @include media-breakpoint-up(lg) { + max-height: calc(100dvh - #{$header-navbar-height} - 1rem); + } + } + .custom-network-menu { + .network-list { + @include media-breakpoint-up(lg) { + min-height: 14rem; + max-height: calc(100dvh - #{$header-navbar-height} - 8.25rem); + overflow-y: scroll; + } + } + } + .dropdown-item { --dropdown-link-color: var(--body-color); --dropdown-link-hover-color: var(--primary); From 25d89326ba3d9c800b7ca2102c3ea42eca9e18af Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Fri, 23 Aug 2024 18:28:11 +0300 Subject: [PATCH 06/15] deploy from the custom-api branch --- .github/workflows/deploy-internal.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-internal.yml b/.github/workflows/deploy-internal.yml index 376774832..6331b4323 100644 --- a/.github/workflows/deploy-internal.yml +++ b/.github/workflows/deploy-internal.yml @@ -2,7 +2,7 @@ name: deploy-internal on: push: - branches: [development] + branches: [custom-api] repository_dispatch: types: deploy-internal workflow_dispatch: @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v2 with: - ref: development + ref: custom-api - name: Use Node.js uses: actions/setup-node@v1 with: From 202efe79a60cceb07d7e142e6565aa6e4e718b3e Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 26 Aug 2024 13:41:20 +0300 Subject: [PATCH 07/15] custom network support on network sub-subdomains --- .../CustomNetwork/CustomNetworkDetails.tsx | 6 +++++- src/hooks/index.ts | 1 + src/hooks/useCustomNetwork.ts | 10 +++++++--- src/hooks/useGetNetworkChangeLink.ts | 20 +++++++++++++++++++ .../Header/components/Switcher/Switcher.tsx | 11 ++++------ 5 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 src/hooks/useGetNetworkChangeLink.ts diff --git a/src/components/CustomNetwork/CustomNetworkDetails.tsx b/src/components/CustomNetwork/CustomNetworkDetails.tsx index 3f5bf4919..52b510a71 100644 --- a/src/components/CustomNetwork/CustomNetworkDetails.tsx +++ b/src/components/CustomNetwork/CustomNetworkDetails.tsx @@ -8,6 +8,7 @@ import { CUSTOM_NETWORK_ID } from 'appConstants'; import { CollapsibleArrows, CopyButton } from 'components'; import { networks } from 'config'; import { storage, scrollToElement } from 'helpers'; +import { useGetNetworkChangeLink } from 'hooks'; import { faTrash, faCheck } from 'icons/regular'; import { activeNetworkSelector } from 'redux/selectors'; import { WithClassnameType } from 'types'; @@ -28,6 +29,7 @@ const NetworkDetail = ({ }; export const CustomNetworkDetails = ({ className }: WithClassnameType) => { + const getNetworkChangeLink = useGetNetworkChangeLink(); const activeNetwork = useSelector(activeNetworkSelector); const { isCustom: activeNetworkIsCustom } = activeNetwork; @@ -47,7 +49,9 @@ export const CustomNetworkDetails = ({ className }: WithClassnameType) => { }; const applyNetwork = () => { - window.location.href = `/${CUSTOM_NETWORK_ID}`; + window.location.href = getNetworkChangeLink({ + networkId: CUSTOM_NETWORK_ID + }); }; if (!existingCustomNetwork) { diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 9d4e65c90..999449b40 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -9,6 +9,7 @@ export * from './useCustomNetwork'; export * from './useDebounce'; export * from './useGetExplorerTitle'; export * from './useGetHash'; +export * from './useGetNetworkChangeLink'; export * from './useGetNodesCategoryCount'; export * from './useGetShardText'; export * from './useGetEpochRemainingTime'; diff --git a/src/hooks/useCustomNetwork.ts b/src/hooks/useCustomNetwork.ts index e28b96d73..611382ce5 100644 --- a/src/hooks/useCustomNetwork.ts +++ b/src/hooks/useCustomNetwork.ts @@ -5,7 +5,7 @@ import { useSelector } from 'react-redux'; import { CUSTOM_NETWORK_ID } from 'appConstants'; import { networks } from 'config'; import { storage } from 'helpers'; -import { useAdapter } from 'hooks'; +import { useAdapter, useGetNetworkChangeLink } from 'hooks'; import { activeNetworkSelector } from 'redux/selectors'; import { DappNetworkConfigType, NetworkType, NetworkAdapterEnum } from 'types'; @@ -31,8 +31,10 @@ const validateUrl = (url: string) => { }; export const useCustomNetwork = (customUrl: string) => { - const activeNetwork = useSelector(activeNetworkSelector); const { getNetworkConfig } = useAdapter(); + const getNetworkChangeLink = useGetNetworkChangeLink(); + const activeNetwork = useSelector(activeNetworkSelector); + const { isCustom: activeNetworkIsCustom } = activeNetwork; const configCustomNetwork = networks.filter((network) => network.isCustom)[0]; @@ -99,7 +101,9 @@ export const useCustomNetwork = (customUrl: string) => { setIsSaving(false); // we want to reset the whole state, react router's navigate might lead to unwanted innacuracies - window.location.href = `/${CUSTOM_NETWORK_ID}`; + window.location.href = getNetworkChangeLink({ + networkId: CUSTOM_NETWORK_ID + }); return; } diff --git a/src/hooks/useGetNetworkChangeLink.ts b/src/hooks/useGetNetworkChangeLink.ts new file mode 100644 index 000000000..37ed220d7 --- /dev/null +++ b/src/hooks/useGetNetworkChangeLink.ts @@ -0,0 +1,20 @@ +import { useSelector } from 'react-redux'; + +import { getSubdomainNetwork } from 'helpers'; +import { defaultNetworkSelector } from 'redux/selectors'; + +export const useGetNetworkChangeLink = () => { + const { id: defaultNetworkId } = useSelector(defaultNetworkSelector); + const { isSubSubdomain } = getSubdomainNetwork(); + + const getNetworkChangeLink = ({ networkId }: { networkId?: string }) => { + if (isSubSubdomain && window?.location?.hostname) { + const [_omit, ...rest] = window.location.hostname.split('.'); + return `https://${[networkId, ...rest].join('.')}`; + } + + return networkId === defaultNetworkId ? '/' : `/${networkId}`; + }; + + return getNetworkChangeLink; +}; diff --git a/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx b/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx index d6941d112..ac477844c 100644 --- a/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx +++ b/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx @@ -6,24 +6,21 @@ import { useSelector } from 'react-redux'; import { CustomNetworkMenu } from 'components'; import { networks, links, hasExtraNetworks } from 'config'; import { getSubdomainNetwork } from 'helpers'; +import { useGetNetworkChangeLink } from 'hooks'; import { faAngleDown } from 'icons/solid'; -import { activeNetworkSelector, defaultNetworkSelector } from 'redux/selectors'; +import { activeNetworkSelector } from 'redux/selectors'; export const Switcher = () => { const { id: activeNetworkId, name: activeNetworkName } = useSelector( activeNetworkSelector ); - const { id: defaultNetworkId } = useSelector(defaultNetworkSelector); const { isSubSubdomain } = getSubdomainNetwork(); + const getNetworkChangeLink = useGetNetworkChangeLink(); const networkLinks = networks .filter((network) => !network.isCustom) .map(({ name, id }) => { - let url = id === defaultNetworkId ? '/' : `/${id}`; - if (isSubSubdomain && window?.location?.hostname) { - const [_omit, ...rest] = window.location.hostname.split('.'); - url = `https://${[id, ...rest].join('.')}`; - } + const url = getNetworkChangeLink({ networkId: id }); return { name, url, From 2f85f984ba1af7651c0e83b5fc44a422285cebc5 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 26 Aug 2024 14:01:54 +0300 Subject: [PATCH 08/15] update configs --- src/config/config.devnet.ts | 8 ++++---- src/config/config.multiple.ts | 8 ++++---- src/config/config.testnet.ts | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/config/config.devnet.ts b/src/config/config.devnet.ts index a3ac68bbe..4d30a43b6 100644 --- a/src/config/config.devnet.ts +++ b/src/config/config.devnet.ts @@ -5,9 +5,6 @@ import { allApps, schema } from './sharedConfig'; export * from './sharedConfig'; export const networks: NetworkType[] = [ - // Saved Custom Network Configs - ...getStorageCustomNetworks(), - { default: true, id: 'devnet', @@ -20,7 +17,10 @@ export const networks: NetworkType[] = [ explorerAddress: 'https://devnet-explorer.multiversx.com', nftExplorerAddress: 'https://devnet.xspotlight.com', apiAddress: 'https://devnet-api.multiversx.com' - } + }, + + // Saved Custom Network Configs + ...getStorageCustomNetworks() ]; export const multiversxApps = allApps([ diff --git a/src/config/config.multiple.ts b/src/config/config.multiple.ts index 297afff97..8163decc5 100644 --- a/src/config/config.multiple.ts +++ b/src/config/config.multiple.ts @@ -11,9 +11,6 @@ export * from './sharedConfig'; export const hasExtraNetworks = true; export const networks: NetworkType[] = [ - // Saved Custom Network Configs - ...getStorageCustomNetworks(), - { default: true, id: 'testnet', @@ -41,7 +38,10 @@ export const networks: NetworkType[] = [ }, // Internal Testnets - ...getInternalNetworks() + ...getInternalNetworks(), + + // Saved Custom Network Configs + ...getStorageCustomNetworks() ]; export const links = getInternalLinks(networks); diff --git a/src/config/config.testnet.ts b/src/config/config.testnet.ts index a215800b7..d2fb9f75e 100644 --- a/src/config/config.testnet.ts +++ b/src/config/config.testnet.ts @@ -5,9 +5,6 @@ import { allApps, schema } from './sharedConfig'; export * from './sharedConfig'; export const networks: NetworkType[] = [ - // Saved Custom Network Configs - ...getStorageCustomNetworks(), - { default: true, id: 'testnet', @@ -20,7 +17,10 @@ export const networks: NetworkType[] = [ explorerAddress: 'https://testnet-explorer.multiversx.com', nftExplorerAddress: 'https://testnet.xspotlight.com', apiAddress: 'https://testnet-api.multiversx.com' - } + }, + + // Saved Custom Network Configs + ...getStorageCustomNetworks() ]; export const multiversxApps = allApps([ From ca8e5de3148a167c89600cfed245c28d4049cd92 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 26 Aug 2024 14:07:03 +0300 Subject: [PATCH 09/15] don't show custommnetwork on internal network links --- src/config/helpers/getInternalLinks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/helpers/getInternalLinks.ts b/src/config/helpers/getInternalLinks.ts index cc681d669..0ccfd596c 100644 --- a/src/config/helpers/getInternalLinks.ts +++ b/src/config/helpers/getInternalLinks.ts @@ -6,7 +6,7 @@ export const getInternalLinks = (networks: NetworkType[]): NetworkUrlType[] => { process.env.VITE_APP_SHARE_PREFIX === 'internal-' ) { const internalLinks = networks - .filter(({ id, name }) => id && name) + .filter(({ id, name, isCustom }) => id && name && !isCustom) .map(({ id = '', name = '' }) => { return { id, From e2e5dc60e3cd437fa04730d97d835bf909f05bf3 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 26 Aug 2024 14:23:22 +0300 Subject: [PATCH 10/15] reset to the default network on custom network removal --- src/components/CustomNetwork/CustomNetworkDetails.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/CustomNetwork/CustomNetworkDetails.tsx b/src/components/CustomNetwork/CustomNetworkDetails.tsx index 52b510a71..36eafacf3 100644 --- a/src/components/CustomNetwork/CustomNetworkDetails.tsx +++ b/src/components/CustomNetwork/CustomNetworkDetails.tsx @@ -10,7 +10,7 @@ import { networks } from 'config'; import { storage, scrollToElement } from 'helpers'; import { useGetNetworkChangeLink } from 'hooks'; import { faTrash, faCheck } from 'icons/regular'; -import { activeNetworkSelector } from 'redux/selectors'; +import { activeNetworkSelector, defaultNetworkSelector } from 'redux/selectors'; import { WithClassnameType } from 'types'; const NetworkDetail = ({ @@ -31,6 +31,7 @@ const NetworkDetail = ({ export const CustomNetworkDetails = ({ className }: WithClassnameType) => { const getNetworkChangeLink = useGetNetworkChangeLink(); const activeNetwork = useSelector(activeNetworkSelector); + const defaultNetwork = useSelector(defaultNetworkSelector); const { isCustom: activeNetworkIsCustom } = activeNetwork; const configCustomNetwork = networks.filter((network) => network.isCustom)[0]; @@ -42,10 +43,13 @@ export const CustomNetworkDetails = ({ className }: WithClassnameType) => { const isSavedCustomNetworkActive = configCustomNetwork?.id === activeNetwork?.id; + const defaultNetworkId = defaultNetwork.id ?? networks[0]?.id; const removeNetwork = () => { storage.removeFromLocal(CUSTOM_NETWORK_ID); - window.location.href = '/'; + window.location.href = getNetworkChangeLink({ + networkId: defaultNetworkId + }); }; const applyNetwork = () => { From df3ae659c3a6e402b24cf92e4c09995cb7c987c0 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 26 Aug 2024 15:28:27 +0300 Subject: [PATCH 11/15] change custom network across sub-subdomains --- .../helpers/getStorageCustomNetworks.ts | 22 +++++++++++ src/helpers/cookie.ts | 37 +++++++++++++++++++ src/helpers/index.ts | 1 + src/hooks/useCustomNetwork.ts | 14 +++++-- 4 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 src/helpers/cookie.ts diff --git a/src/config/helpers/getStorageCustomNetworks.ts b/src/config/helpers/getStorageCustomNetworks.ts index c26ec1ca3..3d0e8878a 100644 --- a/src/config/helpers/getStorageCustomNetworks.ts +++ b/src/config/helpers/getStorageCustomNetworks.ts @@ -1,5 +1,8 @@ +import moment from 'moment'; + import { CUSTOM_NETWORK_ID } from 'appConstants'; import { hasExtraNetworks } from 'config'; +import { cookie } from 'helpers/cookie'; import { storage } from 'helpers/storage'; import { NetworkAdapterEnum, NetworkType } from 'types'; @@ -9,6 +12,25 @@ export const getStorageCustomNetworks = (): NetworkType[] => { } try { + const cookieNetworks = cookie.getFromCookies(CUSTOM_NETWORK_ID); + + // change custom network across sub-subdomains + if (cookieNetworks) { + try { + const parsedCookieNetworks = JSON.parse(cookieNetworks); + if (parsedCookieNetworks && parsedCookieNetworks.length > 0) { + const in30Days = new Date(moment().add(30, 'days').toDate()); + storage.saveToLocal({ + key: CUSTOM_NETWORK_ID, + data: JSON.stringify(parsedCookieNetworks), + expirationDate: in30Days + }); + + cookie.removeFromCookies(CUSTOM_NETWORK_ID); + } + } catch {} + } + const storageNetworks = storage.getFromLocal(CUSTOM_NETWORK_ID); const parsedNetworks = JSON.parse(storageNetworks); diff --git a/src/helpers/cookie.ts b/src/helpers/cookie.ts new file mode 100644 index 000000000..24e3531bd --- /dev/null +++ b/src/helpers/cookie.ts @@ -0,0 +1,37 @@ +import { CUSTOM_NETWORK_ID } from 'appConstants'; + +type KeyType = typeof CUSTOM_NETWORK_ID; + +const domain = `domain=.${process.env.VITE_APP_SHARE_PREFIX}explorer.multiversx.com`; + +export const cookie = { + saveToCookies: ({ + key, + data, + expirationDate + }: { + key: KeyType; + data: string; + expirationDate: Date; + }) => { + const expires = `expires=${expirationDate.toUTCString()}`; + document.cookie = `${key}=${data}; ${expires}; ${domain} path=/;`; + }, + getFromCookies: (key: KeyType) => { + const name = key + '='; + const cookies = document.cookie.split(';'); + for (let i = 0; i < cookies.length; i++) { + let c = cookies[i]; + while (c.charAt(0) == ' ') { + c = c.substring(1); + } + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + return ''; + }, + removeFromCookies: (key: KeyType) => { + document.cookie = `${key}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; ${domain} path=/;`; + } +}; diff --git a/src/helpers/index.ts b/src/helpers/index.ts index 52edc6d9c..e51b46834 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -5,6 +5,7 @@ export * from './amountWithoutRounding'; export * from './analytics'; export * from './capitalize'; export * from './capitalizeFirstLetter'; +export * from './cookie'; export * from './copyToClipboard'; export * from './downloadFile'; export * from './formatValue'; diff --git a/src/hooks/useCustomNetwork.ts b/src/hooks/useCustomNetwork.ts index 611382ce5..6332e4007 100644 --- a/src/hooks/useCustomNetwork.ts +++ b/src/hooks/useCustomNetwork.ts @@ -4,7 +4,7 @@ import { useSelector } from 'react-redux'; import { CUSTOM_NETWORK_ID } from 'appConstants'; import { networks } from 'config'; -import { storage } from 'helpers'; +import { cookie, storage, getSubdomainNetwork } from 'helpers'; import { useAdapter, useGetNetworkChangeLink } from 'hooks'; import { activeNetworkSelector } from 'redux/selectors'; import { DappNetworkConfigType, NetworkType, NetworkAdapterEnum } from 'types'; @@ -33,6 +33,7 @@ const validateUrl = (url: string) => { export const useCustomNetwork = (customUrl: string) => { const { getNetworkConfig } = useAdapter(); const getNetworkChangeLink = useGetNetworkChangeLink(); + const { isSubSubdomain } = getSubdomainNetwork(); const activeNetwork = useSelector(activeNetworkSelector); const { isCustom: activeNetworkIsCustom } = activeNetwork; @@ -83,11 +84,16 @@ export const useCustomNetwork = (customUrl: string) => { try { const in30Days = new Date(moment().add(30, 'days').toDate()); - storage.saveToLocal({ - key: CUSTOM_NETWORK_ID, + const configData = { + key: CUSTOM_NETWORK_ID as typeof CUSTOM_NETWORK_ID, data: JSON.stringify([customNetwork]), expirationDate: in30Days - }); + }; + if (isSubSubdomain) { + cookie.saveToCookies(configData); + } else { + storage.saveToLocal(configData); + } } catch (error) { console.error('Unable to Save Custom Network: ', error); setErrors((errors) => { From 46c9b9443b5965cecc0489daac7359934c432b57 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 26 Aug 2024 15:55:06 +0300 Subject: [PATCH 12/15] change custom network across sub-subdomains using a subdomain restricted temporary cookie --- src/config/helpers/getInternalLinks.ts | 3 ++- src/config/sharedConfig.ts | 3 +++ src/helpers/cookie.ts | 7 ++++--- src/hooks/useCustomNetwork.ts | 3 ++- src/redux/slices/metaTags.ts | 4 ++-- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/config/helpers/getInternalLinks.ts b/src/config/helpers/getInternalLinks.ts index 0ccfd596c..8b226289e 100644 --- a/src/config/helpers/getInternalLinks.ts +++ b/src/config/helpers/getInternalLinks.ts @@ -1,3 +1,4 @@ +import { DEFAULT_HOSTNAME } from 'config'; import { NetworkType, NetworkUrlType } from 'types/network.types'; export const getInternalLinks = (networks: NetworkType[]): NetworkUrlType[] => { @@ -11,7 +12,7 @@ export const getInternalLinks = (networks: NetworkType[]): NetworkUrlType[] => { return { id, name, - url: `https://${id}.${process.env.VITE_APP_SHARE_PREFIX}explorer.multiversx.com` + url: `https://${id}.${process.env.VITE_APP_SHARE_PREFIX}${DEFAULT_HOSTNAME}` }; }); diff --git a/src/config/sharedConfig.ts b/src/config/sharedConfig.ts index 0d2a97901..bddd0b56d 100644 --- a/src/config/sharedConfig.ts +++ b/src/config/sharedConfig.ts @@ -31,6 +31,9 @@ export const SHARE_PREFIX = process.env.VITE_APP_SHARE_PREFIX ? process.env.VITE_APP_SHARE_PREFIX.replace('-', '') : ''; +export const DEFAULT_HOSTNAME = + process.env.VITE_APP_DEFAULT_HOSTNAME ?? 'explorer.multiversx.com'; + export const hasExtraNetworks = false; export const links: NetworkUrlType[] = [ diff --git a/src/helpers/cookie.ts b/src/helpers/cookie.ts index 24e3531bd..80a47b499 100644 --- a/src/helpers/cookie.ts +++ b/src/helpers/cookie.ts @@ -1,8 +1,9 @@ import { CUSTOM_NETWORK_ID } from 'appConstants'; +import { DEFAULT_HOSTNAME } from 'config'; type KeyType = typeof CUSTOM_NETWORK_ID; -const domain = `domain=.${process.env.VITE_APP_SHARE_PREFIX}explorer.multiversx.com`; +const domain = `domain=.${process.env.VITE_APP_SHARE_PREFIX}${DEFAULT_HOSTNAME}`; export const cookie = { saveToCookies: ({ @@ -15,7 +16,7 @@ export const cookie = { expirationDate: Date; }) => { const expires = `expires=${expirationDate.toUTCString()}`; - document.cookie = `${key}=${data}; ${expires}; ${domain} path=/;`; + document.cookie = `${key}=${data}; ${expires}; ${domain}; path=/;`; }, getFromCookies: (key: KeyType) => { const name = key + '='; @@ -32,6 +33,6 @@ export const cookie = { return ''; }, removeFromCookies: (key: KeyType) => { - document.cookie = `${key}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; ${domain} path=/;`; + document.cookie = `${key}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; ${domain}; path=/;`; } }; diff --git a/src/hooks/useCustomNetwork.ts b/src/hooks/useCustomNetwork.ts index 6332e4007..5a5bdce32 100644 --- a/src/hooks/useCustomNetwork.ts +++ b/src/hooks/useCustomNetwork.ts @@ -83,11 +83,12 @@ export const useCustomNetwork = (customUrl: string) => { }; try { + const in2Minutes = new Date(moment().add(2, 'minutes').toDate()); const in30Days = new Date(moment().add(30, 'days').toDate()); const configData = { key: CUSTOM_NETWORK_ID as typeof CUSTOM_NETWORK_ID, data: JSON.stringify([customNetwork]), - expirationDate: in30Days + expirationDate: isSubSubdomain ? in2Minutes : in30Days }; if (isSubSubdomain) { cookie.saveToCookies(configData); diff --git a/src/redux/slices/metaTags.ts b/src/redux/slices/metaTags.ts index dfd916b86..9f1bf06a7 100644 --- a/src/redux/slices/metaTags.ts +++ b/src/redux/slices/metaTags.ts @@ -1,7 +1,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { BRAND_NAME } from 'appConstants'; -import { SHARE_PREFIX } from 'config'; +import { DEFAULT_HOSTNAME, SHARE_PREFIX } from 'config'; import { capitalize } from 'helpers'; import { MetaTagsType } from 'types/metaTags.types'; @@ -10,7 +10,7 @@ const DEFAULT_TITLE = `${BRAND_NAME}${ } Explorer`; const DEFAULT_DESCRIPTION = 'A highly scalable, fast and secure blockchain platform for distributed apps, enterprise use cases and the new internet economy.'; -const DEFAULT_PREVIEW = `https://${process.env.VITE_APP_SHARE_PREFIX}explorer.multiversx.com/${process.env.VITE_APP_SHARE_PREFIX}share.jpg`; +const DEFAULT_PREVIEW = `https://${process.env.VITE_APP_SHARE_PREFIX}${DEFAULT_HOSTNAME}/${process.env.VITE_APP_SHARE_PREFIX}share.jpg`; export const getInitialMetaTagsState = (): MetaTagsType => { return { From 4bba7098bede6aaf47d498620b061f0a2de52a67 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 26 Aug 2024 16:13:06 +0300 Subject: [PATCH 13/15] reset to default network --- src/components/CustomNetwork/CustomNetworkDetails.tsx | 6 +++--- src/helpers/cookie.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/CustomNetwork/CustomNetworkDetails.tsx b/src/components/CustomNetwork/CustomNetworkDetails.tsx index 36eafacf3..6ceb3a219 100644 --- a/src/components/CustomNetwork/CustomNetworkDetails.tsx +++ b/src/components/CustomNetwork/CustomNetworkDetails.tsx @@ -10,7 +10,7 @@ import { networks } from 'config'; import { storage, scrollToElement } from 'helpers'; import { useGetNetworkChangeLink } from 'hooks'; import { faTrash, faCheck } from 'icons/regular'; -import { activeNetworkSelector, defaultNetworkSelector } from 'redux/selectors'; +import { activeNetworkSelector } from 'redux/selectors'; import { WithClassnameType } from 'types'; const NetworkDetail = ({ @@ -31,7 +31,6 @@ const NetworkDetail = ({ export const CustomNetworkDetails = ({ className }: WithClassnameType) => { const getNetworkChangeLink = useGetNetworkChangeLink(); const activeNetwork = useSelector(activeNetworkSelector); - const defaultNetwork = useSelector(defaultNetworkSelector); const { isCustom: activeNetworkIsCustom } = activeNetwork; const configCustomNetwork = networks.filter((network) => network.isCustom)[0]; @@ -43,7 +42,8 @@ export const CustomNetworkDetails = ({ className }: WithClassnameType) => { const isSavedCustomNetworkActive = configCustomNetwork?.id === activeNetwork?.id; - const defaultNetworkId = defaultNetwork.id ?? networks[0]?.id; + const defaultNetwork = networks.find((network) => Boolean(network.default)); + const defaultNetworkId = defaultNetwork?.id ?? networks[0]?.id; const removeNetwork = () => { storage.removeFromLocal(CUSTOM_NETWORK_ID); diff --git a/src/helpers/cookie.ts b/src/helpers/cookie.ts index 80a47b499..5cbefd48b 100644 --- a/src/helpers/cookie.ts +++ b/src/helpers/cookie.ts @@ -1,5 +1,5 @@ import { CUSTOM_NETWORK_ID } from 'appConstants'; -import { DEFAULT_HOSTNAME } from 'config'; +import { DEFAULT_HOSTNAME } from 'config/sharedConfig'; type KeyType = typeof CUSTOM_NETWORK_ID; From a6b61d38c5a52be3eaf6e1c9c244a9b468a41167 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 26 Aug 2024 16:18:26 +0300 Subject: [PATCH 14/15] deploy internal from development --- .github/workflows/deploy-internal.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-internal.yml b/.github/workflows/deploy-internal.yml index 6331b4323..376774832 100644 --- a/.github/workflows/deploy-internal.yml +++ b/.github/workflows/deploy-internal.yml @@ -2,7 +2,7 @@ name: deploy-internal on: push: - branches: [custom-api] + branches: [development] repository_dispatch: types: deploy-internal workflow_dispatch: @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v2 with: - ref: custom-api + ref: development - name: Use Node.js uses: actions/setup-node@v1 with: From f812edbeed82490d600efb79a38b10bf7fd16aa7 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 26 Aug 2024 16:27:21 +0300 Subject: [PATCH 15/15] fix styling --- .../Header/components/Switcher/switcher.styles.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layouts/Layout/components/Header/components/Switcher/switcher.styles.scss b/src/layouts/Layout/components/Header/components/Switcher/switcher.styles.scss index 2a91284ed..f6cbf049b 100644 --- a/src/layouts/Layout/components/Header/components/Switcher/switcher.styles.scss +++ b/src/layouts/Layout/components/Header/components/Switcher/switcher.styles.scss @@ -53,7 +53,7 @@ .dropdown-menu { .network-switch-list { - min-height: 20rem; + min-height: 2rem; max-height: calc(100dvh - #{$header-navbar-height} - 6rem); overflow-y: scroll; @include media-breakpoint-up(lg) { @@ -63,7 +63,7 @@ .custom-network-menu { .network-list { @include media-breakpoint-up(lg) { - min-height: 14rem; + min-height: 2rem; max-height: calc(100dvh - #{$header-navbar-height} - 8.25rem); overflow-y: scroll; }