// Copyright (c) 2020-2021 Drew Lemmy // This file is part of TenebraWeb 2 under AGPL-3.0. // Full details: https://github.com/tmpim/TenebraWeb2/blob/master/LICENSE.txt import { useState, useMemo } from "react"; import { Card, Table, TableProps, Typography } from "antd"; import { useTranslation } from "react-i18next"; import { parseCommonMeta, useNameSuffix } from "@utils/tenebra"; import { HelpIcon } from "@comp/HelpIcon"; import { useBooleanSetting } from "@utils/settings"; const { Text, Title } = Typography; // TODO: This is definitely too crude for my taste, but I had no better ideas const HAS_COMMONMETA = /[=;]/; interface CommonMetaTableProps { metadata: string; nameSuffix: string; } export function CommonMetaTable({ metadata, nameSuffix }: CommonMetaTableProps): JSX.Element { const { t } = useTranslation(); // Parse the CommonMeta from the transaction, showing an error if it fails // (which shouldn't really happen) const parsed = parseCommonMeta(nameSuffix, metadata); if (!parsed) return <Text type="danger">{t("transaction.commonMetaError")}</Text>; // Convert the CommonMeta objects to an array of entries {key, value} const processedCustom = Object.entries(parsed.custom) .map(([key, value]) => ({ key, value })); const processedParsed = Object.entries(parsed) .map(([key, value]) => ({ key, value })) .filter(o => o.key !== "custom"); // Hide the 'custom' object // Both tables display the same columns const columns = [ // Key { title: t("transaction.commonMetaColumnKey"), dataIndex: "key", key: "key" }, // Value { title: t("transaction.commonMetaColumnValue"), dataIndex: "value", key: "value", className: "transaction-metadata-cell-value" } ]; // Props common to both tables const tableProps: TableProps<{ key: string; value: string }> = { size: "small", rowKey: "key", columns, pagination: false, scroll: { y: 160 } // Give it a fixed height }; return <> {/* Custom data table */} <Title level={5}> {t("transaction.commonMetaCustom")} <HelpIcon textKey="transaction.commonMetaCustomHelp" /> </Title> <Table className="commonmeta-custom-table" dataSource={processedCustom} {...tableProps} /> {/* Parsed data table */} <Title level={5} style={{ marginTop: 24 }}> {t("transaction.commonMetaParsed")} <HelpIcon textKey="transaction.commonMetaParsedHelp" /> </Title> <Table className="commonmeta-parsed-table" dataSource={processedParsed} {...tableProps} /> </>; } export function TransactionMetadataCard({ metadata }: { metadata: string }): JSX.Element { const { t } = useTranslation(); const nameSuffix = useNameSuffix(); // Default to the 'Raw' tab instead of 'CommonMeta' const defaultRaw = useBooleanSetting("transactionDefaultRaw"); // Estimate in advance if a CommonMeta tab should be showed const hasCommonMeta = HAS_COMMONMETA.test(metadata); const [activeTab, setActiveTab] = useState<"commonMeta" | "raw">( hasCommonMeta && !defaultRaw ? "commonMeta" : "raw" ); // Tab list for the card const commonMetaTab = { key: "commonMeta", tab: t("transaction.tabCommonMeta") }; const rawTab = { key: "raw", tab: t("transaction.tabRaw") }; // Parsing the CommonMeta and rendering the table is a little too expensive // for my tastes, so it's memoised here. const commonMetaTable = useMemo(() => (hasCommonMeta ? <CommonMetaTable metadata={metadata} nameSuffix={nameSuffix} /> : null), [hasCommonMeta, metadata, nameSuffix]); return <Card className="kw-card transaction-card-metadata" title={t("transaction.cardMetadataTitle")} tabList={hasCommonMeta ? [commonMetaTab, rawTab] : [rawTab]} activeTabKey={activeTab} onTabChange={key => setActiveTab(key as "commonMeta" | "raw")} > {hasCommonMeta && activeTab === "commonMeta" ? ( // Parsed CommonMeta table commonMetaTable ) : ( // Raw metadata <div className="transaction-metadata-raw"> {metadata} </div> )} </Card>; }