From 27650572ddf9d80a774b16d49c7d822273a36044 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Thu, 21 May 2020 17:34:22 +0800 Subject: [PATCH] Display transaction timestamp in details view --- explorer/package-lock.json | 24 +++++++++---------- explorer/package.json | 2 +- .../src/components/TransactionDetails.tsx | 8 +++++++ .../components/account/StakeAccountCards.tsx | 18 +++----------- explorer/src/providers/transactions/index.tsx | 16 ++++++++++--- explorer/src/utils/date.ts | 16 +++++++++++++ 6 files changed, 53 insertions(+), 31 deletions(-) create mode 100644 explorer/src/utils/date.ts diff --git a/explorer/package-lock.json b/explorer/package-lock.json index 439bc0d057..bd13f5ad4d 100644 --- a/explorer/package-lock.json +++ b/explorer/package-lock.json @@ -1269,9 +1269,9 @@ "integrity": "sha512-DetpxZw1fzPD5xUBrIAoplLChO2VB8DlL5Gg+I1IR9b2wPqYIca2WSUxL5g1vLeR4MsQq1NeWriXAVffV+U1Fw==" }, "@solana/web3.js": { - "version": "0.49.4", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-0.49.4.tgz", - "integrity": "sha512-9mV4MXSzUtBNbtNM5CvgtpPBlLpBlJKAfE4PlwchNA/H6MDWF8ts/MCH4pMbtAr1ZIvdxpsAKqCEUXrsj0ScMA==", + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-0.51.0.tgz", + "integrity": "sha512-mt2JF9QcpL/K2/LSHgj/yqSQXxvCNkLknfvmDClLeI2VbtYMypGcw4tU4C2GrdTzKRUdzM8ncaGONxLJKgFsTQ==", "requires": { "@babel/runtime": "^7.3.1", "bn.js": "^5.0.0", @@ -1291,9 +1291,9 @@ }, "dependencies": { "bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-IUTD/REb78Z2eodka1QZyyEk66pciRcP6Sroka0aI3tG/iwIdYLrBD62RsubR7vqdt3WyX8p4jxeatzmRSphtA==" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", + "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==" }, "buffer": { "version": "5.6.0", @@ -1687,9 +1687,9 @@ "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==" }, "@types/lodash": { - "version": "4.14.151", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.151.tgz", - "integrity": "sha512-Zst90IcBX5wnwSu7CAS0vvJkTjTELY4ssKbHiTnGcJgi170uiS8yQDdc3v6S77bRqYQIN1App5a1Pc2lceE5/g==" + "version": "4.14.152", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.152.tgz", + "integrity": "sha512-Vwf9YF2x1GE3WNeUMjT5bTHa2DqgUo87ocdgTScupY2JclZ5Nn7W2RLM/N0+oreexUk8uaVugR81NnTY/jNNXg==" }, "@types/minimatch": { "version": "3.0.3", @@ -1717,9 +1717,9 @@ "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==" }, "@types/qs": { - "version": "6.9.2", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.2.tgz", - "integrity": "sha512-a9bDi4Z3zCZf4Lv1X/vwnvbbDYSNz59h3i3KdyuYYN+YrLjSeJD0dnphdULDfySvUv6Exy/O0K6wX/kQpnPQ+A==" + "version": "6.9.3", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.3.tgz", + "integrity": "sha512-7s9EQWupR1fTc2pSMtXRQ9w9gLOcrJn+h7HOXw4evxyvVqMi4f+q7d2tnFe3ng3SNHjtK+0EzGMGFUQX4/AQRA==" }, "@types/range-parser": { "version": "1.2.3", diff --git a/explorer/package.json b/explorer/package.json index c2cf21215a..063e314216 100644 --- a/explorer/package.json +++ b/explorer/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@solana/web3.js": "^0.49.4", + "@solana/web3.js": "^0.51.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", diff --git a/explorer/src/components/TransactionDetails.tsx b/explorer/src/components/TransactionDetails.tsx index caa99e9de4..ed543ccc4e 100644 --- a/explorer/src/components/TransactionDetails.tsx +++ b/explorer/src/components/TransactionDetails.tsx @@ -24,6 +24,7 @@ import { StakeDetailsCard } from "./instruction/stake/StakeDetailsCard"; import ErrorCard from "./common/ErrorCard"; import LoadingCard from "./common/LoadingCard"; import TableCardBody from "./common/TableCardBody"; +import { displayTimestamp } from "utils/date"; type Props = { signature: TransactionSignature }; export default function TransactionDetails({ signature }: Props) { @@ -145,6 +146,13 @@ function StatusCard({ signature }: Props) { {renderResult()} + {info.timestamp && ( + + Timestamp + {displayTimestamp(info.timestamp)} + + )} + Confirmations {info.confirmations} diff --git a/explorer/src/components/account/StakeAccountCards.tsx b/explorer/src/components/account/StakeAccountCards.tsx index e4c7e14d3b..f7877abe42 100644 --- a/explorer/src/components/account/StakeAccountCards.tsx +++ b/explorer/src/components/account/StakeAccountCards.tsx @@ -4,6 +4,7 @@ import TableCardBody from "components/common/TableCardBody"; import { lamportsToSolString } from "utils"; import Copyable from "components/Copyable"; import { displayAddress } from "utils/tx"; +import { displayTimestamp } from "utils/date"; import { Account, useFetchAccountInfo } from "providers/accounts"; export function StakeAccountCards({ @@ -26,23 +27,10 @@ export function StakeAccountCards({ function LockupCard({ stakeAccount }: { stakeAccount: StakeAccount }) { const unixTimestamp = stakeAccount.meta?.lockup.unixTimestamp; if (unixTimestamp && unixTimestamp > 0) { - const expireDate = new Date(unixTimestamp * 1000); - const dateString = new Intl.DateTimeFormat("en-US", { - year: "numeric", - month: "long", - day: "numeric" - }).format(expireDate); - const timeString = new Intl.DateTimeFormat("en-US", { - hour: "numeric", - minute: "numeric", - second: "numeric", - hour12: false, - timeZoneName: "long" - }).format(expireDate); - const expireString = `${dateString} at ${timeString}`; + const prettyTimestamp = displayTimestamp(unixTimestamp); return (
- Account is locked! Lockup expires on {expireString} + Account is locked! Lockup expires on {prettyTimestamp}
); } else { diff --git a/explorer/src/providers/transactions/index.tsx b/explorer/src/providers/transactions/index.tsx index 770ce01ca9..f70efa2758 100644 --- a/explorer/src/providers/transactions/index.tsx +++ b/explorer/src/providers/transactions/index.tsx @@ -5,7 +5,8 @@ import { SystemProgram, Account, SignatureResult, - PublicKey + PublicKey, + sendAndConfirmTransaction } from "@solana/web3.js"; import { useQuery } from "../../utils/url"; import { useCluster, Cluster, ClusterStatus } from "../cluster"; @@ -28,6 +29,7 @@ export type Confirmations = number | "max"; export interface TransactionStatusInfo { slot: number; result: SignatureResult; + timestamp: number | null; confirmations: Confirmations; } @@ -199,7 +201,12 @@ async function createTestTransaction( toPubkey: testAccount.publicKey, lamports: 1 }); - const signature = await connection.sendTransaction(tx, testAccount); + const signature = await sendAndConfirmTransaction( + connection, + tx, + [testAccount], + 1 + ); fetchTransactionStatus(dispatch, signature, url, clusterStatus); } catch (error) { console.error("Failed to create test failure transaction", error); @@ -223,11 +230,13 @@ export async function fetchTransactionStatus( let fetchStatus; let info: TransactionStatusInfo | undefined; try { - const { value } = await new Connection(url).getSignatureStatus(signature, { + const connection = new Connection(url); + const { value } = await connection.getSignatureStatus(signature, { searchTransactionHistory: true }); if (value !== null) { + let timestamp = await connection.getBlockTime(value.slot); let confirmations: Confirmations; if (typeof value.confirmations === "number") { confirmations = value.confirmations; @@ -237,6 +246,7 @@ export async function fetchTransactionStatus( info = { slot: value.slot, + timestamp, confirmations, result: { err: value.err } }; diff --git a/explorer/src/utils/date.ts b/explorer/src/utils/date.ts new file mode 100644 index 0000000000..3e9a59f123 --- /dev/null +++ b/explorer/src/utils/date.ts @@ -0,0 +1,16 @@ +export function displayTimestamp(unixTimestamp: number): string { + const expireDate = new Date(unixTimestamp * 1000); + const dateString = new Intl.DateTimeFormat("en-US", { + year: "numeric", + month: "long", + day: "numeric" + }).format(expireDate); + const timeString = new Intl.DateTimeFormat("en-US", { + hour: "numeric", + minute: "numeric", + second: "numeric", + hour12: false, + timeZoneName: "long" + }).format(expireDate); + return `${dateString} at ${timeString}`; +}