Skip to content

Commit

Permalink
feat(wallet-dashboard): persist selected network (#4646)
Browse files Browse the repository at this point in the history
* feat: add util

* feat: add usePersistedNetwork hook

* fix get default value

* fix: update persisted netowrk in topnav too

* minor fixes

* fix window undefined
  • Loading branch information
evavirseda authored Jan 8, 2025
1 parent f3049e8 commit cc1b051
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 32 deletions.
12 changes: 6 additions & 6 deletions apps/core/src/hooks/useLocalStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ type SetValue<T> = Dispatch<SetStateAction<T>>;
export function useLocalStorage<T>(key: string, initialValue: T): [T, SetValue<T>] {
const getValue = useCallback(() => {
try {
if (typeof window === 'undefined') {
console.warn(
`Tried reading localStorage key "${key}" even though window is not defined`,
);
return initialValue;
}
const item = window.localStorage.getItem(key);
return item ? (JSON.parse(item) as T) : initialValue;
} catch (error) {
Expand All @@ -21,12 +27,6 @@ export function useLocalStorage<T>(key: string, initialValue: T): [T, SetValue<T

const setValue: SetValue<T> = useCallback(
(value) => {
if (typeof window === 'undefined') {
console.warn(
`Tried setting localStorage key "${key}" even though window is not defined`,
);
}

try {
const newValue = value instanceof Function ? value(storedValue) : value;
window.localStorage.setItem(key, JSON.stringify(newValue));
Expand Down
1 change: 1 addition & 0 deletions apps/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export * from './getExplorerPaths';
export * from './getExplorerLink';
export * from './truncateString';
export * from './determineCountDownText';
export * from './toTitleCase';

export * from './stake';
export * from './transaction';
Expand Down
10 changes: 10 additions & 0 deletions apps/core/src/utils/toTitleCase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export function toTitleCase(str: string): string {
if (!str) return str;
return str
.split(' ')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(' ');
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@

import { SettingsDialog, useSettingsDialog } from '@/components';
import { Badge, BadgeType, Button, ButtonType } from '@iota/apps-ui-kit';
import { ConnectButton, useIotaClientContext } from '@iota/dapp-kit';
import { getNetwork, Network } from '@iota/iota-sdk/client';
import { ThemeSwitcher } from '@iota/core';
import { ConnectButton } from '@iota/dapp-kit';
import { Network } from '@iota/iota-sdk/client';
import { toTitleCase, ThemeSwitcher } from '@iota/core';
import { Settings } from '@iota/ui-icons';
import { usePersistedNetwork } from '@/hooks';

export function TopNav() {
const { network } = useIotaClientContext();
const { name: networkName } = getNetwork(network);
const { persistedNetwork } = usePersistedNetwork();

const {
isSettingsDialogOpen,
settingsDialogView,
Expand All @@ -22,8 +23,10 @@ export function TopNav() {
return (
<div className="flex w-full flex-row items-center justify-end gap-md py-xs--rs">
<Badge
label={networkName}
type={network === Network.Mainnet ? BadgeType.PrimarySoft : BadgeType.Neutral}
label={toTitleCase(persistedNetwork)}
type={
persistedNetwork === Network.Mainnet ? BadgeType.PrimarySoft : BadgeType.Neutral
}
/>
<ConnectButton size="md" />
<SettingsDialog
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import React from 'react';
import { Header, RadioButton } from '@iota/apps-ui-kit';
import { DialogLayout, DialogLayoutBody } from '../../layout';
import { NetworkConfiguration } from '@iota/iota-sdk/client';
import { useIotaClientContext } from '@iota/dapp-kit';
import toast from 'react-hot-toast';
import { usePersistedNetwork } from '@/hooks';

interface NetworkSelectorViewProps {
handleClose: () => void;
Expand All @@ -18,17 +17,10 @@ export function NetworkSelectorView({
onBack,
}: NetworkSelectorViewProps): JSX.Element {
const clientContext = useIotaClientContext();
const activeNetwork = clientContext.network;
// Dashboard doesn't support RPCs yet
const networks = clientContext.networks as Record<string, NetworkConfiguration>;

async function handleNetworkChange(network: NetworkConfiguration) {
if (activeNetwork === network.id) {
return;
}
clientContext.selectNetwork(network.id);
toast.success(`Switched to ${network.name}`);
}
const { persistedNetwork, handleNetworkChange } = usePersistedNetwork();

return (
<DialogLayout>
Expand All @@ -39,7 +31,7 @@ export function NetworkSelectorView({
<div className="px-md" key={network.id}>
<RadioButton
label={network.name}
isChecked={activeNetwork === network.id}
isChecked={persistedNetwork === network.id}
onChange={() => handleNetworkChange(network)}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,21 @@ import {
} from '@iota/apps-ui-kit';
import { DialogLayout, DialogLayoutBody } from '../../layout';
import { SettingsDialogView } from '../enums';
import { getNetwork } from '@iota/iota-sdk/client';
import { useIotaClientContext } from '@iota/dapp-kit';
import { Globe } from '@iota/ui-icons';
import { usePersistedNetwork } from '@/hooks';
import { toTitleCase } from '@iota/core';

interface SettingsListViewProps {
handleClose: () => void;
setView: (view: SettingsDialogView) => void;
}

export function SettingsListView({ handleClose, setView }: SettingsListViewProps): JSX.Element {
const { network } = useIotaClientContext();
const { name: networkName } = getNetwork(network);

const { persistedNetwork } = usePersistedNetwork();
const MENU_ITEMS = [
{
title: 'Network',
subtitle: networkName,
subtitle: toTitleCase(persistedNetwork),
icon: <Globe />,
onClick: () => setView(SettingsDialogView.NetworkSettings),
},
Expand Down
1 change: 1 addition & 0 deletions apps/wallet-dashboard/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export * from './useGetStardustMigratableObjects';
export * from './useGetSupplyIncreaseVestingObjects';
export * from './useGroupedMigrationObjectsByExpirationDate';
export * from './useTransferTransaction';
export * from './usePersistedNetwork';
41 changes: 41 additions & 0 deletions apps/wallet-dashboard/hooks/usePersistedNetwork.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { useIotaClientContext } from '@iota/dapp-kit';
import { NetworkConfiguration } from '@iota/iota-sdk/client';
import { useLocalStorage } from '@iota/core';
import toast from 'react-hot-toast';
import { useEffect } from 'react';

export function usePersistedNetwork() {
const clientContext = useIotaClientContext();
const activeNetwork = clientContext.network;

const LOCAL_STORAGE_KEY = 'network_iota-dashboard';

const [persistedNetwork, setPersistedNetwork] = useLocalStorage<string>(
LOCAL_STORAGE_KEY,
activeNetwork,
);

async function handleNetworkChange(network: NetworkConfiguration) {
if (persistedNetwork === network.id) {
return;
}

clientContext.selectNetwork(network.id);
setPersistedNetwork(network.id);
toast.success(`Switched to ${network.name}`);
}

useEffect(() => {
if (activeNetwork !== persistedNetwork) {
setPersistedNetwork(activeNetwork);
}
}, [persistedNetwork, activeNetwork, setPersistedNetwork]);

return {
persistedNetwork,
handleNetworkChange,
};
}
6 changes: 4 additions & 2 deletions apps/wallet-dashboard/providers/AppProviders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { IotaClientProvider, lightTheme, darkTheme, WalletProvider } from '@iota
import { getAllNetworks, getDefaultNetwork } from '@iota/iota-sdk/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useState } from 'react';
import { KioskClientProvider } from '@iota/core';
import { KioskClientProvider, useLocalStorage } from '@iota/core';
import { growthbook } from '@/lib/utils';
import { ThemeProvider } from '@iota/core';

Expand All @@ -19,6 +19,8 @@ export function AppProviders({ children }: React.PropsWithChildren) {
const [queryClient] = useState(() => new QueryClient());
const allNetworks = getAllNetworks();
const defaultNetwork = getDefaultNetwork();
const [persistedNetwork] = useLocalStorage<string>('network_iota-dashboard', defaultNetwork);

function handleNetworkChange() {
queryClient.resetQueries();
queryClient.clear();
Expand All @@ -28,7 +30,7 @@ export function AppProviders({ children }: React.PropsWithChildren) {
<QueryClientProvider client={queryClient}>
<IotaClientProvider
networks={allNetworks}
defaultNetwork={defaultNetwork}
defaultNetwork={persistedNetwork}
onNetworkChange={handleNetworkChange}
>
<KioskClientProvider>
Expand Down

0 comments on commit cc1b051

Please sign in to comment.