Newer
Older
CrypticOreWallet / src / pages / transactions / TransactionMetadataCard.tsx
@Drew Lemmy Drew Lemmy on 12 Mar 2021 4 KB feat: address picker hints
// Copyright (c) 2020-2021 Drew Lemmy
// This file is part of KristWeb 2 under GPL-3.0.
// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt
import { useState, useMemo } from "react";
import { Card, Table, TableProps, Typography } from "antd";

import { useTranslation } from "react-i18next";

import { parseCommonMeta } from "@utils/commonmeta";
import { useNameSuffix } from "@utils/currency";

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>;
}