From 16383f145ccec4b76315218210a00663513d4092 Mon Sep 17 00:00:00 2001
From: Chris <krzysztof@ssvlabs.io>
Date: Mon, 1 Jul 2024 21:18:23 +0200
Subject: [PATCH] Implementation of Network Switch and Clean up of Prater &
 Goerli

---
 .env.development                              |  2 +-
 public/images/networks/holesky_dark.svg       | 15 ++++
 public/images/networks/holesky_light.svg      | 15 ++++
 public/images/networks/mainnet_dark.svg       | 15 ++++
 public/images/networks/mainnet_light.svg      | 15 ++++
 .../common/components/AppBar/AppBar.styles.ts | 13 +---
 src/app/common/components/AppBar/AppBar.tsx   |  6 +-
 .../components/NetworkIcon/NetworkIcon.tsx    | 16 ++++
 .../common/components/NetworkIcon/index.ts    |  1 +
 .../NetworkSelect/NetworkSelect.tsx           | 77 +++++++++++++++++++
 .../NetworkSelect/NeworkSelect.styles.ts      | 57 ++++++++++++++
 .../common/components/NetworkSelect/index.tsx |  1 +
 src/lib/utils/ChainService.ts                 |  4 -
 13 files changed, 220 insertions(+), 17 deletions(-)
 create mode 100644 public/images/networks/holesky_dark.svg
 create mode 100644 public/images/networks/holesky_light.svg
 create mode 100644 public/images/networks/mainnet_dark.svg
 create mode 100644 public/images/networks/mainnet_light.svg
 create mode 100644 src/app/common/components/NetworkIcon/NetworkIcon.tsx
 create mode 100644 src/app/common/components/NetworkIcon/index.ts
 create mode 100644 src/app/common/components/NetworkSelect/NetworkSelect.tsx
 create mode 100644 src/app/common/components/NetworkSelect/NeworkSelect.styles.ts
 create mode 100644 src/app/common/components/NetworkSelect/index.tsx

diff --git a/.env.development b/.env.development
index 4e536bf..2e0abeb 100644
--- a/.env.development
+++ b/.env.development
@@ -1,5 +1,5 @@
 REACT_APP_DEBUG=true
-REACT_APP_API_BASE_URL=https://api.stage.ssv.network/api/v4/prater
+REACT_APP_API_BASE_URL=https://api.stage.ssv.network/api/v4/holesky
 REACT_APP_LINK_SSV_WEBAPP=https://app.stage.ssv.network/
 REACT_APP_GOOGLE_TAG_SECRET=
 REACT_APP_GOOGLE_TAG_URL=
diff --git a/public/images/networks/holesky_dark.svg b/public/images/networks/holesky_dark.svg
new file mode 100644
index 0000000..82117cb
--- /dev/null
+++ b/public/images/networks/holesky_dark.svg
@@ -0,0 +1,15 @@
+<svg width="16" height="24" viewBox="0 0 16 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Frame" clip-path="url(#clip0_21118_537)">
+<path id="Vector" opacity="0.54" d="M7.9704 0V8.9L0.604253 12.2225L7.9704 0Z" fill="#76C8FB"/>
+<path id="Vector_2" opacity="0.7" d="M7.9704 0L15.3367 12.2225L7.9704 8.9V0Z" fill="#76C8FB"/>
+<path id="Vector_3" opacity="0.54" d="M7.97046 17.9714V23.9999L0.599816 13.6195L7.97046 17.9714Z" fill="#76C8FB"/>
+<path id="Vector_4" opacity="0.7" d="M7.9704 23.9999V17.9714L15.3367 13.6195L7.9704 23.9999Z" fill="#76C8FB"/>
+<path id="Vector_5" opacity="0.4" d="M7.97046 16.5767L0.604313 12.2225L7.97046 8.90002V16.5767Z" fill="#76C8FB"/>
+<path id="Vector_6" opacity="0.5" d="M15.3367 12.2225L7.9704 16.5767V8.90002L15.3367 12.2225Z" fill="#76C8FB"/>
+</g>
+<defs>
+<clipPath id="clip0_21118_537">
+<rect width="14.7369" height="23.9999" fill="white" transform="matrix(-1 0 0 1 15.3367 0)"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/public/images/networks/holesky_light.svg b/public/images/networks/holesky_light.svg
new file mode 100644
index 0000000..dc674a0
--- /dev/null
+++ b/public/images/networks/holesky_light.svg
@@ -0,0 +1,15 @@
+<svg width="16" height="24" viewBox="0 0 16 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Frame" clip-path="url(#clip0_21118_1191)">
+<path id="Vector" opacity="0.54" d="M7.9704 0V8.9L0.604253 12.2225L7.9704 0Z" fill="#76C8FB"/>
+<path id="Vector_2" opacity="0.7" d="M7.9704 0L15.3367 12.2225L7.9704 8.9V0Z" fill="#76C8FB"/>
+<path id="Vector_3" opacity="0.54" d="M7.97046 17.9714V23.9999L0.599816 13.6195L7.97046 17.9714Z" fill="#76C8FB"/>
+<path id="Vector_4" opacity="0.7" d="M7.9704 23.9999V17.9714L15.3367 13.6195L7.9704 23.9999Z" fill="#76C8FB"/>
+<path id="Vector_5" opacity="0.4" d="M7.97046 16.5767L0.604313 12.2225L7.97046 8.90002V16.5767Z" fill="#76C8FB"/>
+<path id="Vector_6" opacity="0.5" d="M15.3367 12.2225L7.9704 16.5767V8.90002L15.3367 12.2225Z" fill="#76C8FB"/>
+</g>
+<defs>
+<clipPath id="clip0_21118_1191">
+<rect width="14.7369" height="23.9999" fill="white" transform="matrix(-1 0 0 1 15.3367 0)"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/public/images/networks/mainnet_dark.svg b/public/images/networks/mainnet_dark.svg
new file mode 100644
index 0000000..4b924d4
--- /dev/null
+++ b/public/images/networks/mainnet_dark.svg
@@ -0,0 +1,15 @@
+<svg width="16" height="24" viewBox="0 0 16 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Frame" clip-path="url(#clip0_21111_1165)">
+<path id="Vector" opacity="0.6" d="M7.9704 0V8.9L0.604252 12.2225L7.9704 0Z" fill="#011627"/>
+<path id="Vector_2" opacity="0.8" d="M7.9704 0L15.3367 12.2225L7.9704 8.9V0Z" fill="#011627"/>
+<path id="Vector_3" opacity="0.6" d="M7.97046 17.9715V23.9999L0.599816 13.6195L7.97046 17.9715Z" fill="#011627"/>
+<path id="Vector_4" opacity="0.8" d="M7.9704 23.9999V17.9715L15.3367 13.6195L7.9704 23.9999Z" fill="#011627"/>
+<path id="Vector_5" opacity="0.8" d="M7.97046 16.5767L0.604313 12.2225L7.97046 8.90002V16.5767Z" fill="#011627"/>
+<path id="Vector_6" d="M15.3367 12.2225L7.9704 16.5767V8.90002L15.3367 12.2225Z" fill="#011627"/>
+</g>
+<defs>
+<clipPath id="clip0_21111_1165">
+<rect width="14.7369" height="23.9999" fill="white" transform="matrix(-1 0 0 1 15.3367 0)"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/public/images/networks/mainnet_light.svg b/public/images/networks/mainnet_light.svg
new file mode 100644
index 0000000..7be232b
--- /dev/null
+++ b/public/images/networks/mainnet_light.svg
@@ -0,0 +1,15 @@
+<svg width="16" height="24" viewBox="0 0 16 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Frame" clip-path="url(#clip0_21111_511)">
+<path id="Vector" opacity="0.64" d="M7.96613 0V8.9L15.3323 12.2225L7.96613 0Z" fill="#FDFEFE"/>
+<path id="Vector_2" opacity="0.84" d="M7.96613 0L0.599792 12.2225L7.96613 8.9V0Z" fill="#FDFEFE"/>
+<path id="Vector_3" opacity="0.64" d="M7.96606 17.9714V23.9999L15.3367 13.6195L7.96606 17.9714Z" fill="#FDFEFE"/>
+<path id="Vector_4" opacity="0.84" d="M7.96613 23.9999V17.9714L0.599792 13.6195L7.96613 23.9999Z" fill="#FDFEFE"/>
+<path id="Vector_5" opacity="0.84" d="M7.96606 16.5767L15.3322 12.2225L7.96606 8.90002V16.5767Z" fill="#FDFEFE"/>
+<path id="Vector_6" d="M0.599792 12.2225L7.96613 16.5767V8.90002L0.599792 12.2225Z" fill="#FDFEFE"/>
+</g>
+<defs>
+<clipPath id="clip0_21111_511">
+<rect width="14.7369" height="23.9999" fill="white" transform="translate(0.599792)"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/src/app/common/components/AppBar/AppBar.styles.ts b/src/app/common/components/AppBar/AppBar.styles.ts
index 3731bc1..6e7cd7c 100644
--- a/src/app/common/components/AppBar/AppBar.styles.ts
+++ b/src/app/common/components/AppBar/AppBar.styles.ts
@@ -132,6 +132,9 @@ export const useStyles = makeStyles((theme) => ({
             textDecoration: 'none',
         },
     },
+    networkSwitchWrapper: {
+        paddingRight: 12,
+    },
     drawer: {
         '& > .MuiDrawer-paper': {
             backgroundColor: '#A1ABBE',
@@ -167,14 +170,4 @@ export const useStyles = makeStyles((theme) => ({
             backgroundColor: 'lightgreen',
         },
     },
-    PraterButton: {
-       backgroundColor: theme.colors.tint80,
-       color: theme.colors.primary,
-        '&:hover': {
-           cursor: 'default',
-        },
-        '&:active': {
-            backgroundColor: theme.colors.tint80,
-        },
-    },
 }));
diff --git a/src/app/common/components/AppBar/AppBar.tsx b/src/app/common/components/AppBar/AppBar.tsx
index b9deded..0142a27 100644
--- a/src/app/common/components/AppBar/AppBar.tsx
+++ b/src/app/common/components/AppBar/AppBar.tsx
@@ -25,6 +25,7 @@ import { useStyles as useAppStyles } from '~app/components/Styles';
 import ApplicationStore from '~app/common/stores/Application.store';
 import DarkModeSwitcher from '~app/common/components/DarkModeSwitcher';
 import Grid from '@material-ui/core/Grid';
+import NetworkSelect from '~app/common/components/NetworkSelect';
 
 const DrawerButtonsContainers = styled.div`
   font-size: 12px;
@@ -165,11 +166,12 @@ const AppBarComponent = () => {
                         display={{ xs: 'none', sm: 'none', md: 'none', lg: 'block' }}>
                         <div className={classes.toolbarButtons}>
                           {!isOverviewPage() && <SmartSearch placeholder={'Search...'} inAppBar />}
+                          <div className={classes.networkSwitchWrapper}>
+                            <NetworkSelect />
+                          </div>
                           <Link href={joinSsvLink} target="_blank">
                             <Button disable={false} type={'primary'} text={'Join SSV Network'} />
                           </Link>
-                          <Button disable={false} extendClass={classes.PraterButton} type={'secondary'}
-                            text={`${capitalize(currentNetwork)}`} />
                           <DarkModeSwitcher style={{ marginLeft: 'auto', marginRight: 0, minWidth: 'auto', width: 70 }} />
                         </div>
                       </Box>
diff --git a/src/app/common/components/NetworkIcon/NetworkIcon.tsx b/src/app/common/components/NetworkIcon/NetworkIcon.tsx
new file mode 100644
index 0000000..683758a
--- /dev/null
+++ b/src/app/common/components/NetworkIcon/NetworkIcon.tsx
@@ -0,0 +1,16 @@
+import React, { ImgHTMLAttributes } from 'react';
+import { useStores } from '~app/hooks/useStores';
+import ApplicationStore from '~app/common/stores/Application.store';
+import { EChain } from '~lib/utils/ChainService';
+
+const NetworkIcon = (props: { network: EChain } & ImgHTMLAttributes<unknown>) => {
+  const stores = useStores();
+  const applicationStore: ApplicationStore = stores.Application;
+  const imgSrc = `/images/networks/${props.network}${applicationStore.isDarkMode ? '_light' : '_dark'}.svg`;
+
+  return (
+    <img width="24" height="24" src={imgSrc} alt={props.network} {...props} />
+  );
+};
+
+export default NetworkIcon;
diff --git a/src/app/common/components/NetworkIcon/index.ts b/src/app/common/components/NetworkIcon/index.ts
new file mode 100644
index 0000000..3f03632
--- /dev/null
+++ b/src/app/common/components/NetworkIcon/index.ts
@@ -0,0 +1 @@
+export { default } from './NetworkIcon';
diff --git a/src/app/common/components/NetworkSelect/NetworkSelect.tsx b/src/app/common/components/NetworkSelect/NetworkSelect.tsx
new file mode 100644
index 0000000..9821775
--- /dev/null
+++ b/src/app/common/components/NetworkSelect/NetworkSelect.tsx
@@ -0,0 +1,77 @@
+import React, { ChangeEvent } from 'react';
+import Box from '@material-ui/core/Box';
+import MenuItem from '@material-ui/core/MenuItem';
+import Select from '@material-ui/core/Select';
+import chainService, { EChain } from '~lib/utils/ChainService';
+import NetworkIcon from '~app/common/components/NetworkIcon';
+import { useStyles } from './NeworkSelect.styles';
+import { KeyboardArrowDown } from '@material-ui/icons';
+
+const availableNetworks = [EChain.Ethereum, EChain.Holesky];
+
+const networkToConfigMap = {
+  [EChain.Ethereum]: {
+    url: 'https://explorer.ssv.network',
+    label: 'Ethereum Mainnet',
+  },
+  [EChain.Holesky]: {
+    url: 'https://holesky.explorer.ssv.network',
+    label: 'Holesky Testnet',
+  },
+};
+
+export default function NetworkSelect() {
+  const currentNetwork: EChain = chainService().getNetwork() as EChain;
+
+  const classes = useStyles();
+
+  const handleChange = (
+    event: ChangeEvent<{ name?: string; value: unknown }>,
+  ) => {
+    const urlToGo = networkToConfigMap[event.target.value as EChain].url;
+    if (urlToGo) {
+      window.open(urlToGo, '_self');
+    }
+  };
+
+  return (
+    <Box className={classes.Wrapper}>
+      <Select
+        variant="standard"
+        autoWidth
+        value={currentNetwork}
+        onChange={handleChange}
+        IconComponent={KeyboardArrowDown}
+        className={classes.Root}
+        classes={{
+          select: classes.Select,
+          icon: classes.Icon,
+        }}
+        MenuProps={{
+          anchorOrigin: {
+            vertical: 'bottom',
+            horizontal: -16,
+          },
+          transformOrigin: {
+            vertical: 'top',
+            horizontal: 'left',
+          },
+          getContentAnchorEl: null,
+          classes: {
+            paper: classes.ItemListWrapper,
+            list: classes.ItemList,
+          },
+        }}
+      >
+        {availableNetworks.map((net) => (
+          <MenuItem value={net}>
+            <div className={classes.ItemWrapper}>
+              <NetworkIcon network={net} className={classes.ItemIcon} />
+              {networkToConfigMap[net].label}
+            </div>
+          </MenuItem>
+        ))}
+      </Select>
+    </Box>
+  );
+}
diff --git a/src/app/common/components/NetworkSelect/NeworkSelect.styles.ts b/src/app/common/components/NetworkSelect/NeworkSelect.styles.ts
new file mode 100644
index 0000000..2af6be1
--- /dev/null
+++ b/src/app/common/components/NetworkSelect/NeworkSelect.styles.ts
@@ -0,0 +1,57 @@
+import { makeStyles } from '@material-ui/core/styles';
+
+export const useStyles = makeStyles((theme) => ({
+  Wrapper: {
+    borderRadius: 8,
+    background: theme.colors.gray0,
+    padding: '0 16px',
+  },
+  Root: {
+    minWidth: 205,
+    '&:after, &:before': {
+      display: 'none !important',
+    },
+  },
+  Select: {
+    backgroundColor: 'initial !important',
+  },
+  Icon: {
+    boxSizing: 'content-box',
+    width: 24,
+    height: 24,
+  },
+
+  ItemListWrapper: {
+    width: 240,
+    boxShadow: 'none',
+    border: `1px solid ${theme.colors.gray20}`,
+  },
+
+  ItemList: {
+    backgroundColor: `${theme.colors.white}`,
+    padding: 0,
+    '& li:hover': {
+      backgroundColor: `${theme.colors.gray20} !important`,
+    },
+    '& li': {
+      backgroundColor: `${theme.colors.white} !important`,
+    },
+    '& li:not(:last-child)': {
+      borderBottom: `1px solid ${theme.colors.gray20}`,
+    },
+  },
+
+  ItemWrapper: {
+    display: 'flex',
+    'align-items': 'center',
+    padding: '6px 0',
+    paddingRight: '6px',
+
+    fontSize: 16,
+    fontWeight: 600,
+  },
+  ItemIcon: {
+    boxSizing: 'content-box',
+    paddingRight: '12px',
+  },
+}));
diff --git a/src/app/common/components/NetworkSelect/index.tsx b/src/app/common/components/NetworkSelect/index.tsx
new file mode 100644
index 0000000..e9ee2d3
--- /dev/null
+++ b/src/app/common/components/NetworkSelect/index.tsx
@@ -0,0 +1 @@
+export { default } from './NetworkSelect';
diff --git a/src/lib/utils/ChainService.ts b/src/lib/utils/ChainService.ts
index af4d2f3..767074c 100644
--- a/src/lib/utils/ChainService.ts
+++ b/src/lib/utils/ChainService.ts
@@ -4,15 +4,11 @@ import config from '~app/common/config';
 export enum EChain {
     Holesky = 'holesky',
     Ethereum = 'mainnet', // ethereum
-    Goerli = 'goerli',
-    Prater = 'prater',
 }
 
 export const CHAIN = {
     HOLESKY: EChain.Holesky,
     ETHEREUM: EChain.Ethereum,
-    PRATER: EChain.Prater, // Goerli was merged with Prater. The combined network retained the Goerli name post-merge.
-    GOERLI: EChain.Goerli,
 };
 
 function extractChain(apiUrl: string): string {