Skip to content

Commit

Permalink
Continue the work on cross-site / cross-paratime search
Browse files Browse the repository at this point in the history
  • Loading branch information
csillag committed May 26, 2023
1 parent da9b878 commit 4796e7f
Show file tree
Hide file tree
Showing 17 changed files with 429 additions and 323 deletions.
14 changes: 3 additions & 11 deletions src/app/components/PageLayout/NetworkButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { FC, ReactNode } from 'react'
import { useTranslation } from 'react-i18next'
import { TFunction } from 'i18next'
import Box from '@mui/material/Box'
import Button, { buttonClasses } from '@mui/material/Button'
import CheckIcon from '@mui/icons-material/Check'
Expand All @@ -10,16 +9,9 @@ import { COLORS } from '../../../styles/theme/colors'
import { Circle } from '../Circle'
import { MainnetIcon } from '../../components/CustomIcons/Mainnet'
import { TestnetIcon } from '../../components/CustomIcons/Testnet'
import { Network } from '../../../types/network'
import { getLayerNames, Network } from '../../../types/network'
import { Layer } from '../../../oasis-indexer/api'

const getLabels = (t: TFunction): { [key in Layer]: string } => ({
[Layer.emerald]: t('common.emerald'),
[Layer.sapphire]: t('common.sapphire'),
[Layer.cipher]: t('common.cipher'),
[Layer.consensus]: t('common.consensus'),
})

const getIcons = (): Record<Network, ReactNode> => ({
[Network.mainnet]: <MainnetIcon />,
[Network.testnet]: <TestnetIcon />,
Expand Down Expand Up @@ -88,7 +80,7 @@ type NetworkButtonProps = {

export const NetworkButton: FC<NetworkButtonProps> = ({ layer, network }) => {
const { t } = useTranslation()
const labels = getLabels(t)
const labels = getLayerNames(t)
const icons = getIcons()

return (
Expand Down Expand Up @@ -128,7 +120,7 @@ export const StyledMobileNetworkButton = styled(Button)(({ theme }) => ({

export const MobileNetworkButton: FC<NetworkButtonProps> = ({ layer }) => {
const { t } = useTranslation()
const labels = getLabels(t)
const labels = getLayerNames(t)

return (
<StyledMobileNetworkButton>
Expand Down
2 changes: 2 additions & 0 deletions src/app/components/StyledDescriptionList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ export const StyledDescriptionList = styled(InlineDescriptionList, {
'dt, dd': {
display: 'flex',
alignItems: 'center',
// TODO do we want to use this here? Discuss with Don
// boxShadow: `0px 1px 0px ${theme.palette.layout.descriptionListSeparator}`,
boxShadow: `0px 1px 0px ${COLORS.grayLight}`,
':last-of-type': {
boxShadow: 'none',
Expand Down
5 changes: 2 additions & 3 deletions src/app/pages/SearchResultsPage/NoResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ import Link from '@mui/material/Link'
import { SearchSuggestionsLinks } from '../../components/Search/SearchSuggestionsLinks'
import { OptionalBreak } from '../../components/OptionalBreak'
import { useTheme } from '@mui/material/styles'
import { getNetworkNames } from '../../../types/network'
import { SearchScope } from '../../../types/searchScope'
import { getNameForScope, SearchScope } from '../../../types/searchScope'

export const NoResults: FC<{ scope?: SearchScope }> = ({ scope }) => {
const { t } = useTranslation()
const theme = useTheme()
const title = !scope
? t('search.noResults.header')
: t('search.noResults.scopeHeader', { scope: getNetworkNames(t)[scope.network] })
: t('search.noResults.scopeHeader', { scope: getNameForScope(t, scope) })

return (
<EmptyState
Expand Down
148 changes: 148 additions & 0 deletions src/app/pages/SearchResultsPage/ResultsElsewhere.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { FC, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { styled } from '@mui/material/styles'
import { getThemesForNetworks } from '../../../styles/theme'
import Box from '@mui/material/Box'
import { SearchQueries } from './hooks'
import ZoomIn from '@mui/icons-material/ZoomIn'
import ZoomOut from '@mui/icons-material/ZoomOut'
import { getKeyForScope, getScopeForKey, SearchScope } from '../../../types/searchScope'
import { getNetworkNames, Network } from '../../../types/network'
import { RouteUtils } from '../../utils/route-utils'
import { ResultsFilteredThemed } from './ResultsFilteredThemed'

const NotificationBox = styled(Box)(({ theme }) => ({
// TODO: this is probably not fully correct.
marginTop: 30,
marginBottom: 30,
fontSize: 14,
marginLeft: '10%',
marginRight: '10%',

boxSizing: 'border-box',
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
padding: '5px 10px',
gap: 10,

height: 50,

background: theme.palette.layout.notificationBackground,
border: `2px solid ${theme.palette.layout.darkBorder}`,
color: theme.palette.layout.notificationText,

borderRadius: 50,
}))

export const ResultsElsewhere: FC<{
wantedScope: SearchScope
resultsInScopes: Record<string, number>
searchQueries: SearchQueries
roseFiatValue: number | undefined
}> = ({ wantedScope, resultsInScopes, searchQueries, roseFiatValue }) => {
const { t } = useTranslation()
const wantedScopeKey = getKeyForScope(wantedScope)
const alsoHasWantedResults = !!resultsInScopes[wantedScopeKey]
const otherScopesKeys = Object.keys(resultsInScopes)
.filter(key => !!resultsInScopes[key])
.filter(scopeKey => scopeKey !== wantedScopeKey)
const otherScopes = otherScopesKeys.map(getScopeForKey)
const hasResultsOnDifferentParatimes = otherScopes.some(
scope => scope.network === wantedScope.network && scope.layer !== wantedScope.layer,
)
const hasResultsOnDifferentNetworks = otherScopes.some(scope => scope.network !== wantedScope.network)
const hasAllKindsOfResults = hasResultsOnDifferentParatimes && hasResultsOnDifferentNetworks
const location = t(
hasAllKindsOfResults
? 'search.otherResults.otherNetworksAndParatimes'
: hasResultsOnDifferentNetworks
? 'search.otherResults.otherNetworks'
: 'search.otherResults.otherParatimes',
)

const totalNumberOfResults = otherScopesKeys.reduce(
(partialSum, scopeKey) => partialSum + resultsInScopes[scopeKey],
0,
)

// openByDefault={scope.network === Network.mainnet && !hasNoResultsInWantedScope}
const openByDefault = false

const [open, setOpen] = useState(openByDefault)

const hasMainnetResults = otherScopes.some(scope => scope.network === Network.mainnet)

const themes = getThemesForNetworks()
const networkNames = getNetworkNames(t)

const notificationTheme = hasMainnetResults ? themes[Network.mainnet] : themes[otherScopes[0].network]

if (!otherScopes.length) return null

const resultsInNetworks: Record<Network, number> = {} as any
const allNetworks = RouteUtils.getEnabledNetworks()
allNetworks.forEach(net => (resultsInNetworks[net] = 0))
otherScopes.forEach(scope => (resultsInNetworks[scope.network] += resultsInScopes[scope.key]))

if (!open) {
return (
<NotificationBox theme={notificationTheme} onClick={() => setOpen(true)}>
<ZoomIn />
<span>
<Trans
t={t}
i18nKey={
alsoHasWantedResults
? 'search.otherResults.clickToShowMore'
: 'search.otherResults.clickToShowThem'
}
values={{
countLabel: t(alsoHasWantedResults ? 'search.results.moreCount' : 'search.results.count', {
count: totalNumberOfResults,
}),
location,
}}
/>
</span>
</NotificationBox>
)
}

return (
<>
<NotificationBox theme={notificationTheme} onClick={() => setOpen(false)}>
<ZoomOut />
<span>
<Trans i18nKey={'search.otherResults.clickToHide'} values={{ location }} />
</span>
</NotificationBox>
{hasResultsOnDifferentParatimes && (
<ResultsFilteredThemed
title={t('search.otherResults.otherParatimesOnNetwork', {
network: networkNames[wantedScope.network],
})}
filter={item => item.network === wantedScope.network && item.layer !== wantedScope.layer}
networkForTheme={wantedScope.network}
searchQueries={searchQueries}
numberOfResults={resultsInNetworks[wantedScope.network]}
roseFiatValue={roseFiatValue}
/>
)}
{allNetworks
.filter(net => net !== wantedScope.network && !!resultsInNetworks[net])
.map(net => (
<ResultsFilteredThemed
key={net}
networkForTheme={net}
title={networkNames[net]}
filter={item => item.network === net}
searchQueries={searchQueries}
numberOfResults={resultsInNetworks[net]}
roseFiatValue={roseFiatValue}
/>
))}
</>
)
}
140 changes: 140 additions & 0 deletions src/app/pages/SearchResultsPage/ResultsFilteredThemed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { ResultsGroupByType } from './ResultsGroupByType'
import { BlockDetailView } from '../BlockDetailPage'
import { RouteUtils } from '../../utils/route-utils'
import { TransactionDetailView } from '../TransactionDetailPage'
import { AccountDetailsView } from '../AccountDetailsPage'
import { SearchQueries } from './hooks'
import { HasScope } from '../../../oasis-indexer/api'
import { getThemesForNetworks } from '../../../styles/theme'
import { Network } from '../../../types/network'
import { SubPageCard } from '../../components/SubPageCard'
import useMediaQuery from '@mui/material/useMediaQuery'
import { styled } from '@mui/material/styles'
import Box from '@mui/material/Box'

const ResultListFrame = styled(Box)(({ theme }) => {
const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
return {
marginBottom: 20,
border: isMobile ? 'none' : `solid 15px ${theme.palette.layout.networkBubbleBorder}`,
background: theme.palette.layout.networkBubbleBackground,
borderRadius: 12,
boxShadow: '-20px 4px 40px rgba(34, 47, 63, 0.24)',
'&& > div > div': {
// border: '2px solid green',
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
paddingLeft: 64,
paddingRight: 64,
background: theme.palette.layout.networkBubbleBackground,
},
'&& > div > div > div.MuiBox-root': {
background: theme.palette.layout.networkBubbleBorder,
borderRadius: 0,
// border: '2px solid red',
marginLeft: -64,
marginTop: -32,
marginRight: -64,
paddingLeft: 64,
paddingRight: 64,
paddingBottom: 32,
paddingTop: 32,
},
'&& > div > div > div.MuiCardContent-root': {},
'&& dt, && dd': {
boxShadow: `0px 1px 0px ${theme.palette.layout.descriptionListSeparator}`,
},
}
})

export type ResultFilter = (item: HasScope) => boolean

/**
* Component for selectively displaying a subset of search results that matches a filter
*
* It doesn't actually run a search query, but uses existing results.
*/
const ResultsFiltered: FC<{
searchQueries: SearchQueries
filter: ResultFilter
roseFiatValue: number | undefined
}> = ({ searchQueries, filter, roseFiatValue }) => {
const { t } = useTranslation()
return (
<>
<ResultsGroupByType
title={t('search.results.blocks.title')}
results={searchQueries.blockHeight.results.filter(filter)}
resultComponent={item => <BlockDetailView isLoading={false} block={item} showLayer={true} />}
link={block => RouteUtils.getBlockRoute(block, block.round)}
linkLabel={t('search.results.blocks.viewLink')}
/>

<ResultsGroupByType
title={t('search.results.transactions.title')}
results={searchQueries.txHash.results.filter(filter)}
resultComponent={item => (
<TransactionDetailView isLoading={false} transaction={item} showLayer={true} />
)}
link={tx => RouteUtils.getTransactionRoute(tx, tx.eth_hash || tx.hash)}
linkLabel={t('search.results.transactions.viewLink')}
/>

<ResultsGroupByType
title={t('search.results.accounts.title')}
results={[
...(searchQueries.oasisAccount.results ?? []).filter(filter),
...(searchQueries.evmBech32Account.results ?? []).filter(filter),
]}
resultComponent={item => (
<AccountDetailsView
isLoading={false}
account={item}
roseFiatValue={roseFiatValue}
showLayer={true}
/>
)}
link={acc => RouteUtils.getAccountRoute(acc, acc.address_eth ?? acc.address)}
linkLabel={t('search.results.accounts.viewLink')}
/>
</>
)
}

/**
* Component for selectively displaying a subset of search results that matched a passed filter,
* with appropriate theming.
*
* It doesn't actually run a search query, but uses existing results.
*/
export const ResultsFilteredThemed: FC<{
title: string
filter: ResultFilter
networkForTheme: Network
searchQueries: SearchQueries
numberOfResults: number
roseFiatValue: number | undefined
}> = ({ filter, title, networkForTheme, searchQueries, numberOfResults, roseFiatValue }) => {
const { t } = useTranslation()

if (!numberOfResults) {
return null
}
const theme = getThemesForNetworks()[networkForTheme]

return (
<ResultListFrame theme={theme}>
<SubPageCard
title={title}
featured
subheader={t('search.results.count', {
count: numberOfResults,
})}
>
<ResultsFiltered filter={filter} searchQueries={searchQueries} roseFiatValue={roseFiatValue} />
</SubPageCard>
</ResultListFrame>
)
}

0 comments on commit 4796e7f

Please sign in to comment.