Skip to content

Commit

Permalink
Feat: Global Parameter Handling (#104)
Browse files Browse the repository at this point in the history
* remove inspector-specific network settings

* set up global network parameter

* adjust nav bar to include params in links

* fix navbar css module name

* resolve array type issue

* add local storage on new tabs

* add network tag to all network-related tools

* cleanup + fix address updates

* add parameter handling to data fetcher

* remove logging

* refactor: remove network badges and rename variable

---------

Co-authored-by: CJ42 <[email protected]>
  • Loading branch information
fhildeb and CJ42 authored Jul 9, 2024
1 parent d6ac5d0 commit 8c51f82
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 66 deletions.
4 changes: 3 additions & 1 deletion components/KeyManagerNonceChecker/KeyManagerNonceChecker.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/* eslint-disable react/no-unescaped-entities */

import { useState } from 'react';
import { useContext, useState } from 'react';

import LSP6KeyManager from '@lukso/lsp-smart-contracts/artifacts/LSP6KeyManager.json';
import useWeb3 from '../../hooks/useWeb3';
import { NetworkContext } from '../../contexts/NetworksContext';

const KeyManagerNonceChecker: React.FC = () => {
const web3 = useWeb3();
Expand All @@ -12,6 +13,7 @@ const KeyManagerNonceChecker: React.FC = () => {
const [callerAddress, setCallerAddress] = useState('');
const [channelId, setChannelId] = useState('');
const [nonce, setNonce] = useState('');
const { network } = useContext(NetworkContext);

const [showNonce, setShowNonce] = useState(false);
const [error, showError] = useState(false);
Expand Down
File renamed without changes.
36 changes: 27 additions & 9 deletions components/NavBar/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import React, { useState } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/router';
import NetworkSwitch from './components/NetworksSwitch';
import styles from './NvaBar.module.scss';
import styles from './NavBar.module.scss';

const NavBar: React.FC = () => {
const [isActive, setIsActive] = useState(false);
Expand All @@ -16,10 +16,28 @@ const NavBar: React.FC = () => {
setIsActive(!isActive);
};

const createLink = (path) => {
if (typeof window === 'undefined') {
return path;
}
const urlParams = new URLSearchParams(window.location.search);

const keys = Array.from(urlParams.keys());

// Remove all page-specific parameters
for (const key of keys) {
if (key !== 'network') {
urlParams.delete(key);
}
}

return `${path}?${urlParams.toString()}`;
};

return (
<nav className="navbar is-light sticky">
<div className={`navbar-brand ${styles.navbarHeight}`}>
<Link href="/">
<Link href={createLink('/')}>
<a className="navbar-item is-hidden-desktop">🛠 ERC725 Tools</a>
</Link>
<a
Expand All @@ -37,12 +55,12 @@ const NavBar: React.FC = () => {

<div className={`navbar-menu ${isActive ? 'is-active' : ''}`}>
<div className="navbar-start ml-3">
<Link href="/">
<Link href={createLink('/')}>
<a className={`navbar-item is-hidden-touch ${styles.navbarHeight}`}>
🛠 ERC725 Tools
</a>
</Link>
<Link href="/inspector">
<Link href={createLink('/inspector')}>
<a
className={`navbar-item ${
router.pathname === '/inspector' && 'has-text-link'
Expand All @@ -51,7 +69,7 @@ const NavBar: React.FC = () => {
🔎 Inspector
</a>
</Link>
<Link href="/lsp-checker">
<Link href={createLink('/lsp-checker')}>
<a
className={`navbar-item ${
router.pathname === '/lsp-checker' && 'has-text-link'
Expand All @@ -60,7 +78,7 @@ const NavBar: React.FC = () => {
✅ LSP Checker
</a>
</Link>
<Link href="/data-fetcher">
<Link href={createLink('/data-fetcher')}>
<a
className={`navbar-item ${
router.pathname === '/data-fetcher' && 'has-text-link'
Expand All @@ -69,7 +87,7 @@ const NavBar: React.FC = () => {
💽 Data Fetcher
</a>
</Link>
<Link href="/key-manager">
<Link href={createLink('/key-manager')}>
<a
className={`navbar-item ${
router.pathname === '/key-manager' && 'has-text-link'
Expand All @@ -78,7 +96,7 @@ const NavBar: React.FC = () => {
🔐 Key Manager
</a>
</Link>
<Link href="/abi-encoder">
<Link href={createLink('/abi-encoder')}>
<a
className={`navbar-item ${
router.pathname === '/abi-encoder' && 'has-text-link'
Expand All @@ -87,7 +105,7 @@ const NavBar: React.FC = () => {
📜 ABI Encoder
</a>
</Link>
<Link href="/lsp2-encoder">
<Link href={createLink('/lsp2-encoder')}>
<a
className={`navbar-item ${
router.pathname === '/lsp2-encoder' && 'has-text-link'
Expand Down
11 changes: 5 additions & 6 deletions components/NavBar/components/NetworksSwitch/NetworksSwitch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,11 @@ const NetworkSwitch: React.FC = () => {
setNetwork(chain);
setIsDropdownActive(false);

if (router.pathname === '/inspector' && router.query.address) {
const updatedUrl = `/inspector?address=${
router.query.address
}&network=${chain.name.toLowerCase()}`;
router.push(updatedUrl, undefined, { shallow: true });
}
// Build the new URL with existing parameters
const urlParams = new URLSearchParams(window.location.search);
urlParams.set('network', chain.name.toLowerCase());
const updatedUrl = `${router.pathname}?${urlParams.toString()}`;
router.push(updatedUrl, undefined, { shallow: true });
};

const handleDropdownBlur = (
Expand Down
101 changes: 93 additions & 8 deletions contexts/NetworksContext.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
import { createContext, useState } from 'react';
import { createContext, useState, useCallback, useEffect } from 'react';
import { RPC_URL } from '../globals';
import { NetworkName } from '../types/network';
import { useRouter } from 'next/router';
export interface INetwork {
name: NetworkName;
rpc: string;
imgUrl?: string;
}

const chains: INetwork[] = [
{
name: NetworkName.MAINNET,
rpc: RPC_URL[NetworkName.MAINNET],
imgUrl: '/lukso.png',
},
{
name: NetworkName.TESTNET,
rpc: RPC_URL[NetworkName.TESTNET],
imgUrl: '/lukso.png',
},
{
name: NetworkName.LOCALHOST,
rpc: RPC_URL[NetworkName.LOCALHOST],
imgUrl: '/lukso.png',
},
];

export interface INetworksContext {
network: INetwork;
setNetwork: (network: INetwork) => void;
Expand All @@ -17,13 +36,79 @@ export const NetworkContext = createContext<INetworksContext>({
setNetwork: () => null,
});

const NetworksProvider = ({ children }: { children: React.ReactNode }) => {
const [network, setNetwork] = useState<INetwork>({
// Default Network
name: NetworkName.MAINNET,
rpc: RPC_URL[NetworkName.MAINNET],
imgUrl: '/lukso.png',
});
const NetworksProvider = ({ children }) => {
const router = useRouter();

const getNetworkFromLocalStorage = (): INetwork => {
if (typeof window !== 'undefined') {
const storedNetworkName = localStorage.getItem('erc725InspectNetwork');
if (storedNetworkName) {
return (
chains.find(
(network) =>
network.name.toLowerCase() === storedNetworkName.toLowerCase(),
) || chains[0]
);
}
}
// Return default if nothing is in local storage
return chains[0];
};

// Get network from URL or switch to default chain
const getNetworkFromUrlOrDefault = useCallback(() => {
const networkParam = router.query.network;
if (typeof networkParam !== 'string') {
// Fallback to local storage or default network
return getNetworkFromLocalStorage();
}
return (
chains.find(
(network) => network.name.toLowerCase() === networkParam?.toLowerCase(),
) || getNetworkFromLocalStorage()
);
}, [router.query.network]);

// Initialize state based on network
const [network, setNetwork] = useState(getNetworkFromUrlOrDefault);

const updateUrlWithNetwork = useCallback(
(networkName) => {
if (typeof window !== 'undefined') {
const queryParams = new URLSearchParams(window.location.search);
queryParams.set('network', networkName);
const updatedUrl = `${router.pathname}?${queryParams.toString()}`;
router.replace(updatedUrl, undefined, { shallow: true });
}
},
[router],
);

useEffect(() => {
const networkFromUrl = getNetworkFromUrlOrDefault();
if (networkFromUrl.name !== network.name) {
setNetwork(networkFromUrl);
}

const networkParam = router.query.network;

if (networkParam === undefined) {
// Update the URL with the network parameter if missing
updateUrlWithNetwork(network.name.toLowerCase());
}
}, [
router.query.network,
network.name,
getNetworkFromUrlOrDefault,
updateUrlWithNetwork,
]);

useEffect(() => {
// Save to local storage whenever the network changes
if (typeof window !== 'undefined') {
localStorage.setItem('erc725InspectNetwork', network.name);
}
}, [network]);

return (
<NetworkContext.Provider value={{ network, setNetwork }}>
Expand Down
35 changes: 34 additions & 1 deletion pages/data-fetcher.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { NextPage } from 'next';
import Head from 'next/head';
import { useEffect, useState } from 'react';
import { useContext, useState, useEffect } from 'react';
import { isAddress } from 'web3-utils';
import ERC725, {
ERC725JSONSchema,
Expand All @@ -25,6 +25,8 @@ import useWeb3 from '../hooks/useWeb3';

import SampleAddressInput from '../components/SampleAddressInput/SampleAddressInput';
import { SAMPLE_ADDRESS } from '../constants';
import { NetworkContext } from '../contexts/NetworksContext';
import { useRouter } from 'next/router';
import { isValidTuple } from '@erc725/erc725.js/build/main/src/lib/decodeData';

const dataKeyList = [
Expand Down Expand Up @@ -55,6 +57,8 @@ const GetData: NextPage = () => {
});

const [erc725js, setERC725JsInstance] = useState<ERC725>();
const { network } = useContext(NetworkContext);
const router = useRouter();

const web3 = useWeb3();

Expand All @@ -71,9 +75,31 @@ const GetData: NextPage = () => {
...LSP17DataKeys,
];

useEffect(() => {
const queryAddress = router.query.address;
const queryDataKey = router.query.dataKey;

if (queryAddress && typeof queryAddress === 'string') {
setAddress(queryAddress);
}
if (queryDataKey && typeof queryDataKey === 'string') {
setDataKey(queryDataKey);
}
}, [router.query]);

const updateURLParams = (address: string, dataKey: string) => {
const currentUrl = new URL(window.location.href);
currentUrl.searchParams.set('address', address);
currentUrl.searchParams.set('datakey', dataKey);
router.replace(currentUrl.href, undefined, { shallow: true });
};

const onContractAddressChange = async (address: string) => {
setAddress(address);
setData('');

updateURLParams(address, dataKey);

if (!isAddress(address) && address.length !== 0) {
setAddressError('The address is not valid');
setInterfaces({
Expand Down Expand Up @@ -101,6 +127,8 @@ const GetData: NextPage = () => {
setDataKey(dataKey);
setData('');

updateURLParams(address, dataKey);

if (
(dataKey.length !== 64 && dataKey.length !== 66) ||
(dataKey.length === 66 && dataKey.slice(0, 2) !== '0x')
Expand Down Expand Up @@ -186,6 +214,10 @@ const GetData: NextPage = () => {
</Head>
<div className="container">
<h2 className="title is-2">Data Fetcher</h2>
<div className="tags has-addons">
<span className="tag is-dark">Network</span>
<span className="tag is-warning">{network.name}</span>
</div>
<article className="message is-info">
<div className="message-body">
<p>
Expand Down Expand Up @@ -270,6 +302,7 @@ const GetData: NextPage = () => {

<div className="select mb-4 is-fullwidth">
<select
value={dataKey}
onChange={(e) => onDataKeyChange(e.target.value)}
className="is-fullwidth"
>
Expand Down
Loading

0 comments on commit 8c51f82

Please sign in to comment.