From 2ea080ed4fdc6e0fd85a360577ce410fa4b725c0 Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 15 Jan 2024 22:36:33 +0900 Subject: [PATCH 01/67] fix: clear form when the dialog is dismissed --- src/components/SubmitTokenInfo/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SubmitTokenInfo/index.tsx b/src/components/SubmitTokenInfo/index.tsx index 4535ef13b..d75afdc0c 100644 --- a/src/components/SubmitTokenInfo/index.tsx +++ b/src/components/SubmitTokenInfo/index.tsx @@ -95,7 +95,7 @@ export const SubmitTokenInfo = ({ onClose, isOpen }: Props) => { } const handleClose = () => { - setIsDirty(false) + clearForm() onClose() } From 95d4d2bfe15c3ffe387ce8770510c2f8a93b04d8 Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 15 Jan 2024 23:11:28 +0900 Subject: [PATCH 02/67] fix: fix selection in token info dialog --- src/components/CommonSelect/index.tsx | 19 +++++++----------- src/components/SubmitTokenInfo/index.tsx | 25 ++++++++++++------------ src/constants/scripts.ts | 3 ++- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/components/CommonSelect/index.tsx b/src/components/CommonSelect/index.tsx index cc5131038..1cf40c0d8 100644 --- a/src/components/CommonSelect/index.tsx +++ b/src/components/CommonSelect/index.tsx @@ -5,21 +5,16 @@ import OutsideClickHandler from 'react-outside-click-handler' import styles from './index.module.scss' import { isMainnet } from '../../utils/chain' -type OptionType = { - value: T - label: string -} - -type Props = { - options: OptionType[] - onChange: (value: T) => void - defaultValue?: T +type Option = Record<'label' | 'value', string> +type Props = { + options: Option[] + onChange: (value: string) => void + defaultValue?: string placeholder?: string className?: string } -function CommonSelect(props: Props) { - const { options, onChange, defaultValue, placeholder, className } = props +function CommonSelect({ options, onChange, defaultValue, placeholder, className }: Props) { const defaultLabel = options.find(option => option.value === defaultValue)?.label const [value, setValue] = useState(defaultLabel) const [isExpanded, setIsExpanded] = useState(false) @@ -27,7 +22,7 @@ function CommonSelect(props: Props) { setIsExpanded(!isExpanded) } - const handleOptionClick = (option: OptionType) => { + const handleOptionClick = (option: Option) => { onChange(option.value) setValue(option.label) toggleExpand() diff --git a/src/components/SubmitTokenInfo/index.tsx b/src/components/SubmitTokenInfo/index.tsx index d75afdc0c..5bfc9265b 100644 --- a/src/components/SubmitTokenInfo/index.tsx +++ b/src/components/SubmitTokenInfo/index.tsx @@ -13,10 +13,10 @@ import CommonModal from '../CommonModal' import { submitTokenInfo } from '../../services/ExplorerService/fetcher' import CommonSelect from '../CommonSelect' import { isMainnet } from '../../utils/chain' +import { scripts } from '../../pages/ScriptList' import { MainnetContractHashTags, TestnetContractHashTags } from '../../constants/scripts' import { isValidNoNegativeInteger } from '../../utils/number' import { useSetToast } from '../Toast' -import { assertIsHashType } from '../../utils/util' type Props = { isOpen: boolean @@ -48,15 +48,11 @@ export const SubmitTokenInfo = ({ onClose, isOpen }: Props) => { const tokenTypeOptions = scriptDataList .filter(scriptData => scriptData.tag.includes('sudt')) .sort((a, b) => a.tag.localeCompare(b.tag)) - .map(scriptData => ({ - value: { - codeHash: scriptData.codeHashes[0], - hashType: scriptData.hashType, - }, - label: scriptData.tag, - })) - const [tokenType, setTokenType] = useState<{ codeHash: string; hashType: string }>(tokenTypeOptions[0].value) - const handleTokenTypesChange = (value: { codeHash: string; hashType: string }) => { + .map(scriptData => ({ label: scripts.get(scriptData.tag)?.name ?? '-', value: scriptData.tag })) + + const [tokenType, setTokenType] = useState(tokenTypeOptions[0].value) + + const handleTokenTypesChange = (value: string) => { setTokenType(value) } @@ -126,10 +122,13 @@ export const SubmitTokenInfo = ({ onClose, isOpen }: Props) => { } setSubmitting(true) - assertIsHashType(tokenType.hashType) + const token = scriptDataList.find(scriptData => scriptData.tag === tokenType) + if (!token) { + throw new Error(`tokenType ${tokenType} is not found`) + } const typeHash = utils.computeScriptHash({ - codeHash: tokenType.codeHash, - hashType: tokenType.hashType, + codeHash: token.codeHashes[0], + hashType: token.hashType, args, }) diff --git a/src/constants/scripts.ts b/src/constants/scripts.ts index 9027e4322..b7895c9b7 100644 --- a/src/constants/scripts.ts +++ b/src/constants/scripts.ts @@ -1,3 +1,4 @@ +import { type HashType } from '@ckb-lumos/base' import { Script } from '../models/Script' export interface ContractHashTag { @@ -6,7 +7,7 @@ export interface ContractHashTag { tag: string category: 'lock' | 'type' depType: 'dep_group' | 'code' - hashType: string + hashType: HashType } export const ScriptTagExtraRules = new Map string>([ From 085fb1d476657888c1dc4f751aa100a28d6cc039 Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 15 Jan 2024 23:15:55 +0900 Subject: [PATCH 03/67] fix: fix error status of fields --- src/components/SubmitTokenInfo/index.tsx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/components/SubmitTokenInfo/index.tsx b/src/components/SubmitTokenInfo/index.tsx index 5bfc9265b..53c56ba99 100644 --- a/src/components/SubmitTokenInfo/index.tsx +++ b/src/components/SubmitTokenInfo/index.tsx @@ -32,7 +32,6 @@ const LabelTooltip = ({ title, icon }: { title: string; icon?: string }) => ( export const SubmitTokenInfo = ({ onClose, isOpen }: Props) => { const { t } = useTranslation() const setToast = useSetToast() - const [isDirty, setIsDirty] = useState(false) const [submitting, setSubmitting] = useState(false) const [args, setArgs] = useState('') @@ -78,7 +77,6 @@ export const SubmitTokenInfo = ({ onClose, isOpen }: Props) => { } const clearForm = () => { - setIsDirty(false) onClose() setArgs('') setSymbol('') @@ -116,7 +114,6 @@ export const SubmitTokenInfo = ({ onClose, isOpen }: Props) => { const validateFields = () => validateBasicFields() && isInputRulesValid const handleConfirm = async () => { - setIsDirty(true) if (!validateFields()) { return } @@ -181,7 +178,7 @@ export const SubmitTokenInfo = ({ onClose, isOpen }: Props) => { } @@ -210,7 +207,7 @@ export const SubmitTokenInfo = ({ onClose, isOpen }: Props) => { /> } @@ -228,7 +225,7 @@ export const SubmitTokenInfo = ({ onClose, isOpen }: Props) => { /> } @@ -238,7 +235,7 @@ export const SubmitTokenInfo = ({ onClose, isOpen }: Props) => { /> } From 98548f22cc2bb479ac9a11290e6d05f39b68c540 Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 15 Jan 2024 23:29:52 +0900 Subject: [PATCH 04/67] feat: adopt token form in footer and update token email template 1. show the token form on submitting token from footer 2. remove sudt section from the email template of token info submission --- src/components/Footer/index.module.scss | 2 +- src/components/Footer/index.tsx | 19 +++++++++++++------ src/components/SubmitTokenInfo/index.tsx | 2 +- src/constants/common.ts | 19 ------------------- 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/components/Footer/index.module.scss b/src/components/Footer/index.module.scss index ba1c0a18b..b61985475 100644 --- a/src/components/Footer/index.module.scss +++ b/src/components/Footer/index.module.scss @@ -1,4 +1,4 @@ -.footerSubmitToken { +.tokenFormBtn { font-size: 18px; color: #acacac; height: 23px; diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx index 3b913fee1..8827910c7 100644 --- a/src/components/Footer/index.tsx +++ b/src/components/Footer/index.tsx @@ -1,5 +1,6 @@ -import { ReactNode, memo, useMemo } from 'react' +import { ReactNode, memo, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' +import { SubmitTokenInfo } from '../SubmitTokenInfo' import { ReactComponent as XIcon } from './footer_X.svg' import { ReactComponent as MediumIcon } from './footer_medium.svg' import { ReactComponent as TelegramIcon } from './footer_telegram.svg' @@ -9,7 +10,7 @@ import { ReactComponent as ForumIcon } from './footer_forum.svg' import { ReactComponent as Discord } from './footer_discord.svg' import { getCurrentYear } from '../../utils/date' import { FooterMenuPanel, FooterItemPanel, FooterImageItemPanel, FooterPanel } from './styled' -import { udtSubmitEmail } from '../../utils/util' +import styles from './index.module.scss' interface FooterLinkItem { label?: string @@ -44,6 +45,7 @@ const FooterImageItem = ({ item }: { item: FooterLinkItem }) => { export default memo(() => { const [t] = useTranslation() + const [isTokenFormDisplayed, setIsTokenFormDisplayed] = useState(false) const Footers = useMemo( () => [ { @@ -74,10 +76,6 @@ export default memo(() => { label: t('footer.faucet'), url: 'https://faucet.nervos.org/', }, - { - label: t('udt.submit_token_info'), - url: udtSubmitEmail(), - }, ], }, { @@ -123,6 +121,11 @@ export default memo(() => { ], [t], ) + + const onSubmitToken = () => { + setIsTokenFormDisplayed(true) + } + return ( @@ -139,6 +142,9 @@ export default memo(() => { .map(item => ( ))} +
{Footers[2].items.map(item => ( @@ -150,6 +156,7 @@ export default memo(() => { {`Copyright © ${getCurrentYear()} Nervos Foundation. `} All Rights Reserved.
+ setIsTokenFormDisplayed(false)} />
) }) diff --git a/src/components/SubmitTokenInfo/index.tsx b/src/components/SubmitTokenInfo/index.tsx index 53c56ba99..cd4319bde 100644 --- a/src/components/SubmitTokenInfo/index.tsx +++ b/src/components/SubmitTokenInfo/index.tsx @@ -47,7 +47,7 @@ export const SubmitTokenInfo = ({ onClose, isOpen }: Props) => { const tokenTypeOptions = scriptDataList .filter(scriptData => scriptData.tag.includes('sudt')) .sort((a, b) => a.tag.localeCompare(b.tag)) - .map(scriptData => ({ label: scripts.get(scriptData.tag)?.name ?? '-', value: scriptData.tag })) + .map(scriptData => ({ label: scripts.get(scriptData.tag)?.name ?? scriptData.tag, value: scriptData.tag })) const [tokenType, setTokenType] = useState(tokenTypeOptions[0].value) diff --git a/src/constants/common.ts b/src/constants/common.ts index e6b5f6100..b93aac503 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -32,25 +32,6 @@ export const TOKEN_EMAIL_ADDRESS = 'ckb-explorer@nervosnet.com' export const TOKEN_EMAIL_SUBJECT = 'Submit Token Info' export const TOKEN_EMAIL_BODY = ` Title: Submit Token Information%0a%0d ----------- Submit sUDT Token Information ----------%0a%0d - -Type Script:%0a%0d - Code Hash:%0a%0d - Hash Type:%0a%0d - Args:%0a%0d - -Information:%0a%0d - Display Name:%0a%0d - UAN:%0a%0d - Decimal: 8 (default)%0a%0d - Description:%0a%0d - Website:%0a%0d - Icon File: attachment (40 x 40)%0a%0d - Other Info:%0a%0d%0a%0d - -Ref:%0a%0d -1. UAN: https://github.com/nervosnetwork/rfcs/pull/335%0a%0d - ---------- Submit NRC 721 Factory Information ----------%0a%0d Information:%0a%0d From 781c3d43311901db6d9c655eb461c9f574a96d1b Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 16 Jan 2024 00:42:58 +0900 Subject: [PATCH 05/67] feat: update the arrow of common selection component --- src/assets/arrow_down_black.png | Bin 0 -> 10838 bytes src/components/CommonSelect/index.module.scss | 38 +++--------------- src/components/CommonSelect/index.tsx | 4 +- 3 files changed, 7 insertions(+), 35 deletions(-) create mode 100644 src/assets/arrow_down_black.png diff --git a/src/assets/arrow_down_black.png b/src/assets/arrow_down_black.png new file mode 100644 index 0000000000000000000000000000000000000000..40a0263dc5770befd2d3e6a6abf43c012a2c2db5 GIT binary patch literal 10838 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 literal 0 HcmV?d00001 diff --git a/src/components/CommonSelect/index.module.scss b/src/components/CommonSelect/index.module.scss index cbd15c4ae..e488acfd3 100644 --- a/src/components/CommonSelect/index.module.scss +++ b/src/components/CommonSelect/index.module.scss @@ -17,11 +17,6 @@ align-items: center; width: 100%; height: 100%; - - img { - width: 16px; - height: 16px; - } } .options { @@ -48,38 +43,15 @@ } } -.arrow, -.arrowTestnet { - width: 16px; - height: 16px; - background-size: cover; - background-position: center; - background-repeat: no-repeat; -} - -.arrowTestnet { - background-image: url('../../assets/arrow_down_blue.png'); -} - .arrow { - background-image: url('../../assets/arrow_down.png'); -} + width: 10px; + transition: 0.3s; -.flip { - transform: rotate(180deg); - animation: rotate-flip 0.3s ease; + &[data-is-flipped='true'] { + transform: rotateX(180deg); + } } .selected { background: #f5f5f5; } - -@keyframes rotate-flip { - 0% { - transform: rotate(0); - } - - 100% { - transform: rotate(-180deg); - } -} diff --git a/src/components/CommonSelect/index.tsx b/src/components/CommonSelect/index.tsx index 1cf40c0d8..2ae28256a 100644 --- a/src/components/CommonSelect/index.tsx +++ b/src/components/CommonSelect/index.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames' import { useEffect, useState } from 'react' import OutsideClickHandler from 'react-outside-click-handler' import styles from './index.module.scss' -import { isMainnet } from '../../utils/chain' +import Arrow from '../../assets/arrow_down_black.png' type Option = Record<'label' | 'value', string> type Props = { @@ -77,7 +77,7 @@ function CommonSelect({ options, onChange, defaultValue, placeholder, className
{value ?? placeholder} -
+ arrow
{isExpanded && (
From 87090e7a3a5de03fcb5894bced76f43b34bdcb76 Mon Sep 17 00:00:00 2001 From: WhiteMind Date: Tue, 16 Jan 2024 10:54:55 +0800 Subject: [PATCH 06/67] feat: implement inscription detail and list page (#196) * refactor: SimpleUDT -> UDT * feat: implement inscription detail page * feat: implement inscription list page * feat: implement two different tokens list entries in the header * feat: temporarily disable the export function on the Inscription details page * fix: inscription links --- src/components/Header/MenusComp/arrow.svg | 3 + .../Header/MenusComp/index.module.scss | 44 ++++++ src/components/Header/MenusComp/index.tsx | 83 ++++++++++-- src/components/Search/SearchByNameResults.tsx | 7 +- src/components/Search/index.tsx | 2 +- src/locales/en.json | 12 +- src/locales/zh.json | 12 +- src/models/UDT/index.ts | 19 +++ src/pages/Tokens/index.tsx | 85 +++++++++--- src/pages/Tokens/styled.tsx | 23 ++++ src/pages/Tokens/styles.module.scss | 39 ++++++ .../SimpleUDTComp.tsx => UDT/UDTComp.tsx} | 128 ++++++++++++------ src/pages/{SimpleUDT => UDT}/index.tsx | 33 +++-- src/pages/{SimpleUDT => UDT}/state.ts | 11 +- src/pages/{SimpleUDT => UDT}/styled.tsx | 10 +- .../{SimpleUDT => UDT}/styles.module.scss | 6 +- src/routes/index.tsx | 16 ++- src/services/ExplorerService/fetcher.ts | 19 ++- 18 files changed, 447 insertions(+), 105 deletions(-) create mode 100644 src/components/Header/MenusComp/arrow.svg create mode 100644 src/components/Header/MenusComp/index.module.scss rename src/pages/{SimpleUDT/SimpleUDTComp.tsx => UDT/UDTComp.tsx} (61%) rename src/pages/{SimpleUDT => UDT}/index.tsx (82%) rename src/pages/{SimpleUDT => UDT}/state.ts (53%) rename src/pages/{SimpleUDT => UDT}/styled.tsx (91%) rename src/pages/{SimpleUDT => UDT}/styles.module.scss (97%) diff --git a/src/components/Header/MenusComp/arrow.svg b/src/components/Header/MenusComp/arrow.svg new file mode 100644 index 000000000..c96928be1 --- /dev/null +++ b/src/components/Header/MenusComp/arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Header/MenusComp/index.module.scss b/src/components/Header/MenusComp/index.module.scss new file mode 100644 index 000000000..17d666e00 --- /dev/null +++ b/src/components/Header/MenusComp/index.module.scss @@ -0,0 +1,44 @@ +.submenu { + padding: 4px 12px; + border-radius: 4px; + background: #fff; + color: #333; + box-shadow: 0 4px 4px 0 rgb(16 16 16 / 5%); + + .link { + display: block; + padding: 12px 0; + border-top: 1px solid #e5e5e5; + line-height: 1; + + &:first-child { + border-top: 0; + } + } +} + +.submenuTrigger { + gap: 4px; + + .icon { + transform: rotate(180deg); + transition: transform 0.1s; + } + + /* stylelint-disable-next-line selector-class-pattern */ + &:global(.ant-dropdown-open) { + .icon { + transform: rotate(0deg); + } + } +} + +.mobileSubmenuTrigger { + width: 100%; + justify-content: space-between; + + .icon { + transform: rotate(180deg); + transition: transform 0.1s; + } +} diff --git a/src/components/Header/MenusComp/index.tsx b/src/components/Header/MenusComp/index.tsx index e42f25242..b6348062a 100644 --- a/src/components/Header/MenusComp/index.tsx +++ b/src/components/Header/MenusComp/index.tsx @@ -1,8 +1,12 @@ import { Link } from 'react-router-dom' -import { memo } from 'react' +import { FC, memo } from 'react' import { useTranslation } from 'react-i18next' +import { Dropdown } from 'antd' +import classNames from 'classnames' import { MobileMenuItem, MobileMenuLink, HeaderMenuPanel } from './styled' import { isMainnet } from '../../../utils/chain' +import styles from './index.module.scss' +import { ReactComponent as ArrowIcon } from './arrow.svg' export enum LinkType { Inner, @@ -12,7 +16,8 @@ export enum LinkType { interface MenuData { type: LinkType name: string - url: string + url?: string + children?: MenuData[] } const useMenuDataList = () => { @@ -31,7 +36,18 @@ const useMenuDataList = () => { { type: LinkType.Inner, name: t('navbar.tokens'), - url: '/tokens', + children: [ + { + type: LinkType.Inner, + name: t('navbar.sUDT'), + url: '/tokens', + }, + { + type: LinkType.Inner, + name: t('navbar.inscription'), + url: '/inscriptions', + }, + ], }, { type: LinkType.Inner, @@ -59,29 +75,72 @@ const useMenuDataList = () => { return list } -const MenuItemLink = ({ menu }: { menu: MenuData }) => { - const { url, type, name } = menu +const SubmenuDropdown: FC<{ menu: MenuData }> = ({ children, menu }) => { return ( - - {name} - + + {menu.children?.map(menu => ( + + {menu.name} + + ))} +
+ } + mouseEnterDelay={0} + transitionName="" + > + {children} + ) } export default memo(({ isMobile }: { isMobile: boolean }) => { const menuList = useMenuDataList() + return isMobile ? ( {menuList .filter(menu => menu.name !== undefined) - .map(menu => ( - - ))} + .map(menu => + menu.children ? ( + + + {menu.name} + + + + ) : ( + + {menu.name} + + ), + )} ) : ( {menuList.map(menu => - menu.type === LinkType.Inner ? ( + // eslint-disable-next-line no-nested-ternary + menu.children ? ( + + {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} + + {menu.name} + + + + ) : menu.type === LinkType.Inner ? ( {menu.name} diff --git a/src/components/Search/SearchByNameResults.tsx b/src/components/Search/SearchByNameResults.tsx index 254470d4b..1d1ae6ed3 100644 --- a/src/components/Search/SearchByNameResults.tsx +++ b/src/components/Search/SearchByNameResults.tsx @@ -43,10 +43,13 @@ const EmptySearchByNameResult = () => { const SearchByNameResult = (props: { item: UDTQueryResult }) => { const { t } = useTranslation() const { item } = props - const { typeHash, fullName, symbol } = item + const { typeHash, fullName, symbol, udtType } = item const displayName = symbol ?? fullName return ( - + {displayName ?? t('udt.unknown_token')} {typeHash} diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 91b4562f2..6e7b450c9 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -73,7 +73,7 @@ const handleSearchById = async ( break case SearchResultType.UDT: - history.push(`/sudt/${query}`) + history.push(data.attributes.udtType === 'omiga_inscription' ? `/inscription/${query}` : `/sudt/${query}`) break default: diff --git a/src/locales/en.json b/src/locales/en.json index 9bf9e472a..e43b5bb02 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -166,6 +166,8 @@ "nervos_dao": "Nervos DAO", "faucet": "Faucet", "tokens": "Tokens", + "sUDT": "sUDT", + "inscription": "Inscription", "docs": "Docs", "search_placeholder": "Block/Transaction/Address/Lock Hash/Type Hash/Args", "search_by_name_placeholder": "Token Name", @@ -581,7 +583,15 @@ "view-transfer-txns": "View Transfer Txns", "view-burn-txns": "View Burn Txns", "address-or-hash": "Transaction Hash / Address", - "address": "Address" + "address": "Address", + "inscriptions": "Inscriptions", + "status": "Status", + "progress": "Progress", + "expected_supply": "Expected Supply", + "mint_limit": "Mint Limit", + "mint_status_minting": "Minting", + "mint_status_closed": "Closed", + "mint_status_rebase_start": "Rebase Start" }, "nft": { "nft_collection": "NFT Collection", diff --git a/src/locales/zh.json b/src/locales/zh.json index ac7d53456..672308145 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -166,6 +166,8 @@ "nervos_dao": "Nervos DAO", "faucet": "水龙头", "tokens": "代币", + "sUDT": "sUDT", + "inscription": "铭文", "docs": "文档", "search_placeholder": "区块/交易/地址/锁定脚本哈希/Type 脚本哈希/参数", "search_by_name_placeholder": "代币名称", @@ -582,7 +584,15 @@ "view-transfer-txns": "查看转帐交易", "view-burn-txns": "查看销毁交易", "address-or-hash": "交易哈希 / 地址", - "address": "地址" + "address": "地址", + "inscriptions": "铭文", + "status": "状态", + "progress": "进度", + "expected_supply": "预期供应量", + "mint_limit": "铸造限制", + "mint_status_minting": "铸造中", + "mint_status_closed": "已关闭", + "mint_status_rebase_start": "重铸开始" }, "nft": { "nft_collection": "NFT 藏品", diff --git a/src/models/UDT/index.ts b/src/models/UDT/index.ts index 40e5a49ff..489fba58c 100644 --- a/src/models/UDT/index.ts +++ b/src/models/UDT/index.ts @@ -16,4 +16,23 @@ export interface UDT { typeScript: Script displayName?: string uan?: string + // TODO: Not quite sure if there are only two types here, so add a string for now. + udtType: 'omiga_inscription' | 'sudt' | string +} + +export enum MintStatus { + Minting = 'minting', + Closed = 'closed', + RebaseStart = 'rebase_start', +} + +export interface OmigaInscriptionCollection extends UDT { + mintStatus: MintStatus + mintLimit: string + expectedSupply: string + inscriptionInfoId: string +} + +export function isOmigaInscriptionCollection(udt: UDT): udt is OmigaInscriptionCollection { + return 'mintStatus' in udt && 'mintLimit' in udt && 'expectedSupply' in udt && 'inscriptionInfoId' in udt } diff --git a/src/pages/Tokens/index.tsx b/src/pages/Tokens/index.tsx index 3ba0d6997..a9f54aa51 100644 --- a/src/pages/Tokens/index.tsx +++ b/src/pages/Tokens/index.tsx @@ -2,6 +2,8 @@ import { Tooltip } from 'antd' import { Link } from 'react-router-dom' import { useQuery } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' +import { FC } from 'react' +import BigNumber from 'bignumber.js' import Content from '../../components/Content' import Pagination from '../../components/Pagination' import SortButton from '../../components/SortButton' @@ -26,9 +28,22 @@ import styles from './styles.module.scss' import { useIsMobile, usePaginationParamsInPage, useSortParam } from '../../hooks' import { explorerService } from '../../services/ExplorerService' import { QueryResult } from '../../components/QueryResult' -import { UDT } from '../../models/UDT' +import { OmigaInscriptionCollection, UDT, isOmigaInscriptionCollection } from '../../models/UDT' -const TokenItem = ({ token, isLast }: { token: UDT; isLast?: boolean }) => { +const TokenProgress: FC<{ token: OmigaInscriptionCollection }> = ({ token }) => { + return ( + + + + ) +} + +const TokenItem = ({ token, isLast }: { token: UDT | OmigaInscriptionCollection; isLast?: boolean }) => { const { displayName, fullName, uan } = token const { t } = useTranslation() @@ -37,6 +52,19 @@ const TokenItem = ({ token, isLast }: { token: UDT; isLast?: boolean }) => { const defaultName = t('udt.unknown_token') const isMobile = useIsMobile() + const mintStatus = + isOmigaInscriptionCollection(token) && + (isMobile ? ( + + {`${t('udt.status')}:`} + {t(`udt.mint_status_${token.mintStatus}`)} + + ) : ( + <> + {t(`udt.mint_status_${token.mintStatus}`)} + + + )) const transactions = isMobile ? ( {`${t('udt.transactions')}:`} @@ -54,7 +82,7 @@ const TokenItem = ({ token, isLast }: { token: UDT; isLast?: boolean }) => { localeNumberString(token.addressesCount) ) - const isKnown = Boolean(name) && token.published + const isKnown = (Boolean(name) && token.published) || isOmigaInscriptionCollection(token) return ( @@ -64,25 +92,36 @@ const TokenItem = ({ token, isLast }: { token: UDT; isLast?: boolean }) => {
{isKnown ? ( - + {symbol} - {name} + {!isOmigaInscriptionCollection(token) && {name}} ) : ( - - {symbol} - {defaultName} - - )} - {!isKnown && ( - - token icon - + <> + + {symbol} + {defaultName} + + + token icon + + )} {token.description && !isMobile &&
{token.description}
}
+ {isOmigaInscriptionCollection(token) && ( + <> + {isMobile && } +
{mintStatus}
+ + )}
{transactions}
{addressCount}
{!isMobile && ( @@ -94,7 +133,7 @@ const TokenItem = ({ token, isLast }: { token: UDT; isLast?: boolean }) => { ) } -export default () => { +const Tokens: FC<{ isInscription?: boolean }> = ({ isInscription }) => { const isMobile = useIsMobile() const { t } = useTranslation() const { currentPage, pageSize: _pageSize, setPage } = usePaginationParamsInPage() @@ -106,7 +145,11 @@ export default () => { data: tokens, total, pageSize, - } = await explorerService.api.fetchTokens(currentPage, _pageSize, sort ?? undefined) + } = await explorerService.api[isInscription ? 'fetchOmigaInscriptions' : 'fetchTokens']( + currentPage, + _pageSize, + sort ?? undefined, + ) if (tokens.length === 0) { throw new Error('Tokens empty') } @@ -124,13 +167,19 @@ export default () => {
- {t('udt.tokens')} + {isInscription ? t('udt.inscriptions') : t('udt.tokens')} {t('udt.submit_token_info')}
{!isMobile && {t('udt.uan_name')}} + {isInscription && ( + + {t('udt.status')} + + + )} {t('udt.transactions')} @@ -166,3 +215,5 @@ export default () => {
) } + +export default Tokens diff --git a/src/pages/Tokens/styled.tsx b/src/pages/Tokens/styled.tsx index fb251d1c8..eb78d429d 100644 --- a/src/pages/Tokens/styled.tsx +++ b/src/pages/Tokens/styled.tsx @@ -66,6 +66,11 @@ export const TokensTableTitle = styled.div` text-align: right; } + > span:nth-child(5) { + flex: 1.8; + text-align: right; + } + @media (max-width: ${variables.largeBreakPoint}) { > span:nth-child(1) { flex: 3.4; @@ -163,6 +168,24 @@ export const TokensTableItem = styled.div` } } + .tokensItemMintStatus { + flex: 3; + display: flex; + align-items: center; + font-size: 14px; + text-align: right; + color: #000; + + @media (max-width: ${variables.largeBreakPoint}) { + flex: 2.5; + } + + @media (max-width: ${variables.mobileBreakPoint}) { + margin-left: 30px; + margin-top: 3px; + } + } + .tokensItemTransactions { flex: 1.8; font-size: 14px; diff --git a/src/pages/Tokens/styles.module.scss b/src/pages/Tokens/styles.module.scss index 2b328436f..b338ea00f 100644 --- a/src/pages/Tokens/styles.module.scss +++ b/src/pages/Tokens/styles.module.scss @@ -1,9 +1,36 @@ +@import '../../styles/variables.module'; + +.link { + display: flex; + align-items: center; +} + .name { margin-left: 0.25rem; font-size: smaller; color: #aaa; } +.progress { + width: 112px; + height: 8px; + border-radius: 8px; + background: #f5f5f5; + overflow: hidden; + + .block { + display: block; + height: 100%; + border-radius: 8px; + background: var(--primary-color); + } + + @media (width <= $mobileBreakPoint) { + margin-left: 30px; + margin-top: 3px; + } +} + .sortIcon { border: none; outline: none; @@ -25,3 +52,15 @@ fill: var(--primary-color); } } + +.mintStatus { + text-align: left; + width: 88px; +} + +.colStatus:not( + [_='This `:not` selector is used to increase the specificity of the selector and serves no other purpose.'][_=''] + ) { + flex: 3; + text-align: left; +} diff --git a/src/pages/SimpleUDT/SimpleUDTComp.tsx b/src/pages/UDT/UDTComp.tsx similarity index 61% rename from src/pages/SimpleUDT/SimpleUDTComp.tsx rename to src/pages/UDT/UDTComp.tsx index f429174f8..78ee536c0 100644 --- a/src/pages/SimpleUDT/SimpleUDTComp.tsx +++ b/src/pages/UDT/UDTComp.tsx @@ -1,14 +1,10 @@ import { Tooltip } from 'antd' import { Link } from 'react-router-dom' import { useTranslation } from 'react-i18next' -import { useState } from 'react' +import { FC, useState } from 'react' +import BigNumber from 'bignumber.js' import TransactionItem from '../../components/TransactionItem/index' -import { - SimpleUDTTransactionsPagination, - SimpleUDTTransactionsPanel, - TypeScriptController, - UDTNoResultPanel, -} from './styled' +import { UDTTransactionsPagination, UDTTransactionsPanel, TypeScriptController, UDTNoResultPanel } from './styled' import { parseUDTAmount } from '../../utils/number' import { ReactComponent as OpenInNew } from '../../assets/open_in_new.svg' import { deprecatedAddrToNewAddr } from '../../utils/util' @@ -17,7 +13,7 @@ import AddressText from '../../components/AddressText' import PaginationWithRear from '../../components/PaginationWithRear' import { CsvExport } from '../../components/CsvExport' import { Transaction } from '../../models/Transaction' -import { UDT } from '../../models/UDT' +import { OmigaInscriptionCollection, UDT, isOmigaInscriptionCollection } from '../../models/UDT' import { Card, CardCellInfo, CardCellsLayout, HashCardHeader } from '../../components/Card' import { useIsMobile } from '../../hooks' import SUDTTokenIcon from '../../assets/sudt_token.png' @@ -28,6 +24,7 @@ import ArrowDownIcon from '../../assets/arrow_down.png' import ArrowUpBlueIcon from '../../assets/arrow_up_blue.png' import ArrowDownBlueIcon from '../../assets/arrow_down_blue.png' import Script from '../../components/Script' +import Capacity from '../../components/Capacity' const typeScriptIcon = (show: boolean) => { if (show) { @@ -36,7 +33,7 @@ const typeScriptIcon = (show: boolean) => { return isMainnet() ? ArrowDownIcon : ArrowDownBlueIcon } -const useAddressContent = (address: string) => { +const IssuerContent: FC<{ address: string }> = ({ address }) => { const { t } = useTranslation() if (!address) { return t('address.unable_decode_address') @@ -64,7 +61,7 @@ const useAddressContent = (address: string) => { ) } -export const SimpleUDTOverviewCard = ({ typeHash, udt }: { typeHash: string; udt: UDT }) => { +export const UDTOverviewCard = ({ typeHash, udt }: { typeHash: string; udt: UDT | OmigaInscriptionCollection }) => { const { t } = useTranslation() const isMobile = useIsMobile() const { @@ -81,33 +78,71 @@ export const SimpleUDTOverviewCard = ({ typeHash, udt }: { typeHash: string; udt } = udt const [showType, setShowType] = useState(false) - const items: CardCellInfo<'left' | 'right'>[] = [ - { - title: t('udt.name'), - content: displayName || fullName, - }, - { - title: t('udt.issuer'), - contentWrapperClass: styles.addressWidthModify, - content: useAddressContent(issuerAddress), - }, - { - title: t('udt.holder_addresses'), - content: addressesCount, - }, - { - title: t(uan ? 'udt.uan' : 'udt.symbol'), - content: uan || symbol, - }, - { - title: t('udt.decimal'), - content: decimal, - }, - { - title: t('udt.total_amount'), - content: parseUDTAmount(totalAmount, decimal), - }, - ] + const items: CardCellInfo<'left' | 'right'>[] = !isOmigaInscriptionCollection(udt) + ? [ + { + title: t('udt.name'), + content: displayName || fullName, + }, + { + title: t('udt.issuer'), + contentWrapperClass: styles.addressWidthModify, + content: , + }, + { + title: t('udt.holder_addresses'), + content: addressesCount, + }, + { + title: t(uan ? 'udt.uan' : 'udt.symbol'), + content: uan || symbol, + }, + { + title: t('udt.decimal'), + content: decimal, + }, + { + title: t('udt.total_amount'), + content: parseUDTAmount(totalAmount, decimal), + }, + ] + : [ + { + title: t('udt.name'), + content: displayName || fullName || (None), + }, + { + title: t('udt.status'), + content: t(`udt.mint_status_${udt.mintStatus}`), + }, + { + title: t('udt.progress'), + content: `${parseUDTAmount(udt.totalAmount, decimal)}/${parseUDTAmount(udt.expectedSupply, decimal)}`, + }, + { + title: t('udt.holder_addresses'), + content: addressesCount, + }, + { + title: t('udt.expected_supply'), + content: ( + + ), + }, + { + title: t('udt.decimal'), + content: decimal, + }, + { + title: t('udt.mint_limit'), + content: parseUDTAmount(udt.mintLimit, decimal), + }, + ] // TODO: To be implemented. const modifyTokenInfo = false &&
Modify Token Info
@@ -123,7 +158,7 @@ export const SimpleUDTOverviewCard = ({ typeHash, udt }: { typeHash: string; udt ) return ( - + {/* When encountering more complex requirements, consider extracting the components within HashCardHeader into smaller components. Then, implement a completely new variant or freely assemble them externally. */} {isMobile && cardTitle} @@ -145,7 +180,7 @@ export const SimpleUDTOverviewCard = ({ typeHash, udt }: { typeHash: string; udt ) } -export const SimpleUDTComp = ({ +export const UDTComp = ({ currentPage, pageSize, transactions, @@ -153,6 +188,7 @@ export const SimpleUDTComp = ({ onPageChange, filterNoResult, id, + isInscription, }: { currentPage: number pageSize: number @@ -161,6 +197,7 @@ export const SimpleUDTComp = ({ onPageChange: (page: number) => void filterNoResult?: boolean id: string + isInscription?: boolean }) => { const { t } = useTranslation() const totalPages = Math.ceil(total / pageSize) @@ -174,7 +211,7 @@ export const SimpleUDTComp = ({ } return ( <> - + {transactions.map( (transaction: Transaction, index: number) => transaction && ( @@ -187,17 +224,18 @@ export const SimpleUDTComp = ({ /> ), )} - - + + } + // TODO: The backend has not yet implemented export support for Inscription (xUDT), so it is disabled for now. + rear={!isInscription && } /> - + ) } -export default SimpleUDTComp +export default UDTComp diff --git a/src/pages/SimpleUDT/index.tsx b/src/pages/UDT/index.tsx similarity index 82% rename from src/pages/SimpleUDT/index.tsx rename to src/pages/UDT/index.tsx index aecd233d8..7cf1efc5b 100644 --- a/src/pages/SimpleUDT/index.tsx +++ b/src/pages/UDT/index.tsx @@ -2,9 +2,10 @@ import { Link, useHistory, useLocation, useParams } from 'react-router-dom' import { useTranslation } from 'react-i18next' import { useQuery } from '@tanstack/react-query' import { Popover } from 'antd' +import { FC } from 'react' import Content from '../../components/Content' -import { SimpleUDTContentPanel, UDTTransactionTitlePanel } from './styled' -import SimpleUDTComp, { SimpleUDTOverviewCard } from './SimpleUDTComp' +import { UDTContentPanel, UDTTransactionTitlePanel } from './styled' +import UDTComp, { UDTOverviewCard } from './UDTComp' import { useIsMobile, usePaginationParamsInPage } from '../../hooks' import Filter from '../../components/Search/Filter' import { localeNumberString } from '../../utils/number' @@ -23,7 +24,7 @@ enum TransactionType { Burn = 'destruction', } -export const SimpleUDT = () => { +export const UDT: FC<{ isInscription?: boolean }> = ({ isInscription }) => { const { t } = useTranslation() const isMobile = useIsMobile() const { push } = useHistory() @@ -35,8 +36,10 @@ export const SimpleUDT = () => { const filter = query.get('filter') const type = query.get('type') - const querySimpleUDT = useQuery(['simple-udt'], () => explorerService.api.fetchSimpleUDT(typeHash)) - const udt = querySimpleUDT.data ?? defaultUDTInfo + const queryUDT = useQuery(['udt', isInscription], () => + isInscription ? explorerService.api.fetchOmigaInscription(typeHash) : explorerService.api.fetchSimpleUDT(typeHash), + ) + const udt = queryUDT.data ?? defaultUDTInfo const querySimpleUDTTransactions = useQuery( ['simple-udt-transactions', typeHash, currentPage, _pageSize, filter, type], @@ -45,7 +48,7 @@ export const SimpleUDT = () => { data: transactions, total, pageSize: resPageSize, - } = await explorerService.api.fetchSimpleUDTTransactions({ + } = await explorerService.api.fetchUDTTransactions({ typeHash, page: currentPage, size: pageSize, @@ -89,11 +92,12 @@ export const SimpleUDT = () => { ] const isFilteredByType = filterList.some(f => f.value === type) + const udtLinkPrefix = !isInscription ? '/sudt' : '/inscription' return ( - - + +
@@ -106,10 +110,10 @@ export const SimpleUDT = () => { showReset={!!filter} placeholder={t('udt.search_placeholder')} onFilter={filter => { - push(`/sudt/${typeHash}?${new URLSearchParams({ filter })}`) + push(`${udtLinkPrefix}/${typeHash}?${new URLSearchParams({ filter })}`) }} onReset={() => { - push(`/sudt/${typeHash}`) + push(`${udtLinkPrefix}/${typeHash}`) }} />
@@ -122,7 +126,7 @@ export const SimpleUDT = () => { {filterList.map(f => ( {f.title} @@ -141,7 +145,7 @@ export const SimpleUDT = () => { {data => ( - { onPageChange={setPage} filterNoResult={filterNoResult} id={typeHash} + isInscription={isInscription} /> )} - + ) } -export default SimpleUDT +export default UDT diff --git a/src/pages/SimpleUDT/state.ts b/src/pages/UDT/state.ts similarity index 53% rename from src/pages/SimpleUDT/state.ts rename to src/pages/UDT/state.ts index 89e1c867f..31bbb2fe5 100644 --- a/src/pages/SimpleUDT/state.ts +++ b/src/pages/UDT/state.ts @@ -1,4 +1,4 @@ -import { UDT } from '../../models/UDT' +import { UDT, OmigaInscriptionCollection, MintStatus } from '../../models/UDT' export const defaultUDTInfo: UDT = { symbol: '', @@ -18,4 +18,13 @@ export const defaultUDTInfo: UDT = { codeHash: '', hashType: '', }, + udtType: 'sudt', +} + +export const defaultOmigaInscriptionInfo: OmigaInscriptionCollection = { + ...defaultUDTInfo, + mintStatus: MintStatus.Closed, + mintLimit: '0', + expectedSupply: '0', + inscriptionInfoId: '', } diff --git a/src/pages/SimpleUDT/styled.tsx b/src/pages/UDT/styled.tsx similarity index 91% rename from src/pages/SimpleUDT/styled.tsx rename to src/pages/UDT/styled.tsx index e8d0da257..b22efbeda 100644 --- a/src/pages/SimpleUDT/styled.tsx +++ b/src/pages/UDT/styled.tsx @@ -15,7 +15,7 @@ export const SUDTContentPanel = styled.div` padding: 20px; } ` -export const SimpleUDTContentPanel = styled.div` +export const UDTContentPanel = styled.div` display: flex; flex-direction: column; align-items: center; @@ -28,7 +28,7 @@ export const SimpleUDTContentPanel = styled.div` } ` -export const SimpleUDTPendingRewardTitlePanel = styled.div` +export const UDTPendingRewardTitlePanel = styled.div` display: flex; flex-direction: row; @@ -54,7 +54,7 @@ export const SimpleUDTPendingRewardTitlePanel = styled.div` } ` -export const SimpleUDTLockScriptController = styled.div` +export const UDTLockScriptController = styled.div` font-size: 16px; font-weight: 600; margin-top: 15px; @@ -102,11 +102,11 @@ export const TypeScriptController = styled(SimpleButton)` } ` -export const SimpleUDTTransactionsPanel = styled.div` +export const UDTTransactionsPanel = styled.div` width: 100%; ` -export const SimpleUDTTransactionsPagination = styled.div` +export const UDTTransactionsPagination = styled.div` margin-top: 4px; width: 100%; ` diff --git a/src/pages/SimpleUDT/styles.module.scss b/src/pages/UDT/styles.module.scss similarity index 97% rename from src/pages/SimpleUDT/styles.module.scss rename to src/pages/UDT/styles.module.scss index ef8eb0d61..3c9a02ff8 100644 --- a/src/pages/SimpleUDT/styles.module.scss +++ b/src/pages/UDT/styles.module.scss @@ -1,6 +1,6 @@ @import '../../styles/variables.module'; -.simpleUDTOverviewCard:not( +.udtOverviewCard:not( [_='This `:not` selector is used to increase the specificity of the selector and serves no other purpose.'] ) { padding-bottom: 20px; @@ -47,6 +47,10 @@ } } +.noneName { + color: #999; +} + .openInNew { cursor: pointer; margin-left: 4px; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index ff7e4c08e..d2766f2d8 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -12,7 +12,7 @@ const Transaction = lazy(() => import('../pages/Transaction')) const TransactionList = lazy(() => import('../pages/TransactionList')) const Address = lazy(() => import('../pages/Address')) const ScriptPage = lazy(() => import('../pages/Script')) -const SimpleUDT = lazy(() => import('../pages/SimpleUDT')) +const UDT = lazy(() => import('../pages/UDT')) const NftCollections = lazy(() => import('../pages/NftCollections')) const NftCollectionInfo = lazy(() => import('../pages/NftCollectionInfo')) const NftInfo = lazy(() => import('../pages/NftInfo')) @@ -94,7 +94,13 @@ const routes: RouteProps[] = [ }, { path: '/sudt/:hash', - component: SimpleUDT, + component: UDT, + }, + { + path: '/inscription/:hash', + render: routeProps => { + return + }, }, { path: '/nft-collections', @@ -116,6 +122,12 @@ const routes: RouteProps[] = [ path: '/tokens', component: Tokens, }, + { + path: '/inscriptions', + render: routeProps => { + return + }, + }, { path: '/charts', component: StatisticsChart, diff --git a/src/services/ExplorerService/fetcher.ts b/src/services/ExplorerService/fetcher.ts index 240b10a69..75081b631 100644 --- a/src/services/ExplorerService/fetcher.ts +++ b/src/services/ExplorerService/fetcher.ts @@ -12,7 +12,7 @@ import { Script } from '../../models/Script' import { Block } from '../../models/Block' import { Transaction } from '../../models/Transaction' import { Address } from '../../models/Address' -import { UDT } from '../../models/UDT' +import { OmigaInscriptionCollection, UDT } from '../../models/UDT' async function v1Get(...args: Parameters) { return requesterV1.get(...args).then(res => toCamelcase>(res.data)) @@ -172,7 +172,7 @@ export const apiFetcher = { | Response.Wrapper | Response.Wrapper | Response.Wrapper - | Response.Wrapper + | Response.Wrapper | Response.Wrapper