From 90a36e6c2a3bfa748777e57d51502ad022645fec Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Tue, 20 Feb 2024 18:16:37 +0100 Subject: [PATCH 1/8] feat: add slot page --- api/src/models/api/nova/ISlotRequest.ts | 11 +++ api/src/models/api/nova/ISlotResponse.ts | 8 ++ api/src/routes.ts | 1 + api/src/routes/nova/slot/get.ts | 30 +++++++ api/src/services/nova/novaApiService.ts | 11 +++ client/src/app/routes.tsx | 2 + client/src/app/routes/nova/SlotPage.scss | 86 ++++++++++++++++++++ client/src/app/routes/nova/SlotPage.tsx | 90 +++++++++++++++++++++ client/src/helpers/nova/hooks/useSlot.ts | 54 +++++++++++++ client/src/models/api/nova/ISlotRequest.ts | 11 +++ client/src/models/api/nova/ISlotResponse.ts | 6 ++ client/src/services/nova/novaApiClient.ts | 11 +++ 12 files changed, 321 insertions(+) create mode 100644 api/src/models/api/nova/ISlotRequest.ts create mode 100644 api/src/models/api/nova/ISlotResponse.ts create mode 100644 api/src/routes/nova/slot/get.ts create mode 100644 client/src/app/routes/nova/SlotPage.scss create mode 100644 client/src/app/routes/nova/SlotPage.tsx create mode 100644 client/src/helpers/nova/hooks/useSlot.ts create mode 100644 client/src/models/api/nova/ISlotRequest.ts create mode 100644 client/src/models/api/nova/ISlotResponse.ts diff --git a/api/src/models/api/nova/ISlotRequest.ts b/api/src/models/api/nova/ISlotRequest.ts new file mode 100644 index 000000000..557fb3337 --- /dev/null +++ b/api/src/models/api/nova/ISlotRequest.ts @@ -0,0 +1,11 @@ +export interface ISlotRequest { + /** + * The network to search on. + */ + network: string; + + /** + * The slot index to get the details for. + */ + slotIndex: string; +} diff --git a/api/src/models/api/nova/ISlotResponse.ts b/api/src/models/api/nova/ISlotResponse.ts new file mode 100644 index 000000000..455990095 --- /dev/null +++ b/api/src/models/api/nova/ISlotResponse.ts @@ -0,0 +1,8 @@ +import { SlotCommitment } from "@iota/sdk-nova"; + +export interface ISlotResponse { + /** + * The deserialized slot. + */ + slot?: SlotCommitment; +} diff --git a/api/src/routes.ts b/api/src/routes.ts index 4fbceb165..33af036bf 100644 --- a/api/src/routes.ts +++ b/api/src/routes.ts @@ -244,4 +244,5 @@ export const routes: IRoute[] = [ }, { path: "/nova/block/:network/:blockId", method: "get", folder: "nova/block", func: "get" }, { path: "/nova/block/metadata/:network/:blockId", method: "get", folder: "nova/block/metadata", func: "get" }, + { path: "/nova/slot/:network/:slotIndex", method: "get", folder: "nova/slot", func: "get" }, ]; diff --git a/api/src/routes/nova/slot/get.ts b/api/src/routes/nova/slot/get.ts new file mode 100644 index 000000000..40339920f --- /dev/null +++ b/api/src/routes/nova/slot/get.ts @@ -0,0 +1,30 @@ +import { ServiceFactory } from "../../../factories/serviceFactory"; +import { ISlotRequest } from "../../../models/api/nova/ISlotRequest"; +import { ISlotResponse } from "../../../models/api/nova/ISlotResponse"; +import { IConfiguration } from "../../../models/configuration/IConfiguration"; +import { NOVA } from "../../../models/db/protocolVersion"; +import { NetworkService } from "../../../services/networkService"; +import { NovaApiService } from "../../../services/nova/novaApiService"; +import { ValidationHelper } from "../../../utils/validationHelper"; + +/** + * Fetch the block from the network. + * @param _ The configuration. + * @param request The request. + * @returns The response. + */ +export async function get(_: IConfiguration, request: ISlotRequest): Promise { + const networkService = ServiceFactory.get("network"); + const networks = networkService.networkNames(); + ValidationHelper.oneOf(request.network, networks, "network"); + ValidationHelper.numberFromString(request.slotIndex, "slotIndex"); + + const networkConfig = networkService.get(request.network); + + if (networkConfig.protocolVersion !== NOVA) { + return {}; + } + + const novaApiService = ServiceFactory.get(`api-service-${networkConfig.network}`); + return novaApiService.getSlotCommitment(Number(request.slotIndex)); +} diff --git a/api/src/services/nova/novaApiService.ts b/api/src/services/nova/novaApiService.ts index 09d70ff27..b00d45de0 100644 --- a/api/src/services/nova/novaApiService.ts +++ b/api/src/services/nova/novaApiService.ts @@ -15,6 +15,7 @@ import { INftDetailsResponse } from "../../models/api/nova/INftDetailsResponse"; import { IOutputDetailsResponse } from "../../models/api/nova/IOutputDetailsResponse"; import { IRewardsResponse } from "../../models/api/nova/IRewardsResponse"; import { ISearchResponse } from "../../models/api/nova/ISearchResponse"; +import { ISlotResponse } from "../../models/api/nova/ISlotResponse"; import { INetwork } from "../../models/db/INetwork"; import { HexHelper } from "../../utils/hexHelper"; import { SearchExecutor } from "../../utils/nova/searchExecutor"; @@ -261,6 +262,16 @@ export class NovaApiService { return manaRewardsResponse ? { outputId, manaRewards: manaRewardsResponse } : { outputId, message: "Rewards data not found" }; } + public async getSlotCommitment(slotIndex: number): Promise { + try { + const slot = await this.client.getCommitmentByIndex(slotIndex); + + return { slot }; + } catch (e) { + logger.error(`Failed fetching slot with slot index ${slotIndex}. Cause: ${e}`); + } + } + /** * Find item on the stardust network. * @param query The query to use for finding items. diff --git a/client/src/app/routes.tsx b/client/src/app/routes.tsx index cd52d8ed5..f0633a9e1 100644 --- a/client/src/app/routes.tsx +++ b/client/src/app/routes.tsx @@ -37,6 +37,7 @@ import StardustOutputPage from "./routes/stardust/OutputPage"; import NovaBlockPage from "./routes/nova/Block"; import NovaOutputPage from "./routes/nova/OutputPage"; import NovaSearch from "./routes/nova/Search"; +import NovaSlotPage from "./routes/nova/SlotPage"; import StardustSearch from "./routes/stardust/Search"; import StardustStatisticsPage from "./routes/stardust/statistics/StatisticsPage"; import StardustTransactionPage from "./routes/stardust/TransactionPage"; @@ -178,6 +179,7 @@ const buildAppRoutes = (protocolVersion: string, withNetworkContext: (wrappedCom , , , + , ]; return ( diff --git a/client/src/app/routes/nova/SlotPage.scss b/client/src/app/routes/nova/SlotPage.scss new file mode 100644 index 000000000..2d4b2e13f --- /dev/null +++ b/client/src/app/routes/nova/SlotPage.scss @@ -0,0 +1,86 @@ +@import "../../../scss/fonts"; +@import "../../../scss/mixins"; +@import "../../../scss/media-queries"; +@import "../../../scss/variables"; + +.slot-page { + display: flex; + flex-direction: column; + + .wrapper { + display: flex; + justify-content: center; + + .inner { + display: flex; + flex: 1; + flex-direction: column; + max-width: $desktop-width; + margin: 40px 25px; + + @include desktop-down { + flex: unset; + width: 100%; + max-width: 100%; + margin: 40px 24px; + padding-right: 24px; + padding-left: 24px; + + > .row { + flex-direction: column; + } + } + + @include tablet-down { + margin: 28px 0; + } + + .slot-page--header { + display: flex; + align-items: center; + justify-content: space-between; + } + + .section { + padding-top: 44px; + + .section--header { + margin-top: 44px; + } + + .card--content__output { + margin-top: 20px; + } + } + } + } + + .tooltip { + .children { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + .tooltip__special { + background-color: #fff4df; + border-radius: 4px; + padding: 0 4px; + font-weight: 400; + } + + .wrap { + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; + width: 170px; + + .arrow { + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; + } + } + } +} diff --git a/client/src/app/routes/nova/SlotPage.tsx b/client/src/app/routes/nova/SlotPage.tsx new file mode 100644 index 000000000..78156f776 --- /dev/null +++ b/client/src/app/routes/nova/SlotPage.tsx @@ -0,0 +1,90 @@ +import React from "react"; +import Modal from "~/app/components/Modal"; +import { ModalData } from "~/app/components/ModalProps"; +import { RouteComponentProps } from "react-router-dom"; +import TruncatedId from "~/app/components/stardust/TruncatedId"; +import classNames from "classnames"; +import useSlotData from "~/helpers/nova/hooks/useSlot"; +import "./SlotPage.scss"; + +interface SlotPageProps { + network: string; + slotIndex: string; +} + +export default function SlotPage({ + match: { + params: { network, slotIndex }, + }, +}: RouteComponentProps): React.JSX.Element { + const { slotCommitment } = useSlotData(network, slotIndex); + + const message: ModalData = { + title: "Slot Page", + description: "

Slot Information here

", + }; + + const dataRows: IDataRow[] = [ + { + label: "Slot Index", + value: slotCommitment?.slot, + highlighted: true, + }, + { + label: "RMC", + value: slotCommitment?.referenceManaCost.toString(), + }, + ]; + + return ( +
+
+
+
+
+

Slot

+ +
+
+
+
+
+

General

+
+
+ {dataRows.map((dataRow, index) => { + if (dataRow.value || dataRow.truncatedId) { + return ; + } + })} +
+
+
+
+ ); +} + +interface IDataRow { + label: string; + value?: string | number; + highlighted?: boolean; + truncatedId?: { + id: string; + link?: string; + showCopyButton?: boolean; + }; +} +const DataRow = ({ label, value, truncatedId, highlighted }: IDataRow) => { + return ( +
+
{label}
+
+ {truncatedId ? ( + + ) : ( + value + )} +
+
+ ); +}; diff --git a/client/src/helpers/nova/hooks/useSlot.ts b/client/src/helpers/nova/hooks/useSlot.ts new file mode 100644 index 000000000..c3b5ee496 --- /dev/null +++ b/client/src/helpers/nova/hooks/useSlot.ts @@ -0,0 +1,54 @@ +import { SlotCommitment } from "@iota/sdk-wasm-nova/web"; +import { plainToInstance } from "class-transformer"; +import { useEffect, useState } from "react"; +import { ServiceFactory } from "~/factories/serviceFactory"; +import { useIsMounted } from "~/helpers/hooks/useIsMounted"; +import { NOVA } from "~/models/config/protocolVersion"; +import { NovaApiClient } from "~/services/nova/novaApiClient"; + +interface IUseSlotData { + slotCommitment: SlotCommitment | null; + error: string | undefined; + isLoading: boolean; +} + +export default function useSlotData(network: string, slotIndex: string): IUseSlotData { + const isMounted = useIsMounted(); + const [apiClient] = useState(ServiceFactory.get(`api-client-${NOVA}`)); + const [slotCommitment, setSlotCommitment] = useState(null); + const [error, setError] = useState(); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + setIsLoading(true); + setSlotCommitment(null); + if (!slotCommitment) { + // eslint-disable-next-line no-void + void (async () => { + apiClient + .getSlotCommitment({ + network, + slotIndex, + }) + .then((response) => { + if (isMounted) { + const slot = plainToInstance(SlotCommitment, response.slot) as unknown as SlotCommitment; + setSlotCommitment(slot); + setError(response.error); + } + }) + .finally(() => { + setIsLoading(false); + }); + })(); + } else { + setIsLoading(false); + } + }, [network, slotIndex]); + + return { + slotCommitment, + error, + isLoading, + }; +} diff --git a/client/src/models/api/nova/ISlotRequest.ts b/client/src/models/api/nova/ISlotRequest.ts new file mode 100644 index 000000000..b00482593 --- /dev/null +++ b/client/src/models/api/nova/ISlotRequest.ts @@ -0,0 +1,11 @@ +export interface ISlotRequest { + /** + * The network to search on. + */ + network: string; + + /** + * The slot index to get the commitment for. + */ + slotIndex: string; +} diff --git a/client/src/models/api/nova/ISlotResponse.ts b/client/src/models/api/nova/ISlotResponse.ts new file mode 100644 index 000000000..dc7e83cb4 --- /dev/null +++ b/client/src/models/api/nova/ISlotResponse.ts @@ -0,0 +1,6 @@ +import { SlotCommitment } from "@iota/sdk-wasm-nova/web"; +import { IResponse } from "../IResponse"; + +export interface ISlotResponse extends IResponse { + slot: SlotCommitment; +} diff --git a/client/src/services/nova/novaApiClient.ts b/client/src/services/nova/novaApiClient.ts index 0ad378576..66aaf87f5 100644 --- a/client/src/services/nova/novaApiClient.ts +++ b/client/src/services/nova/novaApiClient.ts @@ -29,6 +29,8 @@ import { IAddressDetailsRequest } from "~/models/api/nova/address/IAddressDetail import { IAddressDetailsResponse } from "~/models/api/nova/address/IAddressDetailsResponse"; import { IFoundriesResponse } from "~/models/api/nova/foundry/IFoundriesResponse"; import { IFoundriesRequest } from "~/models/api/nova/foundry/IFoundriesRequest"; +import { ISlotRequest } from "~/models/api/nova/ISlotRequest"; +import { ISlotResponse } from "~/models/api/nova/ISlotResponse"; /** * Class to handle api communications on nova. @@ -155,6 +157,15 @@ export class NovaApiClient extends ApiClient { return this.callApi(`nova/output/rewards/${request.network}/${request.outputId}`, "get"); } + /** + * Get the slot commitment. + * @param request The request to send. + * @returns The response from the request. + */ + public async getSlotCommitment(request: ISlotRequest): Promise { + return this.callApi(`nova/slot/${request.network}/${request.slotIndex}`, "get"); + } + /** * Get the stats. * @param request The request to send. From b9386d2380469b9e94f20ec64787d43d63468233 Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Wed, 21 Feb 2024 12:29:22 +0100 Subject: [PATCH 2/8] feat: add utxo changes --- api/src/routes.ts | 1 + api/src/routes/nova/slot/utxochanges/get.ts | 30 +++++++++++ api/src/services/nova/novaApiService.ts | 12 ++++- .../src/app/components/nova/StatusPill.scss | 44 +++++++++++++++ client/src/app/components/nova/StatusPill.tsx | 53 +++++++++++++++++++ client/src/app/routes/nova/SlotPage.tsx | 16 +++++- .../{useSlot.ts => useSlotCommitment.ts} | 0 client/src/services/nova/novaApiClient.ts | 7 +++ 8 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 api/src/routes/nova/slot/utxochanges/get.ts create mode 100644 client/src/app/components/nova/StatusPill.scss create mode 100644 client/src/app/components/nova/StatusPill.tsx rename client/src/helpers/nova/hooks/{useSlot.ts => useSlotCommitment.ts} (100%) diff --git a/api/src/routes.ts b/api/src/routes.ts index 33af036bf..0ce736017 100644 --- a/api/src/routes.ts +++ b/api/src/routes.ts @@ -245,4 +245,5 @@ export const routes: IRoute[] = [ { path: "/nova/block/:network/:blockId", method: "get", folder: "nova/block", func: "get" }, { path: "/nova/block/metadata/:network/:blockId", method: "get", folder: "nova/block/metadata", func: "get" }, { path: "/nova/slot/:network/:slotIndex", method: "get", folder: "nova/slot", func: "get" }, + { path: "/nova/slot/utxochanges/:network/:slotIndex", method: "get", folder: "nova/slot/utxochanges", func: "get" }, ]; diff --git a/api/src/routes/nova/slot/utxochanges/get.ts b/api/src/routes/nova/slot/utxochanges/get.ts new file mode 100644 index 000000000..55d51ef23 --- /dev/null +++ b/api/src/routes/nova/slot/utxochanges/get.ts @@ -0,0 +1,30 @@ +import { UtxoChangesResponse } from "@iota/sdk-nova"; +import { ServiceFactory } from "../../../../factories/serviceFactory"; +import { ISlotRequest } from "../../../../models/api/nova/ISlotRequest"; +import { IConfiguration } from "../../../../models/configuration/IConfiguration"; +import { NOVA } from "../../../../models/db/protocolVersion"; +import { NetworkService } from "../../../../services/networkService"; +import { NovaApiService } from "../../../../services/nova/novaApiService"; +import { ValidationHelper } from "../../../../utils/validationHelper"; + +/** + * Fetch the block from the network. + * @param _ The configuration. + * @param request The request. + * @returns The response. + */ +export async function get(_: IConfiguration, request: ISlotRequest): Promise> { + const networkService = ServiceFactory.get("network"); + const networks = networkService.networkNames(); + ValidationHelper.oneOf(request.network, networks, "network"); + ValidationHelper.numberFromString(request.slotIndex, "slotIndex"); + + const networkConfig = networkService.get(request.network); + + if (networkConfig.protocolVersion !== NOVA) { + return {}; + } + + const novaApiService = ServiceFactory.get(`api-service-${networkConfig.network}`); + return novaApiService.getSlotUTXOChanges(Number(request.slotIndex)); +} diff --git a/api/src/services/nova/novaApiService.ts b/api/src/services/nova/novaApiService.ts index b00d45de0..34615ddf1 100644 --- a/api/src/services/nova/novaApiService.ts +++ b/api/src/services/nova/novaApiService.ts @@ -1,7 +1,7 @@ /* eslint-disable import/no-unresolved */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-return */ -import { Client, OutputResponse } from "@iota/sdk-nova"; +import { Client, OutputResponse, UtxoChangesResponse } from "@iota/sdk-nova"; import { ServiceFactory } from "../../factories/serviceFactory"; import logger from "../../logger"; import { IFoundriesResponse } from "../../models/api/nova/foundry/IFoundriesResponse"; @@ -272,6 +272,16 @@ export class NovaApiService { } } + public async getSlotUTXOChanges(slotIndex: number): Promise> { + try { + const response = await this.client.getUtxoChangesByIndex(slotIndex); + + return response; + } catch (e) { + logger.error(`Failed fetching slot utxo changes with slot index ${slotIndex}. Cause: ${e}`); + } + } + /** * Find item on the stardust network. * @param query The query to use for finding items. diff --git a/client/src/app/components/nova/StatusPill.scss b/client/src/app/components/nova/StatusPill.scss new file mode 100644 index 000000000..244d28285 --- /dev/null +++ b/client/src/app/components/nova/StatusPill.scss @@ -0,0 +1,44 @@ +@import "../../../../scss/fonts"; +@import "../../../../scss/mixins"; +@import "../../../../scss/media-queries"; +@import "../../../../scss/variables"; + +.status-pill { + @include font-size(12px); + + display: flex; + align-items: center; + height: 24px; + margin-right: 8px; + padding: 0 8px; + border: 0; + border-radius: 6px; + outline: none; + background-color: $gray-light; + color: $gray-midnight; + font-family: $inter; + font-weight: 500; + letter-spacing: 0.5px; + white-space: nowrap; + + @include phone-down { + height: 32px; + } + + &.status__ { + &confirmed { + background-color: var(--message-confirmed-bg); + color: $mint-green-7; + } + + &conflicting { + background-color: var(--message-conflicting-bg); + color: $red-7; + } + + &pending { + background-color: var(--light-bg); + color: #8493ad; + } + } +} diff --git a/client/src/app/components/nova/StatusPill.tsx b/client/src/app/components/nova/StatusPill.tsx new file mode 100644 index 000000000..519d626c2 --- /dev/null +++ b/client/src/app/components/nova/StatusPill.tsx @@ -0,0 +1,53 @@ +import classNames from "classnames"; +import React from "react"; +import "./StatusPill.scss"; + +export enum PillStatus { + /** + * The status is pending. + */ + Pending = "pending", + + /** + * The status is confirmed. + */ + Confirmed = "confirmed", + + /** + * The status is rejected. + */ + Rejected = "rejected", +} + +interface IStatusPill { + /** + * Label for the status. + */ + label: string; + /** + * The status of the pill. + */ + status: PillStatus; +} + +const StatusPill: React.FC = ({ label, status }) => ( +
+ {status && ( +
+ {label} +
+ )} +
+); + +export default StatusPill; diff --git a/client/src/app/routes/nova/SlotPage.tsx b/client/src/app/routes/nova/SlotPage.tsx index 78156f776..e81a3ceaa 100644 --- a/client/src/app/routes/nova/SlotPage.tsx +++ b/client/src/app/routes/nova/SlotPage.tsx @@ -4,7 +4,7 @@ import { ModalData } from "~/app/components/ModalProps"; import { RouteComponentProps } from "react-router-dom"; import TruncatedId from "~/app/components/stardust/TruncatedId"; import classNames from "classnames"; -import useSlotData from "~/helpers/nova/hooks/useSlot"; +import useSlotCommitment from "~/helpers/nova/hooks/useSlotCommitment"; import "./SlotPage.scss"; interface SlotPageProps { @@ -12,12 +12,24 @@ interface SlotPageProps { slotIndex: string; } +export enum SlotState { + /** + * The slot is pending. + */ + Pending = "pending", + + /** + * The slot is finalized. + */ + Finalized = "finalized", +} + export default function SlotPage({ match: { params: { network, slotIndex }, }, }: RouteComponentProps): React.JSX.Element { - const { slotCommitment } = useSlotData(network, slotIndex); + const { slotCommitment } = useSlotCommitment(network, slotIndex); const message: ModalData = { title: "Slot Page", diff --git a/client/src/helpers/nova/hooks/useSlot.ts b/client/src/helpers/nova/hooks/useSlotCommitment.ts similarity index 100% rename from client/src/helpers/nova/hooks/useSlot.ts rename to client/src/helpers/nova/hooks/useSlotCommitment.ts diff --git a/client/src/services/nova/novaApiClient.ts b/client/src/services/nova/novaApiClient.ts index 66aaf87f5..45f36e91f 100644 --- a/client/src/services/nova/novaApiClient.ts +++ b/client/src/services/nova/novaApiClient.ts @@ -166,6 +166,13 @@ export class NovaApiClient extends ApiClient { return this.callApi(`nova/slot/${request.network}/${request.slotIndex}`, "get"); } + /** + * Get the slot utxo changes. + */ + public async getSlotUTXOChanges(request: ISlotRequest): Promise { + return this.callApi(`nova/slot/utxochanges/${request.network}/${request.slotIndex}`, "get"); + } + /** * Get the stats. * @param request The request to send. From af6798b55bf9087c28f78f3c8b3a66aa3ab22e4a Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Wed, 21 Feb 2024 20:10:40 +0100 Subject: [PATCH 3/8] refactor: cleanup unused code and add not found component --- api/src/routes.ts | 1 - api/src/routes/nova/slot/utxochanges/get.ts | 30 ----- api/src/services/nova/novaApiService.ts | 17 +-- .../src/app/components/nova/PageDataRow.tsx | 30 +++++ .../src/app/components/nova/StatusPill.scss | 13 +- client/src/app/components/nova/StatusPill.tsx | 44 ++----- client/src/app/lib/enums/index.ts | 1 + client/src/app/lib/enums/slot-state.enums.ts | 11 ++ client/src/app/lib/ui/enums/index.ts | 1 + .../src/app/lib/ui/enums/pill-state.enum.ts | 16 +++ client/src/app/routes/nova/SlotPage.scss | 41 ++----- client/src/app/routes/nova/SlotPage.tsx | 113 ++++++++---------- .../assets/modals/nova/slot/main-header.json | 11 ++ client/src/services/nova/novaApiClient.ts | 7 -- 14 files changed, 152 insertions(+), 184 deletions(-) delete mode 100644 api/src/routes/nova/slot/utxochanges/get.ts create mode 100644 client/src/app/components/nova/PageDataRow.tsx create mode 100644 client/src/app/lib/enums/index.ts create mode 100644 client/src/app/lib/enums/slot-state.enums.ts create mode 100644 client/src/app/lib/ui/enums/index.ts create mode 100644 client/src/app/lib/ui/enums/pill-state.enum.ts create mode 100644 client/src/assets/modals/nova/slot/main-header.json diff --git a/api/src/routes.ts b/api/src/routes.ts index 0ce736017..33af036bf 100644 --- a/api/src/routes.ts +++ b/api/src/routes.ts @@ -245,5 +245,4 @@ export const routes: IRoute[] = [ { path: "/nova/block/:network/:blockId", method: "get", folder: "nova/block", func: "get" }, { path: "/nova/block/metadata/:network/:blockId", method: "get", folder: "nova/block/metadata", func: "get" }, { path: "/nova/slot/:network/:slotIndex", method: "get", folder: "nova/slot", func: "get" }, - { path: "/nova/slot/utxochanges/:network/:slotIndex", method: "get", folder: "nova/slot/utxochanges", func: "get" }, ]; diff --git a/api/src/routes/nova/slot/utxochanges/get.ts b/api/src/routes/nova/slot/utxochanges/get.ts deleted file mode 100644 index 55d51ef23..000000000 --- a/api/src/routes/nova/slot/utxochanges/get.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { UtxoChangesResponse } from "@iota/sdk-nova"; -import { ServiceFactory } from "../../../../factories/serviceFactory"; -import { ISlotRequest } from "../../../../models/api/nova/ISlotRequest"; -import { IConfiguration } from "../../../../models/configuration/IConfiguration"; -import { NOVA } from "../../../../models/db/protocolVersion"; -import { NetworkService } from "../../../../services/networkService"; -import { NovaApiService } from "../../../../services/nova/novaApiService"; -import { ValidationHelper } from "../../../../utils/validationHelper"; - -/** - * Fetch the block from the network. - * @param _ The configuration. - * @param request The request. - * @returns The response. - */ -export async function get(_: IConfiguration, request: ISlotRequest): Promise> { - const networkService = ServiceFactory.get("network"); - const networks = networkService.networkNames(); - ValidationHelper.oneOf(request.network, networks, "network"); - ValidationHelper.numberFromString(request.slotIndex, "slotIndex"); - - const networkConfig = networkService.get(request.network); - - if (networkConfig.protocolVersion !== NOVA) { - return {}; - } - - const novaApiService = ServiceFactory.get(`api-service-${networkConfig.network}`); - return novaApiService.getSlotUTXOChanges(Number(request.slotIndex)); -} diff --git a/api/src/services/nova/novaApiService.ts b/api/src/services/nova/novaApiService.ts index 34615ddf1..e60503dcf 100644 --- a/api/src/services/nova/novaApiService.ts +++ b/api/src/services/nova/novaApiService.ts @@ -1,7 +1,7 @@ /* eslint-disable import/no-unresolved */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-return */ -import { Client, OutputResponse, UtxoChangesResponse } from "@iota/sdk-nova"; +import { Client, OutputResponse } from "@iota/sdk-nova"; import { ServiceFactory } from "../../factories/serviceFactory"; import logger from "../../logger"; import { IFoundriesResponse } from "../../models/api/nova/foundry/IFoundriesResponse"; @@ -262,6 +262,11 @@ export class NovaApiService { return manaRewardsResponse ? { outputId, manaRewards: manaRewardsResponse } : { outputId, message: "Rewards data not found" }; } + /** + * Get the slot commitment. + * @param slotIndex The slot index to get the commitment for. + * @returns The slot commitment. + */ public async getSlotCommitment(slotIndex: number): Promise { try { const slot = await this.client.getCommitmentByIndex(slotIndex); @@ -272,16 +277,6 @@ export class NovaApiService { } } - public async getSlotUTXOChanges(slotIndex: number): Promise> { - try { - const response = await this.client.getUtxoChangesByIndex(slotIndex); - - return response; - } catch (e) { - logger.error(`Failed fetching slot utxo changes with slot index ${slotIndex}. Cause: ${e}`); - } - } - /** * Find item on the stardust network. * @param query The query to use for finding items. diff --git a/client/src/app/components/nova/PageDataRow.tsx b/client/src/app/components/nova/PageDataRow.tsx new file mode 100644 index 000000000..034ef456c --- /dev/null +++ b/client/src/app/components/nova/PageDataRow.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import classNames from "classnames"; +import TruncatedId from "../stardust/TruncatedId"; + +export interface IPageDataRow { + label: string; + value?: string | number; + highlighted?: boolean; + truncatedId?: { + id: string; + link?: string; + showCopyButton?: boolean; + }; +} +const PageDataRow = ({ label, value, truncatedId, highlighted }: IPageDataRow): React.JSX.Element => { + return ( +
+
{label}
+
+ {truncatedId ? ( + + ) : ( + value + )} +
+
+ ); +}; + +export default PageDataRow; diff --git a/client/src/app/components/nova/StatusPill.scss b/client/src/app/components/nova/StatusPill.scss index 244d28285..89bf257ff 100644 --- a/client/src/app/components/nova/StatusPill.scss +++ b/client/src/app/components/nova/StatusPill.scss @@ -1,20 +1,18 @@ -@import "../../../../scss/fonts"; -@import "../../../../scss/mixins"; -@import "../../../../scss/media-queries"; -@import "../../../../scss/variables"; +@import "./../../../scss/fonts"; +@import "./../../../scss/mixins"; +@import "./../../../scss/media-queries"; +@import "./../../../scss/variables"; .status-pill { @include font-size(12px); display: flex; align-items: center; - height: 24px; margin-right: 8px; - padding: 0 8px; + padding: 6px 12px; border: 0; border-radius: 6px; outline: none; - background-color: $gray-light; color: $gray-midnight; font-family: $inter; font-weight: 500; @@ -33,7 +31,6 @@ &conflicting { background-color: var(--message-conflicting-bg); - color: $red-7; } &pending { diff --git a/client/src/app/components/nova/StatusPill.tsx b/client/src/app/components/nova/StatusPill.tsx index 519d626c2..f399bfcbc 100644 --- a/client/src/app/components/nova/StatusPill.tsx +++ b/client/src/app/components/nova/StatusPill.tsx @@ -1,24 +1,8 @@ -import classNames from "classnames"; import React from "react"; +import classNames from "classnames"; +import { PillState } from "~/app/lib/ui/enums"; import "./StatusPill.scss"; -export enum PillStatus { - /** - * The status is pending. - */ - Pending = "pending", - - /** - * The status is confirmed. - */ - Confirmed = "confirmed", - - /** - * The status is rejected. - */ - Rejected = "rejected", -} - interface IStatusPill { /** * Label for the status. @@ -27,27 +11,23 @@ interface IStatusPill { /** * The status of the pill. */ - status: PillStatus; + state: PillState; } -const StatusPill: React.FC = ({ label, status }) => ( -
- {status && ( +const StatusPill: React.FC = ({ label, state }): React.JSX.Element => ( + <> + {label && (
{label}
)} -
+ ); export default StatusPill; diff --git a/client/src/app/lib/enums/index.ts b/client/src/app/lib/enums/index.ts new file mode 100644 index 000000000..877581b6e --- /dev/null +++ b/client/src/app/lib/enums/index.ts @@ -0,0 +1 @@ +export * from "./slot-state.enums"; diff --git a/client/src/app/lib/enums/slot-state.enums.ts b/client/src/app/lib/enums/slot-state.enums.ts new file mode 100644 index 000000000..09d91efb5 --- /dev/null +++ b/client/src/app/lib/enums/slot-state.enums.ts @@ -0,0 +1,11 @@ +export enum SlotState { + /** + * The slot is pending. + */ + Pending = "pending", + + /** + * The slot is finalized. + */ + Finalized = "finalized", +} diff --git a/client/src/app/lib/ui/enums/index.ts b/client/src/app/lib/ui/enums/index.ts new file mode 100644 index 000000000..ccfb67f8b --- /dev/null +++ b/client/src/app/lib/ui/enums/index.ts @@ -0,0 +1 @@ +export * from "./pill-state.enum"; diff --git a/client/src/app/lib/ui/enums/pill-state.enum.ts b/client/src/app/lib/ui/enums/pill-state.enum.ts new file mode 100644 index 000000000..8e6bf6050 --- /dev/null +++ b/client/src/app/lib/ui/enums/pill-state.enum.ts @@ -0,0 +1,16 @@ +export enum PillState { + /** + * The status is pending. + */ + Pending = "pending", + + /** + * The status is confirmed. + */ + Confirmed = "confirmed", + + /** + * The status is rejected. + */ + Rejected = "rejected", +} diff --git a/client/src/app/routes/nova/SlotPage.scss b/client/src/app/routes/nova/SlotPage.scss index 2d4b2e13f..2c802392c 100644 --- a/client/src/app/routes/nova/SlotPage.scss +++ b/client/src/app/routes/nova/SlotPage.scss @@ -37,8 +37,16 @@ .slot-page--header { display: flex; - align-items: center; - justify-content: space-between; + flex-direction: column; + align-items: flex-start; + + .header--title { + margin-bottom: 8px; + } + + .header--status { + display: flex; + } } .section { @@ -54,33 +62,4 @@ } } } - - .tooltip { - .children { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - } - .tooltip__special { - background-color: #fff4df; - border-radius: 4px; - padding: 0 4px; - font-weight: 400; - } - - .wrap { - left: 0; - right: 0; - margin-left: auto; - margin-right: auto; - width: 170px; - - .arrow { - left: 0; - right: 0; - margin-left: auto; - margin-right: auto; - } - } - } } diff --git a/client/src/app/routes/nova/SlotPage.tsx b/client/src/app/routes/nova/SlotPage.tsx index e81a3ceaa..df9e9eb87 100644 --- a/client/src/app/routes/nova/SlotPage.tsx +++ b/client/src/app/routes/nova/SlotPage.tsx @@ -1,45 +1,38 @@ import React from "react"; +import useSlotCommitment from "~/helpers/nova/hooks/useSlotCommitment"; +import StatusPill from "~/app/components/nova/StatusPill"; +import PageDataRow, { IPageDataRow } from "~/app/components/nova/PageDataRow"; import Modal from "~/app/components/Modal"; -import { ModalData } from "~/app/components/ModalProps"; +import mainHeaderMessage from "~assets/modals/nova/slot/main-header.json"; +import NotFound from "~/app/components/NotFound"; +import { SlotState } from "~/app/lib/enums"; import { RouteComponentProps } from "react-router-dom"; -import TruncatedId from "~/app/components/stardust/TruncatedId"; -import classNames from "classnames"; -import useSlotCommitment from "~/helpers/nova/hooks/useSlotCommitment"; +import { PillState } from "~/app/lib/ui/enums"; import "./SlotPage.scss"; -interface SlotPageProps { - network: string; - slotIndex: string; -} - -export enum SlotState { - /** - * The slot is pending. - */ - Pending = "pending", - - /** - * The slot is finalized. - */ - Finalized = "finalized", -} +const SLOT_STATE_TO_PILL_STATE: Record = { + [SlotState.Pending]: PillState.Pending, + [SlotState.Finalized]: PillState.Confirmed, +}; export default function SlotPage({ match: { params: { network, slotIndex }, }, -}: RouteComponentProps): React.JSX.Element { +}: RouteComponentProps<{ + network: string; + slotIndex: string; +}>): React.JSX.Element { const { slotCommitment } = useSlotCommitment(network, slotIndex); - const message: ModalData = { - title: "Slot Page", - description: "

Slot Information here

", - }; + const parsedSlotIndex = parseSlotIndex(slotIndex); + const slotState = slotCommitment ? SlotState.Finalized : SlotState.Pending; + const pillState: PillState = SLOT_STATE_TO_PILL_STATE[slotState]; - const dataRows: IDataRow[] = [ + const dataRows: IPageDataRow[] = [ { label: "Slot Index", - value: slotCommitment?.slot, + value: slotCommitment?.slot || parsedSlotIndex, highlighted: true, }, { @@ -48,55 +41,47 @@ export default function SlotPage({ }, ]; + function parseSlotIndex(slotIndex: string): number | undefined { + const slotIndexNum = parseInt(slotIndex, 10); + if (isNaN(slotIndexNum)) { + return; + } + return slotIndexNum; + } + return (
-
+

Slot

- +
+ {slotCommitment && ( +
+ +
+ )}
-
-
-
-

General

+ {parsedSlotIndex && slotCommitment ? ( +
+
+
+

General

+
+ {dataRows.map((dataRow, index) => { + if (dataRow.value || dataRow.truncatedId) { + return ; + } + })}
- {dataRows.map((dataRow, index) => { - if (dataRow.value || dataRow.truncatedId) { - return ; - } - })} -
+ ) : ( + + )}
); } - -interface IDataRow { - label: string; - value?: string | number; - highlighted?: boolean; - truncatedId?: { - id: string; - link?: string; - showCopyButton?: boolean; - }; -} -const DataRow = ({ label, value, truncatedId, highlighted }: IDataRow) => { - return ( -
-
{label}
-
- {truncatedId ? ( - - ) : ( - value - )} -
-
- ); -}; diff --git a/client/src/assets/modals/nova/slot/main-header.json b/client/src/assets/modals/nova/slot/main-header.json new file mode 100644 index 000000000..73c46186a --- /dev/null +++ b/client/src/assets/modals/nova/slot/main-header.json @@ -0,0 +1,11 @@ +{ + "title": "Slot", + "description": "

Each block in IOTA 2.0 contains a commitment to the content of a certain slot in the past. A slot commitment is a hash value that encapsulates all the crucial information about a slot (such as accepted blocks and transactions, the index of the slot, etc.).

", + "links": [ + { + "label": "Read more", + "href": "https://wiki.iota.org/learn/protocols/iota2.0/core-concepts/consensus/preliminaries/#slot-commitment-chain", + "isExternal": true + } + ] +} diff --git a/client/src/services/nova/novaApiClient.ts b/client/src/services/nova/novaApiClient.ts index 45f36e91f..66aaf87f5 100644 --- a/client/src/services/nova/novaApiClient.ts +++ b/client/src/services/nova/novaApiClient.ts @@ -166,13 +166,6 @@ export class NovaApiClient extends ApiClient { return this.callApi(`nova/slot/${request.network}/${request.slotIndex}`, "get"); } - /** - * Get the slot utxo changes. - */ - public async getSlotUTXOChanges(request: ISlotRequest): Promise { - return this.callApi(`nova/slot/utxochanges/${request.network}/${request.slotIndex}`, "get"); - } - /** * Get the stats. * @param request The request to send. From 20fbd35c00c27907a883bc4f201534cc61c6a9e4 Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Wed, 21 Feb 2024 20:19:20 +0100 Subject: [PATCH 4/8] fix: lint --- api/src/models/api/nova/ISlotResponse.ts | 1 + client/src/assets/modals/stardust/block/main-header.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/api/src/models/api/nova/ISlotResponse.ts b/api/src/models/api/nova/ISlotResponse.ts index 455990095..6daaab070 100644 --- a/api/src/models/api/nova/ISlotResponse.ts +++ b/api/src/models/api/nova/ISlotResponse.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line import/no-unresolved import { SlotCommitment } from "@iota/sdk-nova"; export interface ISlotResponse { diff --git a/client/src/assets/modals/stardust/block/main-header.json b/client/src/assets/modals/stardust/block/main-header.json index efa6875f3..685afaf8d 100644 --- a/client/src/assets/modals/stardust/block/main-header.json +++ b/client/src/assets/modals/stardust/block/main-header.json @@ -8,4 +8,4 @@ "isExternal": true } ] -} +} \ No newline at end of file From 9b36f29b8fc1f6c494311b8a9b3dae2986ed90cb Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Wed, 21 Feb 2024 23:17:23 +0100 Subject: [PATCH 5/8] fix: run formatter --- .../assets/modals/nova/slot/main-header.json | 18 +++++++++--------- .../modals/stardust/block/main-header.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/client/src/assets/modals/nova/slot/main-header.json b/client/src/assets/modals/nova/slot/main-header.json index 73c46186a..c9c5f0a80 100644 --- a/client/src/assets/modals/nova/slot/main-header.json +++ b/client/src/assets/modals/nova/slot/main-header.json @@ -1,11 +1,11 @@ { - "title": "Slot", - "description": "

Each block in IOTA 2.0 contains a commitment to the content of a certain slot in the past. A slot commitment is a hash value that encapsulates all the crucial information about a slot (such as accepted blocks and transactions, the index of the slot, etc.).

", - "links": [ - { - "label": "Read more", - "href": "https://wiki.iota.org/learn/protocols/iota2.0/core-concepts/consensus/preliminaries/#slot-commitment-chain", - "isExternal": true - } - ] + "title": "Slot", + "description": "

Each block in IOTA 2.0 contains a commitment to the content of a certain slot in the past. A slot commitment is a hash value that encapsulates all the crucial information about a slot (such as accepted blocks and transactions, the index of the slot, etc.).

", + "links": [ + { + "label": "Read more", + "href": "https://wiki.iota.org/learn/protocols/iota2.0/core-concepts/consensus/preliminaries/#slot-commitment-chain", + "isExternal": true + } + ] } diff --git a/client/src/assets/modals/stardust/block/main-header.json b/client/src/assets/modals/stardust/block/main-header.json index 685afaf8d..efa6875f3 100644 --- a/client/src/assets/modals/stardust/block/main-header.json +++ b/client/src/assets/modals/stardust/block/main-header.json @@ -8,4 +8,4 @@ "isExternal": true } ] -} \ No newline at end of file +} From 8aa1a2f02a6628bf9bfbe224bbe6667030b04236 Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Mon, 26 Feb 2024 14:00:06 +0100 Subject: [PATCH 6/8] chore: improve slot page --- client/src/app/components/nova/PageDataRow.tsx | 6 +++--- client/src/app/lib/enums/slot-state.enums.ts | 5 +++++ client/src/app/routes/nova/SlotPage.tsx | 7 ++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/client/src/app/components/nova/PageDataRow.tsx b/client/src/app/components/nova/PageDataRow.tsx index 034ef456c..57658ed01 100644 --- a/client/src/app/components/nova/PageDataRow.tsx +++ b/client/src/app/components/nova/PageDataRow.tsx @@ -5,18 +5,18 @@ import TruncatedId from "../stardust/TruncatedId"; export interface IPageDataRow { label: string; value?: string | number; - highlighted?: boolean; + highlight?: boolean; truncatedId?: { id: string; link?: string; showCopyButton?: boolean; }; } -const PageDataRow = ({ label, value, truncatedId, highlighted }: IPageDataRow): React.JSX.Element => { +const PageDataRow = ({ label, value, truncatedId, highlight }: IPageDataRow): React.JSX.Element => { return (
{label}
-
+
{truncatedId ? ( ) : ( diff --git a/client/src/app/lib/enums/slot-state.enums.ts b/client/src/app/lib/enums/slot-state.enums.ts index 09d91efb5..3a190a546 100644 --- a/client/src/app/lib/enums/slot-state.enums.ts +++ b/client/src/app/lib/enums/slot-state.enums.ts @@ -4,6 +4,11 @@ export enum SlotState { */ Pending = "pending", + /** + * The slot is committed. + */ + Committed = "committed", + /** * The slot is finalized. */ diff --git a/client/src/app/routes/nova/SlotPage.tsx b/client/src/app/routes/nova/SlotPage.tsx index df9e9eb87..efb176c67 100644 --- a/client/src/app/routes/nova/SlotPage.tsx +++ b/client/src/app/routes/nova/SlotPage.tsx @@ -12,6 +12,7 @@ import "./SlotPage.scss"; const SLOT_STATE_TO_PILL_STATE: Record = { [SlotState.Pending]: PillState.Pending, + [SlotState.Committed]: PillState.Confirmed, [SlotState.Finalized]: PillState.Confirmed, }; @@ -33,7 +34,7 @@ export default function SlotPage({ { label: "Slot Index", value: slotCommitment?.slot || parsedSlotIndex, - highlighted: true, + highlight: true, }, { label: "RMC", @@ -58,13 +59,13 @@ export default function SlotPage({

Slot

- {slotCommitment && ( + {parsedSlotIndex && (
)}
- {parsedSlotIndex && slotCommitment ? ( + {parsedSlotIndex ? (
From 2e0830001b759f66298ff6358f0a205bc8739470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bego=C3=B1a=20Alvarez?= Date: Mon, 26 Feb 2024 15:40:54 +0100 Subject: [PATCH 7/8] refactor: reuse pill status component --- .../src/app/components/nova/StatusPill.scss | 4 +- client/src/app/components/nova/StatusPill.tsx | 37 ++++++++++------ .../nova/block/BlockTangleState.scss | 37 ---------------- .../nova/block/BlockTangleState.tsx | 43 ++++++++----------- .../section/TransactionMetadataSection.scss | 42 ------------------ .../section/TransactionMetadataSection.tsx | 34 ++++++--------- client/src/app/lib/ui/enums/index.ts | 2 +- .../src/app/lib/ui/enums/pill-state.enum.ts | 16 ------- .../src/app/lib/ui/enums/pill-status.enum.ts | 5 +++ client/src/app/routes/nova/SlotPage.tsx | 14 +++--- 10 files changed, 69 insertions(+), 165 deletions(-) delete mode 100644 client/src/app/components/nova/block/section/TransactionMetadataSection.scss delete mode 100644 client/src/app/lib/ui/enums/pill-state.enum.ts create mode 100644 client/src/app/lib/ui/enums/pill-status.enum.ts diff --git a/client/src/app/components/nova/StatusPill.scss b/client/src/app/components/nova/StatusPill.scss index 89bf257ff..6467cdc5b 100644 --- a/client/src/app/components/nova/StatusPill.scss +++ b/client/src/app/components/nova/StatusPill.scss @@ -24,12 +24,12 @@ } &.status__ { - &confirmed { + &success { background-color: var(--message-confirmed-bg); color: $mint-green-7; } - &conflicting { + &error { background-color: var(--message-conflicting-bg); } diff --git a/client/src/app/components/nova/StatusPill.tsx b/client/src/app/components/nova/StatusPill.tsx index f399bfcbc..289ee81f2 100644 --- a/client/src/app/components/nova/StatusPill.tsx +++ b/client/src/app/components/nova/StatusPill.tsx @@ -1,6 +1,7 @@ -import React from "react"; import classNames from "classnames"; -import { PillState } from "~/app/lib/ui/enums"; +import React from "react"; +import { PillStatus } from "~/app/lib/ui/enums"; +import Tooltip from "../Tooltip"; import "./StatusPill.scss"; interface IStatusPill { @@ -11,22 +12,30 @@ interface IStatusPill { /** * The status of the pill. */ - state: PillState; + status: PillStatus; + /** + * Tooltip explaining further for the label. + */ + tooltip?: string; } -const StatusPill: React.FC = ({ label, state }): React.JSX.Element => ( +const StatusPill: React.FC = ({ label, status, tooltip }): React.JSX.Element => ( <> - {label && ( -
+
+ {tooltip ? ( + + {status} + + ) : ( {label} -
- )} + )} +
); diff --git a/client/src/app/components/nova/block/BlockTangleState.scss b/client/src/app/components/nova/block/BlockTangleState.scss index c69f8c0ab..66652ad31 100644 --- a/client/src/app/components/nova/block/BlockTangleState.scss +++ b/client/src/app/components/nova/block/BlockTangleState.scss @@ -40,41 +40,4 @@ } } } - - .block-tangle-state { - @include font-size(12px); - - display: flex; - align-items: center; - height: 24px; - margin-right: 8px; - padding: 0 8px; - border: 0; - border-radius: 6px; - outline: none; - background-color: $gray-light; - color: $gray-midnight; - font-family: $inter; - font-weight: 500; - letter-spacing: 0.5px; - white-space: nowrap; - - @include phone-down { - height: 32px; - } - - &.block-tangle-state__confirmed { - background-color: var(--message-confirmed-bg); - color: $mint-green-7; - } - - &.block-tangle-state__conflicting { - background-color: var(--message-conflicting-bg); - } - - &.block-tangle-state__pending { - background-color: var(--light-bg); - color: #8493ad; - } - } } diff --git a/client/src/app/components/nova/block/BlockTangleState.tsx b/client/src/app/components/nova/block/BlockTangleState.tsx index e8a4619e6..1d8630c6c 100644 --- a/client/src/app/components/nova/block/BlockTangleState.tsx +++ b/client/src/app/components/nova/block/BlockTangleState.tsx @@ -1,9 +1,9 @@ -import classNames from "classnames"; import React from "react"; -import Tooltip from "../../Tooltip"; -import { BlockState, u64 } from "@iota/sdk-wasm-nova/web"; -import { BlockFailureReason, BLOCK_FAILURE_REASON_STRINGS } from "@iota/sdk-wasm-nova/web/lib/types/models/block-failure-reason"; import moment from "moment"; +import { BlockState, u64 } from "@iota/sdk-wasm-nova/web"; +import { BLOCK_FAILURE_REASON_STRINGS, BlockFailureReason } from "@iota/sdk-wasm-nova/web/lib/types/models/block-failure-reason"; +import StatusPill from "~/app/components/nova/StatusPill"; +import { PillStatus } from "~/app/lib/ui/enums"; import "./BlockTangleState.scss"; export interface BlockTangleStateProps { @@ -23,38 +23,29 @@ export interface BlockTangleStateProps { failureReason?: BlockFailureReason; } +const BLOCK_STATE_TO_PILL_STATUS: Record = { + pending: PillStatus.Pending, + accepted: PillStatus.Success, + confirmed: PillStatus.Success, + finalized: PillStatus.Success, + failed: PillStatus.Error, + rejected: PillStatus.Error, +}; + const BlockTangleState: React.FC = ({ status, issuingTime, failureReason }) => { const blockIssueMoment = moment(Number(issuingTime) / 1000000); const timeReference = blockIssueMoment.fromNow(); const longTimestamp = blockIssueMoment.format("LLLL"); + const pillStatus: PillStatus = BLOCK_STATE_TO_PILL_STATUS[status]; + const failureReasonString: string | undefined = failureReason ? BLOCK_FAILURE_REASON_STRINGS[failureReason] : undefined; + return ( <>
{status && ( -
- {failureReason ? ( - - - {status} - - - ) : ( - {status} - )} -
+
{timeReference} diff --git a/client/src/app/components/nova/block/section/TransactionMetadataSection.scss b/client/src/app/components/nova/block/section/TransactionMetadataSection.scss deleted file mode 100644 index c87daebc3..000000000 --- a/client/src/app/components/nova/block/section/TransactionMetadataSection.scss +++ /dev/null @@ -1,42 +0,0 @@ -@import "../../../../../scss/fonts"; -@import "../../../../../scss/mixins"; -@import "../../../../../scss/media-queries"; -@import "../../../../../scss/variables"; -@import "../../../../../scss/themes"; - -.transaction-tangle-state { - @include font-size(12px); - - display: flex; - align-items: center; - height: 24px; - margin-right: 8px; - padding: 0 8px; - border: 0; - border-radius: 6px; - outline: none; - background-color: $gray-light; - color: $gray-midnight; - font-family: $inter; - font-weight: 500; - letter-spacing: 0.5px; - white-space: nowrap; - - @include phone-down { - height: 32px; - } - - &.transaction-tangle-state__confirmed { - background-color: var(--message-confirmed-bg); - color: $mint-green-7; - } - - &.transaction-tangle-state__conflicting { - background-color: var(--message-conflicting-bg); - } - - &.transaction-tangle-state__pending { - background-color: var(--light-bg); - color: #8493ad; - } -} diff --git a/client/src/app/components/nova/block/section/TransactionMetadataSection.tsx b/client/src/app/components/nova/block/section/TransactionMetadataSection.tsx index eea4c3a02..79ea81e1c 100644 --- a/client/src/app/components/nova/block/section/TransactionMetadataSection.tsx +++ b/client/src/app/components/nova/block/section/TransactionMetadataSection.tsx @@ -1,11 +1,11 @@ -import classNames from "classnames"; -import { TRANSACTION_FAILURE_REASON_STRINGS, Transaction, TransactionMetadata, Utils } from "@iota/sdk-wasm-nova/web"; +import { TRANSACTION_FAILURE_REASON_STRINGS, Transaction, TransactionMetadata, TransactionState, Utils } from "@iota/sdk-wasm-nova/web"; import React from "react"; -import "./TransactionMetadataSection.scss"; import Spinner from "../../../Spinner"; import TruncatedId from "~/app/components/stardust/TruncatedId"; import ContextInputView from "../../ContextInputView"; import { useNetworkInfoNova } from "~/helpers/nova/networkInfo"; +import { PillStatus } from "~/app/lib/ui/enums"; +import StatusPill from "~/app/components/nova/StatusPill"; interface TransactionMetadataSectionProps { readonly transaction?: Transaction; @@ -13,8 +13,17 @@ interface TransactionMetadataSectionProps { readonly metadataError?: string; } +const TRANSACTION_STATE_TO_PILL_STATUS: Record = { + pending: PillStatus.Pending, + accepted: PillStatus.Success, + confirmed: PillStatus.Success, + finalized: PillStatus.Success, + failed: PillStatus.Error, +}; + const TransactionMetadataSection: React.FC = ({ transaction, transactionMetadata, metadataError }) => { const { name: network, bech32Hrp } = useNetworkInfoNova((s) => s.networkInfo); + const pillStatus: PillStatus | undefined = TRANSACTION_STATE_TO_PILL_STATUS[transactionMetadata?.transactionState ?? "pending"]; return (
@@ -28,23 +37,8 @@ const TransactionMetadataSection: React.FC = ({ <>
Transaction Status
-
-
- {transactionMetadata.transactionState} -
+
+
{transactionMetadata.transactionFailureReason && ( diff --git a/client/src/app/lib/ui/enums/index.ts b/client/src/app/lib/ui/enums/index.ts index ccfb67f8b..eeda1a054 100644 --- a/client/src/app/lib/ui/enums/index.ts +++ b/client/src/app/lib/ui/enums/index.ts @@ -1 +1 @@ -export * from "./pill-state.enum"; +export * from "./pill-status.enum"; diff --git a/client/src/app/lib/ui/enums/pill-state.enum.ts b/client/src/app/lib/ui/enums/pill-state.enum.ts deleted file mode 100644 index 8e6bf6050..000000000 --- a/client/src/app/lib/ui/enums/pill-state.enum.ts +++ /dev/null @@ -1,16 +0,0 @@ -export enum PillState { - /** - * The status is pending. - */ - Pending = "pending", - - /** - * The status is confirmed. - */ - Confirmed = "confirmed", - - /** - * The status is rejected. - */ - Rejected = "rejected", -} diff --git a/client/src/app/lib/ui/enums/pill-status.enum.ts b/client/src/app/lib/ui/enums/pill-status.enum.ts new file mode 100644 index 000000000..5a3e05e32 --- /dev/null +++ b/client/src/app/lib/ui/enums/pill-status.enum.ts @@ -0,0 +1,5 @@ +export enum PillStatus { + Pending = "pending", + Success = "success", + Error = "error", +} diff --git a/client/src/app/routes/nova/SlotPage.tsx b/client/src/app/routes/nova/SlotPage.tsx index efb176c67..5094141db 100644 --- a/client/src/app/routes/nova/SlotPage.tsx +++ b/client/src/app/routes/nova/SlotPage.tsx @@ -7,13 +7,13 @@ import mainHeaderMessage from "~assets/modals/nova/slot/main-header.json"; import NotFound from "~/app/components/NotFound"; import { SlotState } from "~/app/lib/enums"; import { RouteComponentProps } from "react-router-dom"; -import { PillState } from "~/app/lib/ui/enums"; +import { PillStatus } from "~/app/lib/ui/enums"; import "./SlotPage.scss"; -const SLOT_STATE_TO_PILL_STATE: Record = { - [SlotState.Pending]: PillState.Pending, - [SlotState.Committed]: PillState.Confirmed, - [SlotState.Finalized]: PillState.Confirmed, +const SLOT_STATE_TO_PILL_STATUS: Record = { + [SlotState.Pending]: PillStatus.Pending, + [SlotState.Committed]: PillStatus.Success, + [SlotState.Finalized]: PillStatus.Success, }; export default function SlotPage({ @@ -28,7 +28,7 @@ export default function SlotPage({ const parsedSlotIndex = parseSlotIndex(slotIndex); const slotState = slotCommitment ? SlotState.Finalized : SlotState.Pending; - const pillState: PillState = SLOT_STATE_TO_PILL_STATE[slotState]; + const pillStatus: PillStatus = SLOT_STATE_TO_PILL_STATUS[slotState]; const dataRows: IPageDataRow[] = [ { @@ -61,7 +61,7 @@ export default function SlotPage({
{parsedSlotIndex && (
- +
)}
From ba176e589a862c5603de7e5837df32c80ae1871c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bego=C3=B1a=20Alvarez?= Date: Mon, 26 Feb 2024 15:52:49 +0100 Subject: [PATCH 8/8] refactor: rename hook --- client/src/app/routes/nova/SlotPage.tsx | 4 ++-- .../nova/hooks/{useSlotCommitment.ts => useSlotDetails.ts} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename client/src/helpers/nova/hooks/{useSlotCommitment.ts => useSlotDetails.ts} (93%) diff --git a/client/src/app/routes/nova/SlotPage.tsx b/client/src/app/routes/nova/SlotPage.tsx index 5094141db..a8d6aed2e 100644 --- a/client/src/app/routes/nova/SlotPage.tsx +++ b/client/src/app/routes/nova/SlotPage.tsx @@ -1,5 +1,5 @@ import React from "react"; -import useSlotCommitment from "~/helpers/nova/hooks/useSlotCommitment"; +import useuseSlotDetails from "~/helpers/nova/hooks/useSlotDetails"; import StatusPill from "~/app/components/nova/StatusPill"; import PageDataRow, { IPageDataRow } from "~/app/components/nova/PageDataRow"; import Modal from "~/app/components/Modal"; @@ -24,7 +24,7 @@ export default function SlotPage({ network: string; slotIndex: string; }>): React.JSX.Element { - const { slotCommitment } = useSlotCommitment(network, slotIndex); + const { slotCommitment } = useuseSlotDetails(network, slotIndex); const parsedSlotIndex = parseSlotIndex(slotIndex); const slotState = slotCommitment ? SlotState.Finalized : SlotState.Pending; diff --git a/client/src/helpers/nova/hooks/useSlotCommitment.ts b/client/src/helpers/nova/hooks/useSlotDetails.ts similarity index 93% rename from client/src/helpers/nova/hooks/useSlotCommitment.ts rename to client/src/helpers/nova/hooks/useSlotDetails.ts index c3b5ee496..45da0620f 100644 --- a/client/src/helpers/nova/hooks/useSlotCommitment.ts +++ b/client/src/helpers/nova/hooks/useSlotDetails.ts @@ -6,13 +6,13 @@ import { useIsMounted } from "~/helpers/hooks/useIsMounted"; import { NOVA } from "~/models/config/protocolVersion"; import { NovaApiClient } from "~/services/nova/novaApiClient"; -interface IUseSlotData { +interface IUseSlotDetails { slotCommitment: SlotCommitment | null; error: string | undefined; isLoading: boolean; } -export default function useSlotData(network: string, slotIndex: string): IUseSlotData { +export default function useSlotDetails(network: string, slotIndex: string): IUseSlotDetails { const isMounted = useIsMounted(); const [apiClient] = useState(ServiceFactory.get(`api-client-${NOVA}`)); const [slotCommitment, setSlotCommitment] = useState(null);