Skip to content

Commit

Permalink
Fixed network select dropdown interaction and add custom 404 pages
Browse files Browse the repository at this point in the history
  • Loading branch information
lempira committed Nov 21, 2024
1 parent bda522d commit 0ed1c2c
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 54 deletions.
14 changes: 7 additions & 7 deletions src/App.routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const routes = evalTemplates([
},
{
template: Urls.Network.Explore.Transaction.ById,
errorElement: <ErrorPage title={transactionPageTitle} />,
errorElement: <ErrorPage title={transactionPageTitle} redirectUrl={Urls.Network.Explore} />,
children: [
{
template: Urls.Network.Explore.Transaction.ById,
Expand All @@ -71,7 +71,7 @@ export const routes = evalTemplates([
children: [
{
template: Urls.Network.Explore.Block.ByRound,
errorElement: <ErrorPage title={blockPageTitle} />,
errorElement: <ErrorPage title={blockPageTitle} redirectUrl={Urls.Network.Explore} />,
element: <BlockPage />,
},
{
Expand All @@ -84,21 +84,21 @@ export const routes = evalTemplates([
{
template: Urls.Network.Explore.Account.ByAddress,
element: <AccountPage />,
errorElement: <ErrorPage title={accountPageTitle} />,
errorElement: <ErrorPage title={accountPageTitle} redirectUrl={Urls.Network.Explore} />,
},
{
template: Urls.Network.Explore.Asset.ById,
element: <AssetPage />,
errorElement: <ErrorPage title={assetPageTitle} />,
errorElement: <ErrorPage title={assetPageTitle} redirectUrl={Urls.Network.Explore} />,
},
{
template: Urls.Network.Explore.Application.ById,
errorElement: <ErrorPage title={applicationPageTitle} />,
errorElement: <ErrorPage title={applicationPageTitle} redirectUrl={Urls.Network.Explore} />,
element: <ApplicationPage />,
},
{
template: Urls.Network.TransactionWizard,
errorElement: <ErrorPage title={transactionWizardPageTitle} />,
errorElement: <ErrorPage title={transactionWizardPageTitle} redirectUrl={Urls.Network.TransactionWizard} />,
element: <TransactionWizardPage />,
},
{
Expand All @@ -116,7 +116,7 @@ export const routes = evalTemplates([
},
{
template: Urls.Network.AppLab.Edit.ById,
errorElement: <ErrorPage title={editAppInterfacePageTitle} />,
errorElement: <ErrorPage title={editAppInterfacePageTitle} redirectUrl={Urls.Network.AppLab} />,
element: <EditAppInterfacePage />,
},
],
Expand Down
6 changes: 4 additions & 2 deletions src/features/accounts/pages/account-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { invariant } from '@/utils/invariant'
import { UrlParams } from '../../../routes/urls'
import { useRequiredParam } from '../../common/hooks/use-required-param'
import { isAddress } from '@/utils/is-address'
import { is404 } from '@/utils/error'
import { is404, StatusError } from '@/utils/error'
import { RenderLoadable } from '@/features/common/components/render-loadable'
import { Account } from '../models'
import { useLoadableAccount } from '../data'
Expand All @@ -18,7 +18,9 @@ export const accountFailedToLoadMessage = 'Account failed to load'

const transformError = (e: Error) => {
if (is404(e)) {
return new Error(accountInvalidAddressMessage)
const error = new Error(accountInvalidAddressMessage) as StatusError
error.status = 404
return error
}

// eslint-disable-next-line no-console
Expand Down
12 changes: 10 additions & 2 deletions src/features/app-interfaces/pages/edit-app-interface-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { EditAppInterface } from '../components/edit/edit-app-interface'
import { useAppInterface } from '../data'
import { invariant } from '@/utils/invariant'
import { isInteger } from '@/utils/is-integer'
import { is404 } from '@/utils/error'
import { is404, StatusError } from '@/utils/error'
import { RenderLoadable } from '@/features/common/components/render-loadable'
import { PageLoader } from '@/features/common/components/page-loader'
import { useRequiredParam } from '@/features/common/hooks/use-required-param'
Expand All @@ -12,7 +12,15 @@ import { useTitle } from '@/utils/use-title'

const transformError = (e: Error) => {
if (is404(e)) {
return new Error(appInterfaceNotFoundMessage)
const error = new Error(appInterfaceNotFoundMessage) as StatusError
error.status = 404
return error
}
// This is needed because the App interface not found doesn't return a 404 status code
if (e.message.includes('App interface not found')) {
const error = new Error(appInterfaceNotFoundMessage) as StatusError
error.status = 404
return error
}

// eslint-disable-next-line no-console
Expand Down
6 changes: 4 additions & 2 deletions src/features/applications/pages/application-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ import { isInteger } from '@/utils/is-integer'
import { useLoadableApplication } from '../data'
import { RenderLoadable } from '@/features/common/components/render-loadable'
import { ApplicationDetails } from '../components/application-details'
import { is404 } from '@/utils/error'
import { is404, StatusError } from '@/utils/error'
import { useCallback } from 'react'
import { PageTitle } from '@/features/common/components/page-title'
import { PageLoader } from '@/features/common/components/page-loader'
import { useTitle } from '@/utils/use-title'

const transformError = (e: Error) => {
if (is404(e)) {
return new Error(applicationNotFoundMessage)
const error = new Error(applicationNotFoundMessage) as StatusError
error.status = 404
return error
}

// eslint-disable-next-line no-console
Expand Down
6 changes: 4 additions & 2 deletions src/features/assets/pages/asset-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { UrlParams } from '../../../routes/urls'
import { useRequiredParam } from '../../common/hooks/use-required-param'
import { isInteger } from '@/utils/is-integer'
import { RenderLoadable } from '@/features/common/components/render-loadable'
import { is404 } from '@/utils/error'
import { is404, StatusError } from '@/utils/error'
import { AssetDetails } from '../components/asset-details'
import { useLoadableAsset } from '../data'
import { useCallback } from 'react'
Expand All @@ -13,7 +13,9 @@ import { useTitle } from '@/utils/use-title'

const transformError = (e: Error) => {
if (is404(e)) {
return new Error(assetNotFoundMessage)
const error = new Error(assetNotFoundMessage) as StatusError
error.status = 404
return error
}

// eslint-disable-next-line no-console
Expand Down
6 changes: 4 additions & 2 deletions src/features/blocks/pages/block-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { UrlParams } from '../../../routes/urls'
import { useRequiredParam } from '../../common/hooks/use-required-param'
import { RenderLoadable } from '@/features/common/components/render-loadable'
import { BlockDetails } from '../components/block-details'
import { is404 } from '@/utils/error'
import { is404, StatusError } from '@/utils/error'
import { useLoadableBlock } from '../data'
import { isInteger } from '@/utils/is-integer'
import { PageTitle } from '@/features/common/components/page-title'
Expand All @@ -12,7 +12,9 @@ import { useTitle } from '@/utils/use-title'

const transformError = (e: Error) => {
if (is404(e)) {
return new Error(blockNotFoundMessage)
const error = new Error(blockNotFoundMessage) as StatusError
error.status = 404
return error
}

// eslint-disable-next-line no-console
Expand Down
19 changes: 15 additions & 4 deletions src/features/common/pages/error-page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
import { asError } from '@/utils/error'
import { useRouteError } from 'react-router-dom'
import { asError, is404 } from '@/utils/error'
import { Link, useRouteError } from 'react-router-dom'
import { PageTitle } from '../components/page-title'
import { useSelectedNetwork } from '@/features/network/data'
import { UrlTemplateObj } from '@/routes/url-template'

type ErrorPageProps = {
title?: string
redirectUrl?: UrlTemplateObj<unknown>
}

export function ErrorPage({ title }: ErrorPageProps) {
export function ErrorPage({ title, redirectUrl }: ErrorPageProps) {
const error = asError(useRouteError())

const [selectedNetwork] = useSelectedNetwork()
return (
<>
<PageTitle title={title ?? 'Error'} />
<div>
<p>Error: {error.message}</p>
</div>
{is404(error) && redirectUrl && (
<div className="mt-4">
<p>Are you sure you have the correct network selected?</p>
<Link to={redirectUrl.build({ networkId: selectedNetwork })} className="text-primary hover:underline">
Go to main feature page
</Link>
</div>
)}
</>
)
}
6 changes: 4 additions & 2 deletions src/features/groups/pages/group-page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useRequiredParam } from '@/features/common/hooks/use-required-param'
import { UrlParams } from '@/routes/urls'
import { useLoadableGroup } from '../data'
import { is404 } from '@/utils/error'
import { is404, StatusError } from '@/utils/error'
import { RenderLoadable } from '@/features/common/components/render-loadable'
import { GroupDetails } from '../components/group-details'
import { invariant } from '@/utils/invariant'
Expand All @@ -17,7 +17,9 @@ export const groupFailedToLoadMessage = 'Transaction group failed to load'

const transformError = (e: Error) => {
if (is404(e)) {
return new Error(groupNotFoundMessage)
const error = new Error(groupNotFoundMessage) as StatusError
error.status = 404
return error
}

// eslint-disable-next-line no-console
Expand Down
24 changes: 12 additions & 12 deletions src/features/network/components/network-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import { Label } from '@/features/common/components/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/features/common/components/select'
import { cn } from '@/features/common/utils'
import { useCallback } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { Urls } from '@/routes/urls'

const settingsUrl = Urls.Settings.build({})
import { useLocation, useNavigate, useParams } from 'react-router-dom'

type NetworkSelectProps = {
showLabel?: boolean
Expand All @@ -15,31 +12,34 @@ type NetworkSelectProps = {
export function NetworkSelect({ showLabel = true }: NetworkSelectProps) {
const [selectedNetwork, setSelectedNetwork] = useSelectedNetwork()
const networkConfigs = useNetworkConfigs()
const { networkId: currentNetworkId } = useParams()
const navigate = useNavigate()
const location = useLocation()

const handleNetworkChange = useCallback(
async (value: string) => {
if (location.pathname !== settingsUrl) {
navigate(settingsUrl)
(newNetworkId: string) => {
const currentPath = location.pathname
setSelectedNetwork(newNetworkId)
if (currentNetworkId) {
const newUrl = currentPath.replace(currentNetworkId, newNetworkId)
navigate(newUrl)
}
await setSelectedNetwork(value)
},
[location.pathname, navigate, setSelectedNetwork]
[currentNetworkId, location.pathname, navigate, setSelectedNetwork]
)

return (
<div className={cn('flex w-48 flex-col')}>
<div className={cn('flex flex-col')}>
{showLabel && (
<Label htmlFor="network" className={cn('ml-0.5 mb-2')}>
Active network
</Label>
)}
<Select onValueChange={handleNetworkChange} value={selectedNetwork}>
<SelectTrigger id="network">
<SelectTrigger id="network" className="w-fit min-w-32">
<SelectValue placeholder="Select network" />
</SelectTrigger>
<SelectContent className={cn('bg-card text-card-foreground')}>
<SelectContent className={cn('bg-card text-card-foreground w-fit')}>
{Object.entries(networkConfigs).map(([id, config]) => (
<SelectItem key={id} value={id}>
{config.name}
Expand Down
28 changes: 13 additions & 15 deletions src/features/network/pages/network-page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useRequiredParam } from '@/features/common/hooks/use-required-param'
import { UrlParams, Urls } from '@/routes/urls'
import { UrlParams } from '@/routes/urls'
import { useNetworkConfigs, useSelectedNetwork } from '@/features/network/data'
import { useLocation, useNavigate } from 'react-router-dom'
import { useEffect } from 'react'
Expand All @@ -11,28 +11,26 @@ type Props = {
const wildcardNetworkRoute = '_'

export function NetworkPage({ children }: Props) {
const { networkId } = useRequiredParam(UrlParams.NetworkId)
const { networkId: currentNetworkId } = useRequiredParam(UrlParams.NetworkId)
const networkConfigs = useNetworkConfigs()

if (!(networkId in networkConfigs) && networkId !== wildcardNetworkRoute) {
throw new Error(`"${networkId}" is not a valid network.`)
if (!(currentNetworkId in networkConfigs) && currentNetworkId !== wildcardNetworkRoute) {
throw new Error(`"${currentNetworkId}" is not a valid network.`)
}

const navigate = useNavigate()
const [selectedNetwork] = useSelectedNetwork()
const { pathname, search, hash } = useLocation()
const [selectedNetwork, setSelectedNetwork] = useSelectedNetwork()
const { pathname } = useLocation()

useEffect(() => {
if (networkId === wildcardNetworkRoute) {
// Handle the wildcard network route
navigate(pathname.replace(wildcardNetworkRoute, selectedNetwork) + search + hash, { replace: true })
} else if (networkId !== selectedNetwork) {
// When a user changes the network, their history will contain routes for the previous network.
// This handles navigating the user to the explore page for the selected network, so they don't get 404s when navigating through history.
navigate(Urls.Network.Explore.build({ networkId: selectedNetwork }), { replace: true })
if (currentNetworkId === selectedNetwork) {
return
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [networkId, selectedNetwork])

setSelectedNetwork(currentNetworkId)
const newUrl = pathname.replace(selectedNetwork, currentNetworkId)
navigate(newUrl, { replace: true })
}, [currentNetworkId, navigate, pathname, selectedNetwork, setSelectedNetwork])

return children
}
6 changes: 4 additions & 2 deletions src/features/transactions/pages/inner-transaction-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import { useLoadableInnerTransactionAtom } from '../data'
import { RenderLoadable } from '@/features/common/components/render-loadable'
import { isValidInnerTransactionId } from '../utils/is-valid-inner-transaction-id'
import { isTransactionId } from '@/utils/is-transaction-id'
import { is404 } from '@/utils/error'
import { is404, StatusError } from '@/utils/error'
import { PageTitle } from '@/features/common/components/page-title'
import { PageLoader } from '@/features/common/components/page-loader'
import { useSplatParam } from '@/features/common/hooks/use-splat-param'
import { useTitle } from '@/utils/use-title'

const transformError = (e: Error) => {
if (is404(e)) {
return new Error(transactionNotFoundMessage)
const error = new Error(transactionNotFoundMessage) as StatusError
error.status = 404
return error
}

// eslint-disable-next-line no-console
Expand Down
6 changes: 4 additions & 2 deletions src/features/transactions/pages/transaction-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { UrlParams } from '../../../routes/urls'
import { useRequiredParam } from '../../common/hooks/use-required-param'
import { TransactionDetails } from '../components/transaction-details'
import { RenderLoadable } from '@/features/common/components/render-loadable'
import { is404 } from '@/utils/error'
import { is404, StatusError } from '@/utils/error'
import { useLoadableTransactionAtom } from '../data'
import { isTransactionId } from '@/utils/is-transaction-id'
import { PageTitle } from '@/features/common/components/page-title'
Expand All @@ -12,7 +12,9 @@ import { useTitle } from '@/utils/use-title'

const transformError = (e: Error) => {
if (is404(e)) {
return new Error(transactionNotFoundMessage)
const error = new Error(transactionNotFoundMessage) as StatusError
error.status = 404
return error
}

// eslint-disable-next-line no-console
Expand Down
4 changes: 4 additions & 0 deletions src/utils/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ export const asError = (error: unknown) => {
export const is404 = (error: Error) => 'status' in error && error.status === 404

export const is400 = (error: Error) => 'status' in error && error.status === 400

export interface StatusError extends Error {
status?: number
}

0 comments on commit 0ed1c2c

Please sign in to comment.