-
Notifications
You must be signed in to change notification settings - Fork 95
feat: add transaction page to view all messages in a tx #261
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
base: feat/ica-decoding
Are you sure you want to change the base?
Changes from all commits
1411bdb
8aee94d
e5e15ba
f5fa4d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import { toTitleCase, trimToLength } from '@hyperlane-xyz/utils'; | ||
| import { SpinnerIcon } from '@hyperlane-xyz/widgets'; | ||
| import Image from 'next/image'; | ||
| import Link from 'next/link'; | ||
| import { useEffect, useMemo } from 'react'; | ||
| import { toast } from 'react-toastify'; | ||
| import { Card } from '../../components/layout/Card'; | ||
|
|
@@ -21,7 +22,7 @@ import { WarpTransferDetailsCard } from './cards/WarpTransferDetailsCard'; | |
| import { useIsIcaMessage } from './ica'; | ||
| import { usePiChainMessageQuery } from './pi-queries/usePiChainMessageQuery'; | ||
| import { PLACEHOLDER_MESSAGE } from './placeholderMessages'; | ||
| import { useMessageQuery } from './queries/useMessageQuery'; | ||
| import { useMessageQuery, useTransactionMessageCount } from './queries/useMessageQuery'; | ||
| import { parseWarpRouteMessageDetails } from './utils'; | ||
|
|
||
| interface Props { | ||
|
|
@@ -96,15 +97,29 @@ export function MessageDetails({ messageId, message: messageFromUrlParams }: Pro | |
| [message, warpRouteChainAddressMap, multiProvider], | ||
| ); | ||
|
|
||
| // Check if there are multiple messages in this origin transaction | ||
| const txMessageCount = useTransactionMessageCount(origin?.hash); | ||
| const showTxLink = txMessageCount > 1; | ||
|
|
||
| return ( | ||
| <> | ||
| <Card className="flex items-center justify-between rounded-full px-1"> | ||
| <h2 className="font-medium text-blue-500">{`${ | ||
| isIcaMsg ? 'ICA ' : '' | ||
| } Message ${trimToLength(msgId, 6)} to ${getChainDisplayName( | ||
| multiProvider, | ||
| destinationChainName, | ||
| )}`}</h2> | ||
| <div className="flex items-center gap-3"> | ||
| <h2 className="font-medium text-blue-500">{`${ | ||
| isIcaMsg ? 'ICA ' : '' | ||
| } Message ${trimToLength(msgId, 6)} to ${getChainDisplayName( | ||
| multiProvider, | ||
| destinationChainName, | ||
| )}`}</h2> | ||
| {showTxLink && ( | ||
| <Link | ||
| href={`/tx/${origin.hash}`} | ||
| className="text-sm text-gray-500 transition-colors hover:text-gray-700" | ||
| > | ||
| View all {txMessageCount} messages in tx → | ||
| </Link> | ||
| )} | ||
|
Comment on lines
+118
to
+121
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| </div> | ||
| <StatusHeader | ||
| messageStatus={status} | ||
| isMessageFound={isMessageFound} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import { useRouter } from 'next/router'; | ||
| import { useEffect, useRef, useState } from 'react'; | ||
|
|
||
| import { Fade, IconButton, RefreshIcon, useDebounce } from '@hyperlane-xyz/widgets'; | ||
|
|
@@ -10,6 +11,7 @@ import { | |
| SearchEmptyError, | ||
| SearchFetching, | ||
| SearchInvalidError, | ||
| SearchRedirecting, | ||
| SearchUnknownError, | ||
| } from '../../components/search/SearchStates'; | ||
| import { useReadyMultiProvider } from '../../store'; | ||
|
|
@@ -130,6 +132,46 @@ export function MessageSearch() { | |
| const isAnyMessageFound = isMessagesFound || isPiMessagesFound; | ||
| const messageListResult = isMessagesFound ? messageList : piMessageList; | ||
|
|
||
| // Compute redirect URL for direct message/tx lookups | ||
| const router = useRouter(); | ||
| const redirectUrl = (() => { | ||
| // Wait for queries to complete | ||
| if (!hasAllRun || isAnyFetching) return null; | ||
|
|
||
| // Don't redirect without user input (prevents redirect on homepage with latest messages) | ||
| if (!hasInput) return null; | ||
|
|
||
| // Don't redirect if filters are applied | ||
| if (originChainFilter || destinationChainFilter || startTimeFilter || endTimeFilter) | ||
| return null; | ||
|
|
||
| // Need at least one result | ||
| if (!messageListResult.length) return null; | ||
|
|
||
| const firstMessage = messageListResult[0]; | ||
|
|
||
| // Single result → always go to message page | ||
| if (messageListResult.length === 1) { | ||
| return `/message/${firstMessage.msgId}`; | ||
| } | ||
yorhodes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // Multiple results + origin tx hash match → go to tx page | ||
| // Only redirect if GraphQL found results (tx page uses GraphQL only, not PI) | ||
| const inputLower = sanitizedInput.toLowerCase(); | ||
| if (isMessagesFound && firstMessage.origin?.hash?.toLowerCase() === inputLower) { | ||
| return `/tx/${firstMessage.origin.hash}`; | ||
| } | ||
|
|
||
| return null; | ||
| })(); | ||
|
Comment on lines
+137
to
+166
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rather than using a |
||
|
|
||
| // Perform the redirect | ||
| useEffect(() => { | ||
| if (redirectUrl) { | ||
| router.replace(redirectUrl); | ||
| } | ||
| }, [redirectUrl, router]); | ||
|
Comment on lines
+169
to
+173
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use |
||
|
|
||
| // Show message list if there are no errors and filters are valid | ||
| const showMessageTable = | ||
| !isAnyError && | ||
|
|
@@ -176,22 +218,28 @@ export function MessageSearch() { | |
| <RefreshButton loading={isAnyFetching} onClick={refetch} /> | ||
| </div> | ||
| </div> | ||
| <Fade show={showMessageTable}> | ||
| <SearchRedirecting show={!!redirectUrl} /> | ||
| <Fade show={showMessageTable && !redirectUrl}> | ||
| <MessageTable messageList={messageListResult} isFetching={isAnyFetching} /> | ||
| </Fade> | ||
| <SearchFetching | ||
| show={!isAnyError && isValidInput && !isAnyMessageFound && !hasAllRun} | ||
| show={!redirectUrl && !isAnyError && isValidInput && !isAnyMessageFound && !hasAllRun} | ||
| isPiFetching={isPiFetching} | ||
| /> | ||
| <SearchEmptyError | ||
| show={!isAnyError && isValidInput && !isAnyMessageFound && hasAllRun} | ||
| show={!redirectUrl && !isAnyError && isValidInput && !isAnyMessageFound && hasAllRun} | ||
| hasInput={hasInput} | ||
| allowAddress={true} | ||
| /> | ||
| <SearchUnknownError show={isAnyError && isValidInput} /> | ||
| <SearchInvalidError show={!isValidInput} allowAddress={true} /> | ||
| <SearchUnknownError show={!redirectUrl && isAnyError && isValidInput} /> | ||
| <SearchInvalidError show={!redirectUrl && !isValidInput} allowAddress={true} /> | ||
| <SearchChainError | ||
| show={(!isValidOrigin || !isValidDestination) && isValidInput && !!multiProvider} | ||
| show={ | ||
| !redirectUrl && | ||
| (!isValidOrigin || !isValidDestination) && | ||
| isValidInput && | ||
| !!multiProvider | ||
| } | ||
| /> | ||
| </Card> | ||
| </> | ||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in practice this text never actually shows up and it just flashes me from the search page to the tx page