Skip to content

Commit

Permalink
fix: search in custom mode (#806)
Browse files Browse the repository at this point in the history
Custom mode searching did not work after the react-router 6 updates.
  • Loading branch information
ckniffen authored Aug 21, 2023
1 parent aacdfa8 commit c3849a8
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 71 deletions.
29 changes: 29 additions & 0 deletions src/containers/App/navigation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { buildPath } from '../shared/routing'
import { NavigationMenuAnyRoute } from '../Header/NavigationMenu'
import { LEDGERS_ROUTE, NETWORK_ROUTE, VALIDATOR_ROUTE } from './routes'

const isNetwork = (path) =>
path.indexOf(buildPath(NETWORK_ROUTE, {})) === 0 ||
path.indexOf(buildPath(VALIDATOR_ROUTE, { identifier: '' })) === 0

// NOTE: for submenus, remove `path` field and add `children` array of objects
export const navigationConfig: NavigationMenuAnyRoute[] = [
{
route: LEDGERS_ROUTE,
title: 'explorer',
current: (path: string) => !isNetwork(path),
},
{
route: NETWORK_ROUTE,
title: 'network',
current: (path: string) => isNetwork(path),
},
{
link: 'https://xrpl.org',
title: 'xrpl_org',
},
{
link: 'https://github.com/ripple/explorer',
title: 'github',
},
]
30 changes: 1 addition & 29 deletions src/containers/App/routes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { buildPath, RouteDefinition } from '../shared/routing'

import { NavigationMenuAnyRoute } from '../Header/NavigationMenu'
import { RouteDefinition } from '../shared/routing'

export const ACCOUNT_ROUTE: RouteDefinition<{
id?: string
Expand Down Expand Up @@ -58,29 +56,3 @@ export const VALIDATOR_ROUTE: RouteDefinition<{
}> = {
path: `/validators/:identifier/:tab?`,
}

const isNetwork = (path) =>
path.indexOf(buildPath(NETWORK_ROUTE, {})) === 0 ||
path.indexOf(buildPath(VALIDATOR_ROUTE, { identifier: '' })) === 0

// NOTE: for submenus, remove `path` field and add `children` array of objects
export const navigationConfig: NavigationMenuAnyRoute[] = [
{
route: LEDGERS_ROUTE,
title: 'explorer',
current: (path: string) => !isNetwork(path),
},
{
route: NETWORK_ROUTE,
title: 'network',
current: (path: string) => isNetwork(path),
},
{
link: 'https://xrpl.org',
title: 'xrpl_org',
},
{
link: 'https://github.com/ripple/explorer',
title: 'github',
},
]
120 changes: 79 additions & 41 deletions src/containers/Header/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ import {
import './search.scss'
import { isValidPayString } from '../../rippled/payString'
import { getTransaction } from '../../rippled/lib/rippled'
import { buildPath } from '../shared/routing'
import {
ACCOUNT_ROUTE,
LEDGER_ROUTE,
NFT_ROUTE,
PAYSTRING_ROUTE,
TOKEN_ROUTE,
TRANSACTION_ROUTE,
VALIDATOR_ROUTE,
} from '../App/routes'

const determineHashType = async (id: string, rippledContext: XrplClient) => {
try {
Expand All @@ -32,66 +42,98 @@ const determineHashType = async (id: string, rippledContext: XrplClient) => {
// separator for currency formats
const separators = /[.:+-]/

const getIdType = async (id: string, rippledContext: XrplClient) => {
const getRoute = async (
id: string,
rippledContext: XrplClient,
): Promise<{ type: string; path: string } | null> => {
if (DECIMAL_REGEX.test(id)) {
return 'ledgers'
return {
type: 'ledgers',
path: buildPath(LEDGER_ROUTE, { identifier: id }),
}
}
if (isValidClassicAddress(id)) {
return 'accounts'
return {
type: 'accounts',
path: buildPath(ACCOUNT_ROUTE, { id: normalizeAccount(id) }),
}
}
if (HASH_REGEX.test(id)) {
// Transactions and NFTs share the same syntax
// We must make an api call to ensure if it's one or the other
return determineHashType(id, rippledContext)
const type = await determineHashType(id, rippledContext)
let path
if (type === 'transactions') {
path = buildPath(TRANSACTION_ROUTE, { identifier: id.toUpperCase() })
} else if (type === 'nft') {
path = buildPath(NFT_ROUTE, { id: id.toUpperCase() })
}

return {
path,
type,
}
}
if (isValidXAddress(id) || isValidClassicAddress(id.split(':')[0])) {
return 'accounts' // TODO: Consider a new path/page specific to X-addresses
return {
type: 'accounts',
path: buildPath(ACCOUNT_ROUTE, { id: normalizeAccount(id) }), // TODO: Consider a new path/page specific to X-addresses
}
}
if (isValidPayString(id) || isValidPayString(id.replace('@', '$'))) {
return 'paystrings'
let normalizedId = id
if (!isValidPayString(id)) {
normalizedId = id.replace('@', '$')
}

return {
type: 'paystrings',
path: buildPath(PAYSTRING_ROUTE, { id: normalizedId }),
}
}
if (
(CURRENCY_REGEX.test(id) || FULL_CURRENCY_REGEX.test(id)) &&
isValidClassicAddress(id.split(separators)[1])
) {
return 'token'
const components = id.split(separators)
return {
type: 'token',
path: buildPath(TOKEN_ROUTE, {
token: `${components[0].toLowerCase()}.${components[1]}`,
}),
}
}
if (VALIDATORS_REGEX.test(id)) {
return 'validators'
return {
type: 'validators',
path: buildPath(VALIDATOR_ROUTE, { identifier: normalizeAccount(id) }),
}
}

return 'invalid'
return null
}

// normalize classicAddress:tag to X-address
// TODO: Take network into account (!)
const normalize = (id: string, type: string) => {
if (type === 'transactions') {
return id.toUpperCase()
const normalizeAccount = (id: string) => {
if (!id.includes(':')) {
return id
}
if (type === 'accounts' && id.includes(':')) {
// TODO: Test invalid classic address; "invalid" tag (?)
const components = id.split(':')
try {
const xAddress = classicAddressToXAddress(
components[0],
components[1] === undefined || components[1] === 'false'
? false
: Number(components[1]),
false,
) // TODO: Take network into account (!)
return xAddress
} catch (_) {
/* version_invalid: version bytes do not match any of the provided version(s) */
}
} else if (type === 'paystrings') {
if (!isValidPayString(id)) {
return id.replace('@', '$')
}
} else if (type === 'token') {
const components = id.split(separators)
return `${components[0].toLowerCase()}.${components[1]}`
// TODO: Test invalid classic address; "invalid" tag (?)
const components = id.split(':')
try {
const xAddress = classicAddressToXAddress(
components[0],
components[1] === undefined || components[1] === 'false'
? false
: Number(components[1]),
false,
) // TODO: Take network into account (!)
return xAddress
} catch (_) {
/* version_invalid: version bytes do not match any of the provided version(s) */
}

return id
}

Expand All @@ -107,17 +149,13 @@ export const Search = ({ callback = () => {} }: SearchProps) => {

const handleSearch = async (id: string) => {
const strippedId = id.replace(/^["']|["']$/g, '')
const type = await getIdType(strippedId, socket)
const route = await getRoute(strippedId, socket)
track('search', {
search_term: strippedId,
search_category: type,
search_category: route?.type,
})

navigate(
type === 'invalid'
? `/search/${strippedId}`
: `/${type}/${normalize(strippedId, type)}`,
)
navigate(route === null ? `/search/${strippedId}` : route.path)
callback()
}

Expand Down
2 changes: 1 addition & 1 deletion src/containers/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FC } from 'react'
import classnames from 'classnames'

import { Banner } from './Banner'
import { navigationConfig } from '../App/routes'
import { navigationConfig } from '../App/navigation'
import { NavigationMenu } from './NavigationMenu'

import './header.scss'
Expand Down
2 changes: 2 additions & 0 deletions src/containers/Header/test/Search.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,6 @@ describe('Search component', () => {
expect(window.location.pathname).toEqual(`/transactions/${hash}`)
wrapper.unmount()
})

// TODO: Add custom search tests
})

0 comments on commit c3849a8

Please sign in to comment.