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: add slot page #1163

Merged
merged 11 commits into from
Feb 26, 2024
Merged
11 changes: 11 additions & 0 deletions api/src/models/api/nova/ISlotRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface ISlotRequest {
/**
* The network to search on.
*/
network: string;

/**
* The slot index to get the details for.
*/
slotIndex: string;
}
10 changes: 10 additions & 0 deletions api/src/models/api/nova/ISlotResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// eslint-disable-next-line import/no-unresolved
import { SlotCommitment } from "@iota/sdk-nova";
import { IResponse } from "./IResponse";

export interface ISlotResponse extends IResponse {
/**
* The deserialized slot.
*/
slot?: SlotCommitment;
}
1 change: 1 addition & 0 deletions api/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,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" },
];
30 changes: 30 additions & 0 deletions api/src/routes/nova/slot/get.ts
Original file line number Diff line number Diff line change
@@ -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<ISlotResponse> {
const networkService = ServiceFactory.get<NetworkService>("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<NovaApiService>(`api-service-${networkConfig.network}`);
return novaApiService.getSlotCommitment(Number(request.slotIndex));
}
16 changes: 16 additions & 0 deletions api/src/services/nova/novaApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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 { ITransactionDetailsResponse } from "../../models/api/nova/ITransactionDetailsResponse";
import { INetwork } from "../../models/db/INetwork";
import { HexHelper } from "../../utils/hexHelper";
Expand Down Expand Up @@ -306,6 +307,21 @@ 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<ISlotResponse> {
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.
Expand Down
30 changes: 30 additions & 0 deletions client/src/app/components/nova/PageDataRow.tsx
Original file line number Diff line number Diff line change
@@ -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;
highlight?: boolean;
truncatedId?: {
id: string;
link?: string;
showCopyButton?: boolean;
};
}
const PageDataRow = ({ label, value, truncatedId, highlight }: IPageDataRow): React.JSX.Element => {
return (
<div className="section--data">
<div className="label">{label}</div>
<div className={classNames("value code", { highlight })}>
{truncatedId ? (
<TruncatedId id={truncatedId.id} link={truncatedId.link} showCopyButton={truncatedId.showCopyButton} />
) : (
value
)}
</div>
</div>
);
};

export default PageDataRow;
41 changes: 41 additions & 0 deletions client/src/app/components/nova/StatusPill.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@import "./../../../scss/fonts";
@import "./../../../scss/mixins";
@import "./../../../scss/media-queries";
@import "./../../../scss/variables";

.status-pill {
@include font-size(12px);

display: flex;
align-items: center;
margin-right: 8px;
padding: 6px 12px;
border: 0;
border-radius: 6px;
outline: none;
color: $gray-midnight;
font-family: $inter;
font-weight: 500;
letter-spacing: 0.5px;
white-space: nowrap;

@include phone-down {
height: 32px;
}

&.status__ {
&success {
background-color: var(--message-confirmed-bg);
color: $mint-green-7;
}

&error {
background-color: var(--message-conflicting-bg);
}

&pending {
background-color: var(--light-bg);
color: #8493ad;
}
}
}
42 changes: 42 additions & 0 deletions client/src/app/components/nova/StatusPill.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import classNames from "classnames";
import React from "react";
import { PillStatus } from "~/app/lib/ui/enums";
import Tooltip from "../Tooltip";
import "./StatusPill.scss";

interface IStatusPill {
/**
* Label for the status.
*/
label: string;
/**
* The status of the pill.
*/
status: PillStatus;
/**
* Tooltip explaining further for the label.
*/
tooltip?: string;
}

const StatusPill: React.FC<IStatusPill> = ({ label, status, tooltip }): React.JSX.Element => (
<>
<div
className={classNames("status-pill", {
status__success: status === PillStatus.Success,
status__error: status === PillStatus.Error,
status__pending: status === PillStatus.Pending,
})}
>
{tooltip ? (
<Tooltip tooltipContent={tooltip}>
<span className="capitalize-text">{status}</span>
</Tooltip>
) : (
<span className="capitalize-text">{label}</span>
)}
</div>
</>
);

export default StatusPill;
37 changes: 0 additions & 37 deletions client/src/app/components/nova/block/BlockTangleState.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
43 changes: 17 additions & 26 deletions client/src/app/components/nova/block/BlockTangleState.tsx
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -23,38 +23,29 @@ export interface BlockTangleStateProps {
failureReason?: BlockFailureReason;
}

const BLOCK_STATE_TO_PILL_STATUS: Record<BlockState, PillStatus> = {
pending: PillStatus.Pending,
accepted: PillStatus.Success,
confirmed: PillStatus.Success,
finalized: PillStatus.Success,
failed: PillStatus.Error,
rejected: PillStatus.Error,
};

const BlockTangleState: React.FC<BlockTangleStateProps> = ({ 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 (
<>
<div className="blocks-tangle-state">
{status && (
<React.Fragment>
<div
className={classNames(
"block-tangle-state",
{
"block-tangle-state__confirmed": status === "confirmed" || "finalized",
},
{
"block-tangle-state__conflicting": status === "rejected" && "failed",
},
{ "block-tangle-state__pending": status === "pending" },
)}
>
{failureReason ? (
<Tooltip tooltipContent={BLOCK_FAILURE_REASON_STRINGS[failureReason]}>
<span className="capitalize-text" style={{ color: "#ca493d" }}>
{status}
</span>
</Tooltip>
) : (
<span className="capitalize-text">{status}</span>
)}
</div>
<StatusPill status={pillStatus} label={status} tooltip={failureReasonString} />
<div className="block-tangle-reference">
<span title={longTimestamp} className="time-reference">
{timeReference}
Expand Down

This file was deleted.

Loading
Loading