diff --git a/public/locales/en.json b/public/locales/en.json index 7e433e2..e5993c2 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -238,7 +238,7 @@ "transactionsCardTitle": "Transactions", "transactionsError": "There was an error fetching your transactions. See the console for details.", - "blockValueCardTitle": "Block Value", + "blockValueCardTitle": "Block value", "blockValueBaseValue": "Base value (<1>)", "blockValueBaseValueNames": "{{count, number}} name", "blockValueBaseValueNames_plural": "{{count, number}} names", @@ -248,7 +248,7 @@ "blockValueReset_plural": "Resets in <1>{{count, number}} blocks", "blockValueEmptyDescription": "The block value increases when <1>names are purchased.", - "blockDifficultyCardTitle": "Block Difficulty", + "blockDifficultyCardTitle": "Block difficulty", "blockDifficultyError": "There was an error fetching the block difficulty. See the console for details.", "blockDifficultyHashRate": "Approx. <1 />", "blockDifficultyHashRateTooltip": "Estimated combined network mining hash rate, based on the current work.", @@ -259,7 +259,7 @@ "motdCardTitle": "Server MOTD", "motdDebugMode": "This sync node is an unofficial development server. Balances and transactions can be manipulated. Proceed with caution.", - "whatsNewCardTitle": "What's New" + "whatsNewCardTitle": "What's new" }, "credits": { @@ -284,8 +284,12 @@ "autoRefreshTables": "Auto-refresh tables", "autoRefreshTablesDescription": "Whether or not large table listings (e.g. transactions, names) should automatically refresh when a change is detected on the network.", - "subMenuDebug": "Debug settings", + "subMenuAdvanced": "Advanced settings", + "copyNameSuffixes": "Include suffix when copying names", + "addressCopyButtons": "Show copy buttons next to all addresses", + "nameCopyButtons": "Show copy buttons next to all names", + "subMenuDebug": "Debug settings", "advancedWalletFormats": "Advanced wallet formats", "menuTranslations": "Translations", @@ -441,5 +445,34 @@ "resultInvalid": "That does not look like a valid Krist address.", "resultUnknownTitle": "Unknown error", "resultUnknown": "See console for details." + }, + + "name": { + "title": "Name", + + "buttonSendKrist": "Send Krist to {{name}}", + "buttonTransferKrist": "Transfer Krist to {{name}}", + "buttonARecord": "Update A record", + "buttonTransferName": "Transfer name", + + "owner": "Owned by", + "originalOwner": "Purchased by", + "registered": "Registered", + "updated": "Last updated", + "unpaid": "Unpaid blocks", + "unpaidCount": "{{count, number}} block", + "unpaidCount_plural": "{{count, number}} blocks", + + "cardRecentTransactionsTitle": "Recent transactions", + "cardHistoryTitle": "Name history", + "transactionsError": "There was an error fetching the transactions. See the console for details.", + "historyError": "There was an error fetching the name history. See the console for details.", + + "resultInvalidTitle": "Invalid name", + "resultInvalid": "That does not look like a valid Krist name.", + "resultNotFoundTitle": "Name not found", + "resultNotFound": "That name does not exist.", + "resultUnknownTitle": "Unknown error", + "resultUnknown": "See console for details." } } diff --git a/src/components/ContextualAddress.tsx b/src/components/ContextualAddress.tsx index 33cd274..51f6a26 100644 --- a/src/components/ContextualAddress.tsx +++ b/src/components/ContextualAddress.tsx @@ -3,7 +3,7 @@ // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import React from "react"; import classNames from "classnames"; -import { Tooltip } from "antd"; +import { Tooltip, Typography } from "antd"; import { useSelector } from "react-redux"; import { RootState } from "../store"; @@ -14,11 +14,14 @@ import { Wallet, useWallets } from "../krist/wallets/Wallet"; import { parseCommonMeta, CommonMeta } from "../utils/commonmeta"; import { stripNameSuffix } from "../utils/currency"; +import { useBooleanSetting } from "../utils/settings"; import { KristNameLink } from "./KristNameLink"; import "./ContextualAddress.less"; +const { Text } = Typography; + interface Props { address: KristAddress | string | null; wallet?: Wallet | false; @@ -26,6 +29,7 @@ source?: boolean; hideNameAddress?: boolean; allowWrap?: boolean; + neverCopyable?: boolean; className?: string; } @@ -73,11 +77,13 @@ source, hideNameAddress, allowWrap, + neverCopyable, className }: Props): JSX.Element { const { t } = useTranslation(); const { walletAddressMap } = useWallets(); const nameSuffix = useSelector((s: RootState) => s.node.currency.name_suffix); + const addressCopyButtons = useBooleanSetting("addressCopyButtons"); if (!origAddress) return ( {t("contextualAddressUnknown")} @@ -94,30 +100,35 @@ const commonMeta = parseCommonMeta(nameSuffix, metadata); const hasMetaname = source ? !!commonMeta?.returnRecipient : !!commonMeta?.recipient; + const copyable = !neverCopyable && addressCopyButtons + ? { text: address } : undefined; + const classes = classNames("contextual-address", className, { "contextual-address-allow-wrap": allowWrap }); - return - {commonMeta && hasMetaname - ? ( - // Display the metaname and link to the name if possible - - ) - : ( - // Display our wallet label if present, but link to the address - - {wallet && wallet.label - ? {wallet.label} - : {address}} - - ) - } - ; + return + + {commonMeta && hasMetaname + ? ( + // Display the metaname and link to the name if possible + + ) + : ( + // Display our wallet label if present, but link to the address + + {wallet && wallet.label + ? {wallet.label} + : {address}} + + ) + } + + ; } diff --git a/src/components/KristNameLink.tsx b/src/components/KristNameLink.tsx index 1227981..f441e35 100644 --- a/src/components/KristNameLink.tsx +++ b/src/components/KristNameLink.tsx @@ -3,29 +3,45 @@ // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import React from "react"; import classNames from "classnames"; +import { Typography } from "antd"; import { useSelector } from "react-redux"; import { RootState } from "../store"; import { Link } from "react-router-dom"; +import { useBooleanSetting } from "../utils/settings"; + +const { Text } = Typography; + interface OwnProps { name: string; noLink?: boolean; + neverCopyable?: boolean; } type Props = React.HTMLProps & OwnProps; -export function KristNameLink({ name, noLink, ...props }: Props): JSX.Element | null { +export function KristNameLink({ name, noLink, neverCopyable, ...props }: Props): JSX.Element | null { const nameSuffix = useSelector((s: RootState) => s.node.currency.name_suffix); + const nameCopyButtons = useBooleanSetting("nameCopyButtons"); + const copyNameSuffixes = useBooleanSetting("copyNameSuffixes"); if (!name) return null; + const nameWithSuffix = `${name}.${nameSuffix}`; - const contents = `${name}.${nameSuffix}`; + const copyable = !neverCopyable && nameCopyButtons + ? { text: copyNameSuffixes ? nameWithSuffix : name } + : undefined; + const classes = classNames("krist-name", props.className); - return + return {noLink - ? contents - : {contents}} - ; + ? nameWithSuffix + : ( + + {nameWithSuffix} + + )} + ; } diff --git a/src/components/Statistic.less b/src/components/Statistic.less index d34cbff..fda735a 100644 --- a/src/components/Statistic.less +++ b/src/components/Statistic.less @@ -11,5 +11,19 @@ &-value { font-size: @heading-3-size; + + .ant-typography-copy { + line-height: 1 !important; + margin-left: @padding-xs; + + .anticon { + font-size: @font-size-base; + vertical-align: 0; + } + } + } + + &-green &-value { + color: @kw-green; } } diff --git a/src/components/Statistic.tsx b/src/components/Statistic.tsx index 8596ebb..1a25fbd 100644 --- a/src/components/Statistic.tsx +++ b/src/components/Statistic.tsx @@ -2,6 +2,7 @@ // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import React from "react"; +import classNames from "classnames"; import { useTranslation } from "react-i18next"; @@ -11,12 +12,19 @@ title?: string; titleKey?: string; value?: React.ReactNode; + + className?: string; + green?: boolean; } -export function Statistic({ title, titleKey, value }: Props): JSX.Element { +export function Statistic({ title, titleKey, value, className, green }: Props): JSX.Element { const { t } = useTranslation(); - return
+ const classes = classNames("kw-statistic", className, { + "kw-statistic-green": green + }); + + return
{titleKey ? t(titleKey) : title} {value}
; diff --git a/src/components/transactions/TransactionItem.tsx b/src/components/transactions/TransactionItem.tsx index c8a7918..590acad 100644 --- a/src/components/transactions/TransactionItem.tsx +++ b/src/components/transactions/TransactionItem.tsx @@ -133,7 +133,11 @@ ) : tx.type === "name_transfer" && ( // Transaction name - + )} ; diff --git a/src/components/wallets/SelectWalletFormat.tsx b/src/components/wallets/SelectWalletFormat.tsx index d16f7f4..269fcc7 100644 --- a/src/components/wallets/SelectWalletFormat.tsx +++ b/src/components/wallets/SelectWalletFormat.tsx @@ -4,19 +4,17 @@ import React from "react"; import { Select } from "antd"; -import { useSelector } from "react-redux"; -import { RootState } from "../../store"; - import { useTranslation } from "react-i18next"; import { WalletFormatName, ADVANCED_FORMATS } from "../../krist/wallets/formats/WalletFormat"; +import { useBooleanSetting } from "../../utils/settings"; interface Props { initialFormat: WalletFormatName; } export function SelectWalletFormat({ initialFormat }: Props): JSX.Element { - const advancedWalletFormats = useSelector((s: RootState) => s.settings.walletFormats); + const advancedWalletFormats = useBooleanSetting("walletFormats"); const { t } = useTranslation(); return