Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: direct link #161

Merged
merged 4 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"date-fns": "^2.30.0",
"dayjs": "^1.11.7",
"ethers": "^6.3.0",
"history": "^5.3.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-router-dom": "^6.14.2",
Expand Down
6 changes: 5 additions & 1 deletion src/components/ExplorerTable/ExplorerTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ const ExplorerTable: React.FC<ExplorerTable> = ({ state, sharedConfig }: Explore
<TableRow className={classes.row} key={transfer.id}>
<TableCell className={clsx(classes.row, classes.dataRow, classes.cellRow)}>
{txHash !== undefined ? (
<Link className={classes.hashAnchorLink} to={`/transfer/${deposit?.txHash!}`} state={{ id: id, page: state.queryParams.page }}>
<Link
className={classes.hashAnchorLink}
to={`/transfer/${deposit?.txHash!}`}
state={{ id: id, page: state.queryParams.page, txHash: deposit?.txHash }}
>
{txHash}
</Link>
) : (
Expand Down
20 changes: 5 additions & 15 deletions src/context/ExplorerContext.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { useEffect } from "react"

import type { ExplorerContextState, ExplorerContext as ExplorerContextType, ExplorerState, SharedConfig, SharedConfigDomain } from "../types"
import type { ExplorerContextState, ExplorerContext as ExplorerContextType, ExplorerState } from "../types"

import { getAccount, getChainId } from "./connection"
import { routes } from "./data"
import { reducer } from "./reducer"
import { useGetTransferData } from "./useGetTransferData"
import { useGetSharedConfig } from "./useGetSharedConfig"

const ExplorerCtx = React.createContext<ExplorerContextType | undefined>(undefined)

Expand All @@ -22,6 +23,7 @@ const ExplorerProvider = ({ children }: { children: React.ReactNode | React.Reac
transferDetails: undefined,
pillColorStatus: undefined,
account: undefined,
sharedConfig: [],
}

const [explorerContextState, explorerContextDispatcher] = React.useReducer(reducer, explorerPageContextState)
Expand All @@ -30,20 +32,12 @@ const ExplorerProvider = ({ children }: { children: React.ReactNode | React.Reac
const [account, setAccount] = React.useState<string | undefined>(undefined)
const [explorerUrls, setExplorerUrls] = React.useState<[] | ExplorerState["explorerUrls"]>([])

const [sharedConfig, setSharedConfig] = React.useState<SharedConfigDomain[] | []>([])

const getSharedConfig = async (): Promise<void> => {
const reponse = await fetch(import.meta.env.VITE_SHARED_CONFIG_URL as string)
const domainsData = (await reponse.json()) as SharedConfig

setSharedConfig(domainsData.domains)
localStorage.setItem("sharedConfig", JSON.stringify(domainsData))
}

const { search } = window.location
const urlParams = new URLSearchParams(search)
const page = urlParams.get("page")

useGetSharedConfig(explorerContextDispatcher)

useGetTransferData(routes(), explorerContextDispatcher, explorerContextState, Number(page))

useEffect(() => {
Expand All @@ -57,8 +51,6 @@ const ExplorerProvider = ({ children }: { children: React.ReactNode | React.Reac
})
}

void getSharedConfig()

// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
setExplorerUrls(JSON.parse(import.meta.env.VITE_EXPLORER_URLS))

Expand All @@ -80,8 +72,6 @@ const ExplorerProvider = ({ children }: { children: React.ReactNode | React.Reac
chainId,
account,
routes: routes(),
sharedConfig,
setSharedConfig,
explorerUrls,
}}
>
Expand Down
7 changes: 7 additions & 0 deletions src/context/data/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export const fetchTransfer = async (url: string): Promise<Transfer> => {
return transfer
}

export const fetchTransferByTxHash = async (url: string): Promise<Transfer | Transfer[]> => {
const response = await fetch(url)
const data = (await response.json()) as Transfer | Transfer[]
return data
}

export const routes = (): Routes => {
const { VITE_INDEXER_URL } = import.meta.env

Expand All @@ -23,5 +29,6 @@ export const routes = (): Routes => {
transfer: async (id: string) => await fetchTransfer(`${indexerUrl}/transfers/${id}`),
transferBySender: async (sender: string, page: string, limit: string) =>
await fetchTransfers(`${indexerUrl}/sender/${sender}/transfers?limit=${limit}&page=${page}`),
transferByTransactionHash: async (txHash: string) => await fetchTransferByTxHash(`${indexerUrl}/transfers/txHash/${txHash}`),
}
}
5 changes: 5 additions & 0 deletions src/context/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export function reducer(state: ExplorerContextState, action: Actions): ExplorerC
...state,
error: action.payload,
}
case "fetch_shared_config":
return {
...state,
sharedConfig: action.payload,
}
default:
return state
}
Expand Down
18 changes: 18 additions & 0 deletions src/context/useGetSharedConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useEffect } from "react"
import { Actions, SharedConfig } from "../types"

export function useGetSharedConfig(explorerContextDispatcher: React.Dispatch<Actions>): void {
const getSharedConfig = async (): Promise<void> => {
const reponse = await fetch(import.meta.env.VITE_SHARED_CONFIG_URL as string)
const domainsData = (await reponse.json()) as SharedConfig

explorerContextDispatcher({
type: "fetch_shared_config",
payload: domainsData.domains,
})
}

useEffect(() => {
void getSharedConfig()
}, [])
}
36 changes: 25 additions & 11 deletions src/pages/DetailView/DetailView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,18 @@ import useUpdateInterval from "./hooks/useUpdateInterval"

dayjs.extend(localizedFormat)

export type LocationT = {
state: { id: string; page: number; txHash: string }
}

export default function DetailView() {
const explorerContext = useExplorer()

const { sharedConfig, setSharedConfig, explorerUrls, routes } = explorerContext
const { explorerUrls, routes, explorerContextState } = explorerContext

const { classes } = useStyles()

const { state: data } = useLocation() as { state: { id: string; page: number } }
const { state: data } = useLocation() as LocationT

const initState: DetailViewState = {
transferDetails: null,
Expand All @@ -46,20 +50,22 @@ export default function DetailView() {
clipboardMessageT2: "Copy to clipboard",
delay: 5000,
fetchingStatus: "idle",
isLoading: "none",
fallbackPage: 1,
}

const [state, dispatcher] = useReducer(reducer, initState)

useClipboard(state, dispatcher)

useFetchTransfer(routes, sharedConfig, setSharedConfig, data, dispatcher)
useFetchTransfer(routes, data?.txHash, dispatcher)

useUpdateInterval(state, dispatcher, data, routes)
useUpdateInterval(state, dispatcher, data?.txHash, routes)

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const renderTransferDetails = (transfer: Transfer | null) => {
const fromDomainInfo = getDomainData(transfer?.fromDomainId!, sharedConfig)
const toDomainInfo = getDomainData(transfer?.toDomainId!, sharedConfig)
const fromDomainInfo = getDomainData(transfer?.fromDomainId!, explorerContextState.sharedConfig)
const toDomainInfo = getDomainData(transfer?.toDomainId!, explorerContextState.sharedConfig)

const { resource, usdValue } = transfer as Transfer

Expand Down Expand Up @@ -207,18 +213,19 @@ export default function DetailView() {
<span className={classes.detailsInnerContentTitle}>Fees:</span>
<span className={classes.detailsInnerContent}>{getFormatedFee(transfer?.fee!, fromDomainInfo!)}</span>
</div>
{Array.isArray(state.transferDetails) && <br />}
</Container>
)
}

return (
<Container>
<Box className={classes.boxContainer}>
{state.transferStatus !== "none" ? (
{state.isLoading === "done" && explorerContextState.sharedConfig.length && (
<section className={classes.sectionContainer}>
<span className={classes.backIcon}>
<Link
to={`/?page=${data.page}`}
to={`/?page=${data?.page || state.fallbackPage}`}
style={{
color: "black",
textDecoration: "none",
Expand All @@ -233,10 +240,17 @@ export default function DetailView() {
<Typography variant="h4" sx={{ fontSize: "24px" }}>
Transaction Detail
</Typography>
<Container className={classes.transferDetailsContainer}>{renderTransferDetails(state.transferDetails)}</Container>
<Container className={classes.transferDetailsContainer}>
{Array.isArray(state.transferDetails)
? state.transferDetails.map(transfer => renderTransferDetails(transfer))
: renderTransferDetails(state.transferDetails)}
</Container>
</section>
) : (
<CircularProgress />
)}
{state.isLoading === "loading" && (
<Container className={classes.circularProgress}>
<CircularProgress />
</Container>
)}
</Box>
</Container>
Expand Down
79 changes: 24 additions & 55 deletions src/pages/DetailView/hooks/useFetchTransfer.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import { useEffect } from "react"
import { useParams } from "react-router-dom"
import history from "history/browser"
import { sanitizeTransferData } from "../../../utils/Helpers"
import { Routes, SharedConfig, SharedConfigDomain, Transfer } from "../../../types"
import { Routes, Transfer } from "../../../types"
import { DetailViewActions } from "../reducer"

export default function useFetchTransfer(
routes: Routes,
sharedConfig: SharedConfigDomain[] | [],
setSharedConfig: React.Dispatch<React.SetStateAction<SharedConfigDomain[] | []>>,
transferId: { id: string } | null,
dispatcher: React.Dispatch<DetailViewActions>,
): void {
const fetchTransfer = async (): Promise<void> => {
const transfer = await routes.transfer(transferId!.id)
const sanitizedTransfer = sanitizeTransferData([transfer])
export default function useFetchTransfer(routes: Routes, txHash: string, dispatcher: React.Dispatch<DetailViewActions>): void {
const fetchTransfer = async (txHashFallback?: string): Promise<void> => {
dispatcher({
type: "fetch_transfer",
})

let transfer: Transfer | Transfer[]

if (txHashFallback) {
transfer = await routes.transferByTransactionHash(txHashFallback)
} else {
transfer = await routes.transferByTransactionHash(txHash)
}

const sanitizedTransfer = Array.isArray(transfer) ? sanitizeTransferData([...transfer]) : sanitizeTransferData([transfer])

dispatcher({
type: "set_transfer_details",
payload: sanitizedTransfer[0],
payload: sanitizedTransfer,
})

dispatcher({
Expand All @@ -31,50 +36,14 @@ export default function useFetchTransfer(
})
}

// fallback when you are opening the detail view on new tab
const params = useParams()

const getTransfersFromLocalStorage = (): void => {
const transfers = localStorage.getItem("transfers")
const { txHash } = params
const parsedTransfers = JSON.parse(transfers!) as Transfer[]
const transfer = parsedTransfers.find(transfer => transfer.deposit?.txHash === txHash)

if (transfer) {
dispatcher({
type: "set_transfer_details",
payload: transfer,
})

dispatcher({
type: "set_transfer_status",
payload: "completed",
})

dispatcher({
type: "update_fetching_status",
payload: "fetching",
})
}
}

const getSharedConfigFromLocalStorage = (): void => {
const sharedConfig = localStorage.getItem("sharedConfig")
const parsedSharedConfig = JSON.parse(sharedConfig!) as SharedConfig

setSharedConfig(parsedSharedConfig.domains)
}

useEffect(() => {
if (transferId !== null) {
if (txHash !== undefined) {
void fetchTransfer()
} else {
getTransfersFromLocalStorage()
}

// fallback because ExplorerState is new coming to a new tab
if (sharedConfig.length === 0) {
getSharedConfigFromLocalStorage()
const { pathname } = history.location
const txHashFallback = pathname.split("/").filter(Boolean)[1]
history.replace(history.location.pathname, { txHash, page: 1, id: "" })
void fetchTransfer(txHashFallback)
}
}, [])
}, [txHash])
}
18 changes: 13 additions & 5 deletions src/pages/DetailView/hooks/useUpdateInterval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,30 @@ import { Routes } from "../../../types"
export default function useUpdateInterval(
state: DetailViewState,
dispatcher: React.Dispatch<DetailViewActions>,
transferId: { id: string } | null,
txHash: string,
routes: Routes,
): void {
const fetchUpdatedTransfer = async (): Promise<void> => {
const transfer = await routes.transfer(transferId!.id)
const sanitizedTransfer = sanitizeTransferData([transfer])
dispatcher({
type: "fetch_transfer",
})

const transfer = await routes.transferByTransactionHash(txHash)
const sanitizedTransfer = Array.isArray(transfer) ? sanitizeTransferData([...transfer]) : sanitizeTransferData([transfer])

dispatcher({
type: "set_transfer_details",
payload: sanitizedTransfer[0],
payload: sanitizedTransfer,
})
}

useInterval(
() => {
if (state.transferDetails?.status !== "executed") {
const isExecuted = Array.isArray(state.transferDetails)
? state.transferDetails.every(transfer => transfer.status === "executed")
: state.transferDetails?.status === "executed"

if (!isExecuted) {
void fetchUpdatedTransfer()
} else {
dispatcher({
Expand Down
Loading
Loading