From 3455e6333cfb4b6bd0a937ba3dca900c7fe6ed3f Mon Sep 17 00:00:00 2001
From: Kristof Csillag <kristof@oasisprotocol.org>
Date: Tue, 14 Jan 2025 01:49:39 +0100
Subject: [PATCH] Add support for merging all networks in the layer selector

Please note that this only make sense if you don't have
layers that are named the same on multiple networks.

(If you do, you can override names using the spacialScopeNames
config option in config.ts.)
---
 .changelog/1866.trivial.md                      | 1 +
 src/app/components/LayerPicker/LayerDetails.tsx | 9 +++++++++
 src/app/components/LayerPicker/LayerMenu.tsx    | 6 ++++--
 src/app/components/LayerPicker/index.tsx        | 8 +++++---
 src/app/utils/route-utils.ts                    | 2 ++
 src/locales/en/translation.json                 | 1 +
 6 files changed, 22 insertions(+), 5 deletions(-)
 create mode 100644 .changelog/1866.trivial.md

diff --git a/.changelog/1866.trivial.md b/.changelog/1866.trivial.md
new file mode 100644
index 000000000..62152304f
--- /dev/null
+++ b/.changelog/1866.trivial.md
@@ -0,0 +1 @@
+Add support for merging all networks in the layer selector
diff --git a/src/app/components/LayerPicker/LayerDetails.tsx b/src/app/components/LayerPicker/LayerDetails.tsx
index feee98d8b..06cf7bb61 100644
--- a/src/app/components/LayerPicker/LayerDetails.tsx
+++ b/src/app/components/LayerPicker/LayerDetails.tsx
@@ -20,6 +20,7 @@ import { getNameForScope, SearchScope } from '../../../types/searchScope'
 import { useConsensusFreshness, useRuntimeFreshness } from '../OfflineBanner/hook'
 import { LayerStatus } from '../LayerStatus'
 import { useScreenSize } from '../../hooks/useScreensize'
+import { mergeNetworksInLayerSelector } from '../../utils/route-utils'
 
 type LayerDetailsContent = {
   description: string
@@ -154,6 +155,7 @@ const RuntimeDetails: FC<LayerDetailsProps> = props => {
   const { handleConfirm, selectedScope } = props
   const isOutOfDate = useRuntimeFreshness(selectedScope).outOfDate
   const details = getDetails(t)[selectedScope.network]?.[selectedScope.layer]
+  const networkNames = getNetworkNames(t)
 
   return (
     <LayerDetailsSection
@@ -162,6 +164,13 @@ const RuntimeDetails: FC<LayerDetailsProps> = props => {
       isOutOfDate={isOutOfDate}
       selectedScope={selectedScope}
     >
+      {mergeNetworksInLayerSelector && (
+        <Typography sx={{ fontSize: '14px', color: COLORS.brandExtraDark, pb: 4 }}>
+          {t('layerPicker.hostedOn', {
+            network: networkNames[selectedScope.network],
+          })}
+        </Typography>
+      )}
       {details?.description && (
         <Typography sx={{ fontSize: '14px', color: COLORS.brandExtraDark, pb: 4 }}>
           {details.description}
diff --git a/src/app/components/LayerPicker/LayerMenu.tsx b/src/app/components/LayerPicker/LayerMenu.tsx
index 8c4b9b21e..851a1e63a 100644
--- a/src/app/components/LayerPicker/LayerMenu.tsx
+++ b/src/app/components/LayerPicker/LayerMenu.tsx
@@ -9,7 +9,7 @@ import Tooltip from '@mui/material/Tooltip'
 import { COLORS } from '../../../styles/theme/colors'
 import { Layer } from '../../../oasis-nexus/api'
 import { getLayerLabels } from '../../utils/content'
-import { isScopeHidden, RouteUtils } from '../../utils/route-utils'
+import { isScopeHidden, mergeNetworksInLayerSelector, RouteUtils } from '../../utils/route-utils'
 import { Network } from '../../../types/network'
 import { orderByLayer } from '../../../types/layers'
 import { useScreenSize } from '../../hooks/useScreensize'
@@ -123,7 +123,9 @@ const getOptionsForNetwork = (network: Network, activeScope?: SearchScope | unde
 export const LayerMenu: FC<LayerMenuProps> = ({ selectedNetwork, selectedScope, setSelectedScope }) => {
   const activeScope = useScopeParam()!
   const [hoveredScope, setHoveredScope] = useState<undefined | SearchScope>()
-  const options = getOptionsForNetwork(selectedNetwork ?? activeScope.network)
+  const options = mergeNetworksInLayerSelector
+    ? RouteUtils.getVisibleScopes(activeScope).map(scope => ({ scope, enabled: true }))
+    : getOptionsForNetwork(selectedNetwork ?? activeScope.network)
 
   return (
     <MenuList>
diff --git a/src/app/components/LayerPicker/index.tsx b/src/app/components/LayerPicker/index.tsx
index f91fa661b..2c48b9ddf 100644
--- a/src/app/components/LayerPicker/index.tsx
+++ b/src/app/components/LayerPicker/index.tsx
@@ -13,7 +13,7 @@ import { useRequiredScopeParam } from '../../hooks/useScopeParam'
 import { NetworkMenu } from './NetworkMenu'
 import { LayerMenu } from './LayerMenu'
 import { LayerDetails } from './LayerDetails'
-import { scopeFreedom, RouteUtils } from '../../utils/route-utils'
+import { scopeFreedom, RouteUtils, mergeNetworksInLayerSelector } from '../../utils/route-utils'
 import { styled } from '@mui/material/styles'
 import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft'
 import { useScreenSize } from '../../hooks/useScreensize'
@@ -109,7 +109,9 @@ const LayerPickerContent: FC<LayerPickerContentProps> = ({ isOutOfDate, onClose,
           <div>
             {
               // Do we need a "back to networks" button ?
-              ((scopeFreedom === 'network-layer' && tabletStep === LayerPickerTabletStep.Layer) || // Stepping back from layers
+              ((scopeFreedom === 'network-layer' &&
+                !mergeNetworksInLayerSelector &&
+                tabletStep === LayerPickerTabletStep.Layer) || // Stepping back from layers
                 (scopeFreedom === 'network' && tabletStep === LayerPickerTabletStep.LayerDetails)) && ( // Stepping back from details, skipping layers
                 <TabletBackButton
                   variant="text"
@@ -146,7 +148,7 @@ const LayerPickerContent: FC<LayerPickerContentProps> = ({ isOutOfDate, onClose,
       <Divider />
       <StyledContent>
         <Grid container>
-          {scopeFreedom !== 'layer' &&
+          {!(scopeFreedom === 'layer' || mergeNetworksInLayerSelector) &&
             (!isTablet || (isTablet && tabletStep === LayerPickerTabletStep.Network)) && (
               <Grid xs={12} md={3}>
                 <NetworkMenu
diff --git a/src/app/utils/route-utils.ts b/src/app/utils/route-utils.ts
index 1d9768db9..7cc2e0796 100644
--- a/src/app/utils/route-utils.ts
+++ b/src/app/utils/route-utils.ts
@@ -72,6 +72,8 @@ export const hiddenScopes: SearchScope[] = [
   // { network: Network.mainnet, layer: Layer.sapphire }, // This is only for testing
 ]
 
+export const mergeNetworksInLayerSelector = false
+
 export const isScopeHidden = (scope: SearchScope): boolean =>
   !!hiddenScopes.find(s => s.network === scope.network && s.layer === scope.layer)
 
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index 61f56edf4..9711cd81f 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -544,6 +544,7 @@
     "consensus": "The consensus layer is a scalable, high-throughput, secure, proof-of-stake consensus run by a decentralized set of validator nodes.",
     "goToDashboard": "Go to dashboard",
     "hex": "Hex: {{ id }}",
+    "hostedOn": "Hosted on {{ network }}",
     "readMore": "Read more about {{ layer }} on {{ network }} in Oasis Docs",
     "rpcHttp": "RPC HTTP endpoint: {{ endpoint }}",
     "rpcWebSockets": "RPC WebSockets endpoint: {{ endpoint }}",