feat: improve lighthouse report
This commit is contained in:
parent
f5fdc37fc3
commit
03d965bfc9
|
@ -48,7 +48,7 @@ const Balance: FC<Props> = ({ className }) => {
|
|||
}
|
||||
className="hover:cursor-pointer"
|
||||
>
|
||||
<h3 className="text-4xl">
|
||||
<h1 className="text-4xl">
|
||||
<span className="transition-colors">Ӿ</span>
|
||||
<span
|
||||
className={clsx(
|
||||
|
@ -72,11 +72,11 @@ const Balance: FC<Props> = ({ className }) => {
|
|||
'NO'
|
||||
)}
|
||||
</span>
|
||||
</h3>
|
||||
</h1>
|
||||
{showFiatBalance && (
|
||||
<h4 className="text-xl transition-colors">
|
||||
<h2 className="text-xl transition-colors">
|
||||
$ {(Number(xnoBalance) * xnoPrice).toFixed(2)}
|
||||
</h4>
|
||||
</h2>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -59,17 +59,20 @@ const BottomMenu: FC<Props> = ({ className }) => {
|
|||
{pathname !== '/dashboard' && (
|
||||
<Link href="/dashboard">
|
||||
<a
|
||||
role="menuitem"
|
||||
className={clsx(
|
||||
'h-12 p-1 bg-purple-400 transition-colors rounded shadow hover:bg-purple-400 disabled:hover:bg-purple-400 disabled:cursor-default',
|
||||
{ 'pointer-events-none': isWelcoming }
|
||||
)}
|
||||
>
|
||||
<span className="hidden">go back to the dashboard</span>
|
||||
<HomeIcon className="h-full text-purple-50 dark:text-gray-900 transition-colors" />
|
||||
</a>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
<div
|
||||
role="menuitem"
|
||||
className={clsx(
|
||||
'flex gap-4 xs:gap-6 items-end',
|
||||
leftHanded ? 'flex-row-reverse' : 'flex-row'
|
||||
|
@ -77,6 +80,7 @@ const BottomMenu: FC<Props> = ({ className }) => {
|
|||
>
|
||||
{'share' in navigator ? (
|
||||
<button
|
||||
aria-label="Share your nano address"
|
||||
disabled={isWelcoming}
|
||||
className="w-10 h-16 p-1 bg-purple-400 transition-colors rounded shadow hover:bg-purple-400 disabled:hover:bg-purple-400 disabled:cursor-default"
|
||||
onClick={onShare}
|
||||
|
@ -85,6 +89,7 @@ const BottomMenu: FC<Props> = ({ className }) => {
|
|||
</button>
|
||||
) : (
|
||||
<button
|
||||
aria-label="Copy your nano address to the clipboard"
|
||||
disabled={isWelcoming || confirmCopyAddress}
|
||||
className={clsx(
|
||||
'p-1 h-16 w-10 rounded shadow transition-colors',
|
||||
|
@ -105,12 +110,14 @@ const BottomMenu: FC<Props> = ({ className }) => {
|
|||
<div className={clsx('flex h-16', { 'flex-row-reverse': leftHanded })}>
|
||||
<Link href="/receive/qr">
|
||||
<a
|
||||
role="navigation"
|
||||
className={clsx(
|
||||
'bg-purple-400 transition-colors h-16 px-1 xs:px-2 w-10 xs:w-14 hover:bg-purple-400 disabled:hover:bg-purple-400 shadow disabled:cursor-default',
|
||||
leftHanded ? 'rounded-r' : 'rounded-l',
|
||||
{ 'pointer-events-none': isWelcoming }
|
||||
)}
|
||||
>
|
||||
<span className="hidden">see your qrcode</span>
|
||||
<LoginIcon
|
||||
className={
|
||||
'h-full text-purple-50 dark:text-gray-900 transition-colors w-full -rotate-child-90'
|
||||
|
@ -118,17 +125,22 @@ const BottomMenu: FC<Props> = ({ className }) => {
|
|||
/>
|
||||
</a>
|
||||
</Link>
|
||||
<div className="h-16 p-1 border-t-2 border-b-2 border-purple-400 shadow">
|
||||
<div
|
||||
role="presentation"
|
||||
className="h-16 p-1 border-t-2 border-b-2 border-purple-400 shadow"
|
||||
>
|
||||
<QrcodeIcon className="h-full text-gray-900 dark:text-purple-100 transition-colors" />
|
||||
</div>
|
||||
<Link href="/send/qrOrAddress">
|
||||
<a
|
||||
role="navigation"
|
||||
className={clsx(
|
||||
'bg-purple-400 transition-colors h-16 px-1 xs:px-2 w-10 xs:w-14 hover:bg-purple-400 disabled:hover:bg-purple-400 shadow disabled:cursor-default',
|
||||
leftHanded ? 'rounded-l' : 'rounded-r',
|
||||
{ 'pointer-events-none': isWelcoming }
|
||||
)}
|
||||
>
|
||||
<span className="hidden">send ӾNO</span>
|
||||
<PaperAirplaneIcon className="h-full text-purple-50 dark:text-gray-900 transition-colors w-full rotate-[30deg] translate-x-1 -translate-y-0.5" />
|
||||
</a>
|
||||
</Link>
|
||||
|
|
|
@ -2,6 +2,7 @@ import { ChevronUpIcon, ClockIcon } from '@heroicons/react/outline'
|
|||
import { DownloadIcon, RefreshIcon, UploadIcon } from '@heroicons/react/solid'
|
||||
import clsx from 'clsx'
|
||||
import { Unit, convert } from 'nanocurrency'
|
||||
import Link from 'next/link'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
|
||||
import useAccountHistory from '../lib/hooks/useAccountHistory'
|
||||
|
@ -70,9 +71,9 @@ const RecentTransactions: FC<Props> = () => {
|
|||
className="flex items-center justify-between gap-1"
|
||||
onClick={() => setReceivablesExpanded(prev => !prev)}
|
||||
>
|
||||
<h2 className="flex-1 text-2xl font-semibold transition-colors text-gray-900 dark:text-purple-50">
|
||||
<h1 className="flex-1 text-2xl font-semibold transition-colors text-gray-900 dark:text-purple-50">
|
||||
incoming
|
||||
</h2>
|
||||
</h1>
|
||||
|
||||
<span className="w-6 text-base text-center dark:text-gray-900 text-purple-50 rounded-full bg-purple-400 dark:bg-purple-50">
|
||||
{receivableBlocks.length}
|
||||
|
@ -98,6 +99,7 @@ const RecentTransactions: FC<Props> = () => {
|
|||
className="flex items-center justify-between px-3 py-3 text-black border-r-4 border-yellow-400 rounded shadow bg-gray-50 transition-colors dark:hover:bg-gray-700 dark:bg-gray-800 dark:text-purple-50 gap-2"
|
||||
>
|
||||
<button
|
||||
aria-label={`Receive transaction from ${receivable.from}`}
|
||||
className="contents"
|
||||
onClick={() => onIncomingClick(receivable)}
|
||||
>
|
||||
|
@ -132,11 +134,12 @@ const RecentTransactions: FC<Props> = () => {
|
|||
)}
|
||||
<section className="flex flex-col flex-1 w-full min-h-0 gap-3">
|
||||
<div className="flex items-center justify-between gap-1">
|
||||
<h2 className="text-2xl font-semibold text-gray-900 dark:text-purple-50">
|
||||
<h1 className="text-2xl font-semibold text-gray-900 dark:text-purple-50">
|
||||
recent transactions
|
||||
</h2>
|
||||
</h1>
|
||||
{hasHistory && (
|
||||
<button
|
||||
aria-label="Refresh transaction history"
|
||||
onClick={() => {
|
||||
setRefectingHistory(true)
|
||||
refetchHistory()
|
||||
|
@ -163,40 +166,46 @@ const RecentTransactions: FC<Props> = () => {
|
|||
txn.type === 'send' ? 'border-yellow-300' : 'border-green-300'
|
||||
)}
|
||||
>
|
||||
<button className="contents" onClick={() => {}}>
|
||||
{txn.type === 'send' ? (
|
||||
<UploadIcon className="flex-shrink-0 w-6 text-yellow-300" />
|
||||
) : (
|
||||
<DownloadIcon
|
||||
className={clsx('w-6 flex-shrink-0 text-green-300')}
|
||||
/>
|
||||
)}
|
||||
<div className="flex-1 overflow-hidden text-left overflow-ellipsis whitespace-nowrap">
|
||||
{Intl.DateTimeFormat([], {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: '2-digit',
|
||||
}).format(Number(txn.local_timestamp) * 1000)}{' '}
|
||||
- {<span className="text-xs">{txn.account}</span>}
|
||||
</div>
|
||||
<span className="flex-shrink-0 font-medium">
|
||||
Ӿ{' '}
|
||||
{rawToNanoDisplay(txn.amount) === 'small' ? (
|
||||
'<0.01'
|
||||
) : rawToNanoDisplay(txn.amount).startsWith('0.') ? (
|
||||
<>
|
||||
<span className="text-sm font-semibold">0</span>
|
||||
{rawToNanoDisplay(txn.amount).substring(1)}
|
||||
</>
|
||||
<Link href={`/transaction/${txn.hash}`}>
|
||||
<a className="contents pointer-events-none">
|
||||
<span className="hidden">
|
||||
see {txn.hash} transaction details
|
||||
</span>
|
||||
{txn.type === 'send' ? (
|
||||
<UploadIcon className="flex-shrink-0 w-6 text-yellow-300" />
|
||||
) : (
|
||||
rawToNanoDisplay(txn.amount)
|
||||
<DownloadIcon
|
||||
className={clsx('w-6 flex-shrink-0 text-green-300')}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
<div className="flex-1 overflow-hidden text-left overflow-ellipsis whitespace-nowrap">
|
||||
{Intl.DateTimeFormat([], {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: '2-digit',
|
||||
}).format(Number(txn.local_timestamp) * 1000)}{' '}
|
||||
- {<span className="text-xs">{txn.account}</span>}
|
||||
</div>
|
||||
<span className="flex-shrink-0 font-medium">
|
||||
Ӿ{' '}
|
||||
{rawToNanoDisplay(txn.amount) === 'small' ? (
|
||||
'<0.01'
|
||||
) : rawToNanoDisplay(txn.amount).startsWith('0.') ? (
|
||||
<>
|
||||
<span className="text-sm font-semibold">0</span>
|
||||
{rawToNanoDisplay(txn.amount).substring(1)}
|
||||
</>
|
||||
) : (
|
||||
rawToNanoDisplay(txn.amount)
|
||||
)}
|
||||
</span>
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
{hasMore && (
|
||||
<button
|
||||
aria-label="Load more transactions"
|
||||
className="px-4 py-1.5 font-bold transition-colors bg-purple-400 rounded text-purple-50 shadow dark:text-gray-900 place-self-center"
|
||||
onClick={loadMore}
|
||||
>
|
||||
|
|
|
@ -75,6 +75,7 @@ const TopMenu: FC<Props> = () => {
|
|||
{!isWelcoming && (
|
||||
<div className="relative z-20" ref={advancedButtonRef}>
|
||||
<button
|
||||
aria-label="Open adavanced menu"
|
||||
className={clsx(
|
||||
'w-10 p-1 rounded bg-purple-400 shadow text-purple-50 dark:text-gray-900 hover:cursor-pointer hover:bg-purple-400 transition-colors dark:hover:text-purple-50'
|
||||
)}
|
||||
|
@ -96,6 +97,7 @@ const TopMenu: FC<Props> = () => {
|
|||
>
|
||||
<li>
|
||||
<button
|
||||
aria-label="Copy your nano seed to your clipboard"
|
||||
className={clsx(
|
||||
'p-1 rounded transition-colors dark:hover:text-gray-900 w-full hover:bg-purple-300 bg-purple-400 dark:text-gray-900 text-purple-50'
|
||||
)}
|
||||
|
@ -133,6 +135,7 @@ const TopMenu: FC<Props> = () => {
|
|||
|
||||
<div className="relative z-20" ref={preferencesButtonRef}>
|
||||
<button
|
||||
aria-label="Open preferences menu"
|
||||
className={clsx(
|
||||
'w-10 p-1 rounded bg-purple-400 shadow text-purple-50 hover:cursor-pointer hover:bg-purple-400 transition-colors dark:hover:text-purple-50 dark:text-gray-900'
|
||||
)}
|
||||
|
@ -173,6 +176,7 @@ const TopMenu: FC<Props> = () => {
|
|||
)} */}
|
||||
<li role="menuitem">
|
||||
<button
|
||||
aria-label="Toggle dark mode"
|
||||
disabled={!showPreferences}
|
||||
className={clsx(
|
||||
'p-1 rounded transition-colors duration-100 w-full dark:hover:text-purple-50 hover:bg-purple-300 dark:text-purple-400 dark:bg-gray-900 bg-purple-400 text-purple-50',
|
||||
|
@ -188,6 +192,7 @@ const TopMenu: FC<Props> = () => {
|
|||
</li>
|
||||
<li role="menuitem">
|
||||
<button
|
||||
aria-label="Toggle left handed mode"
|
||||
disabled={!showPreferences}
|
||||
className={clsx(
|
||||
'p-1 rounded transition-colors duration-100 w-full dark:hover:text-purple-50 bg-purple-400',
|
||||
|
|
|
@ -21,8 +21,8 @@ const _fetchAccountReceivable = (
|
|||
// most nodes haven't upgraded yet https://docs.nano.org/commands/rpc-protocol/#accounts_pending
|
||||
// this will be the future api for this function
|
||||
const fetchAccountReceivable = async (address: string, count = 20) =>
|
||||
_fetchAccountReceivable(address, count).catch(() =>
|
||||
_fetchAccountReceivable(address, count, true)
|
||||
_fetchAccountReceivable(address, count, true).catch(() =>
|
||||
_fetchAccountReceivable(address, count)
|
||||
)
|
||||
|
||||
export default fetchAccountReceivable
|
||||
|
|
|
@ -5,6 +5,7 @@ import withAnalyzer from '@next/bundle-analyzer'
|
|||
*/
|
||||
const nextConfig = {
|
||||
compress: true,
|
||||
productionBrowserSourceMaps: true,
|
||||
}
|
||||
|
||||
export default withAnalyzer({
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { AppProps } from 'next/app'
|
||||
import Head from 'next/head'
|
||||
import { FC } from 'react'
|
||||
import { SWRConfig } from 'swr'
|
||||
|
||||
|
@ -18,13 +19,18 @@ const MyApp: FC<AppProps> = ({ Component, pageProps }) => {
|
|||
if (validatingCredential) return null // todo
|
||||
|
||||
return (
|
||||
<SWRConfig value={{ fetcher }}>
|
||||
<MemCacheProvider>
|
||||
<Layout>
|
||||
<Component {...pageProps} />
|
||||
</Layout>
|
||||
</MemCacheProvider>
|
||||
</SWRConfig>
|
||||
<>
|
||||
<Head>
|
||||
<title>zep⚡️ - nano wallet</title>
|
||||
</Head>
|
||||
<SWRConfig value={{ fetcher }}>
|
||||
<MemCacheProvider>
|
||||
<Layout>
|
||||
<Component {...pageProps} />
|
||||
</Layout>
|
||||
</MemCacheProvider>
|
||||
</SWRConfig>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ class MyDocument extends Document {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<Html className="dark">
|
||||
<Html className="dark" lang="en">
|
||||
<Head>
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<link rel="apple-touch-icon" href="/images/icon-small.png" />
|
||||
|
@ -30,6 +30,7 @@ class MyDocument extends Document {
|
|||
href="/images/favicon-16x16.png"
|
||||
/>
|
||||
<meta name="theme-color" content="#8a5cf6" />
|
||||
<meta name="description" content="nano digital currency wallet" />
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
import type { NextPage } from 'next'
|
||||
import Head from 'next/head'
|
||||
|
||||
import RecentTransactions from '../components/RecentTransactions'
|
||||
|
||||
const Dashboard: NextPage = () => {
|
||||
return <RecentTransactions />
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>zep⚡️ - dashboard</title>
|
||||
</Head>
|
||||
<RecentTransactions />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Dashboard
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { FingerPrintIcon } from '@heroicons/react/outline'
|
||||
import { useRouter } from 'next/dist/client/router'
|
||||
import Head from 'next/head'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import { checkBiometrics } from '../lib/biometrics'
|
||||
|
@ -22,15 +23,23 @@ const Landing = () => {
|
|||
}, [push, biometricsAuth])
|
||||
|
||||
return (
|
||||
<main className="flex flex-col items-center w-full h-full">
|
||||
<h1 className="text-4xl font-medium mb-16">welcome</h1>
|
||||
<button className="p-3 dark:bg-gray-800 bg-purple-50 mb-3 rounded shadow hover:cursor-pointer">
|
||||
<FingerPrintIcon className="h-16 text-gray-900 dark:text-purple-50" />
|
||||
</button>
|
||||
<h2 className="text-2xl text-center">
|
||||
please sign in with your biometrics
|
||||
</h2>
|
||||
</main>
|
||||
<>
|
||||
<Head>
|
||||
<title>zep⚡️ - sign in</title>
|
||||
</Head>
|
||||
<main className="flex flex-col items-center w-full h-full">
|
||||
<h1 className="text-4xl font-medium mb-16">welcome</h1>
|
||||
<button
|
||||
aria-label="Trigger biometrics authentication"
|
||||
className="p-3 dark:bg-gray-800 bg-purple-50 mb-3 rounded shadow hover:cursor-pointer"
|
||||
>
|
||||
<FingerPrintIcon className="h-16 text-gray-900 dark:text-purple-50" />
|
||||
</button>
|
||||
<h2 className="text-2xl text-center">
|
||||
please sign in with your biometrics
|
||||
</h2>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { LoginIcon } from '@heroicons/react/outline'
|
||||
import type { NextPage } from 'next'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useState } from 'react'
|
||||
|
||||
|
@ -19,22 +20,24 @@ const MyQrCode: NextPage = () => {
|
|||
const [xnoToSend, setXnoToSend] = useState('')
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col gap-8">
|
||||
<span className="flex items-center justify-start gap-2">
|
||||
<LoginIcon className="-rotate-child-90 dark:text-purple-50 h-7 xs:h-10 text-gray-900 translate-x-1 transition-colors" />
|
||||
<h1 className="text-3xl sm:text-5xl">receive</h1>
|
||||
</span>
|
||||
<>
|
||||
<Head>
|
||||
<title>zep⚡️ - my qrcode</title>
|
||||
</Head>
|
||||
<div className="h-full flex flex-col gap-8">
|
||||
<span className="flex items-center justify-start gap-2">
|
||||
<LoginIcon className="-rotate-child-90 dark:text-purple-50 h-7 xs:h-10 text-gray-900 translate-x-1 transition-colors" />
|
||||
<h1 className="text-3xl sm:text-5xl">receive</h1>
|
||||
</span>
|
||||
|
||||
<canvas
|
||||
className="!w-64 !h-64 rounded place-self-center shadow-lg"
|
||||
ref={canvasRef}
|
||||
/>
|
||||
<canvas className="rounded place-self-center shadow" ref={canvasRef} />
|
||||
|
||||
<div className="flex flex-col place-self-center items-center gap-3">
|
||||
<span className="text-xl transition-colors">optional amount</span>
|
||||
<XnoInput value={xnoToSend} onChange={setXnoToSend} />
|
||||
<div className="flex flex-col place-self-center items-center gap-3">
|
||||
<span className="text-xl transition-colors">optional amount</span>
|
||||
<XnoInput value={xnoToSend} onChange={setXnoToSend} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import { PaperAirplaneIcon } from '@heroicons/react/solid'
|
|||
import clsx from 'clsx'
|
||||
import { Unit, convert } from 'nanocurrency'
|
||||
import type { NextPage } from 'next'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
|
@ -60,79 +61,83 @@ const Send: NextPage = () => {
|
|||
}, [amount, xnoToSend, hasQueryAmount])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full gap-8 pb-4">
|
||||
<span className="flex items-center gap-2">
|
||||
<PaperAirplaneIcon className="transition-colors dark:text-purple-50 h-7 xs:h-10 text-gray-900 rotate-[30deg] translate-x-1" />
|
||||
<h1 className="text-3xl sm:text-5xl">send</h1>
|
||||
</span>
|
||||
<form
|
||||
onSubmit={ev => ev.preventDefault()}
|
||||
className="flex flex-col gap-3 items-center h-full"
|
||||
>
|
||||
<XnoInput value={xnoToSend} onChange={setXnoToSend} />
|
||||
<span className="flex-1 text-lg text-center">
|
||||
<span className="text-extrabold">to</span>
|
||||
<br />
|
||||
<span className="text-purple-400 font-medium">
|
||||
{address?.substring(0, 10)}
|
||||
</span>
|
||||
{address?.substring(10, 21)}
|
||||
<br />
|
||||
{address?.substring(21, 42)}
|
||||
<br />
|
||||
{address?.substring(42, 56)}
|
||||
<span className="text-purple-400 font-medium">
|
||||
{address?.substring(56)}
|
||||
</span>
|
||||
<>
|
||||
<Head>
|
||||
<title>zep⚡️ - send ӾNO</title>
|
||||
</Head>
|
||||
<div className="flex flex-col h-full gap-8 pb-4">
|
||||
<span className="flex items-center gap-2">
|
||||
<PaperAirplaneIcon className="transition-colors dark:text-purple-50 h-7 xs:h-10 text-gray-900 rotate-[30deg] translate-x-1" />
|
||||
<h1 className="text-3xl sm:text-5xl">send</h1>
|
||||
</span>
|
||||
<div
|
||||
className={clsx(
|
||||
'dark:bg-gray-800 bg-purple-100 rounded-2xl p-2 relative w-72 z-10 transition-all hover:cursor-pointer',
|
||||
{
|
||||
'opacity-50': disableSlider,
|
||||
}
|
||||
)}
|
||||
onMouseMove={ev => {
|
||||
if (sliding && !disableSlider) setCurrentX(ev.clientX)
|
||||
}}
|
||||
onMouseUp={() => {
|
||||
if (sliderPercentage !== 1 && !disableSlider) backToBase()
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
if (sliderPercentage !== 1 && !disableSlider) backToBase()
|
||||
}}
|
||||
<form
|
||||
onSubmit={ev => ev.preventDefault()}
|
||||
className="flex flex-col gap-3 items-center h-full"
|
||||
>
|
||||
<XnoInput value={xnoToSend} onChange={setXnoToSend} />
|
||||
<span className="flex-1 text-lg text-center">
|
||||
<span className="text-extrabold">to</span>
|
||||
<br />
|
||||
<span className="text-purple-400 font-medium">
|
||||
{address?.substring(0, 10)}
|
||||
</span>
|
||||
{address?.substring(10, 21)}
|
||||
<br />
|
||||
{address?.substring(21, 42)}
|
||||
<br />
|
||||
{address?.substring(42, 56)}
|
||||
<span className="text-purple-400 font-medium">
|
||||
{address?.substring(56)}
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
className={clsx(
|
||||
'dark:bg-purple-50 bg-purple-400 p-2 rounded-xl w-11 z-30 transform-gpu transition-colors',
|
||||
'dark:bg-gray-800 bg-purple-100 rounded-2xl p-2 relative w-72 z-10 transition-all hover:cursor-pointer',
|
||||
{
|
||||
'transition-all': !sliding,
|
||||
'opacity-50': disableSlider,
|
||||
}
|
||||
)}
|
||||
style={{
|
||||
transform: `translate3d(${sliderPercentage * 228}px, 0, 0)`,
|
||||
onMouseMove={ev => {
|
||||
if (sliding && !disableSlider) setCurrentX(ev.clientX)
|
||||
}}
|
||||
onTouchStart={ev => {
|
||||
if (!disableSlider) {
|
||||
setSliding(true)
|
||||
setStartX(ev.touches.item(0).clientX)
|
||||
}
|
||||
onMouseUp={() => {
|
||||
if (sliderPercentage !== 1 && !disableSlider) backToBase()
|
||||
}}
|
||||
onTouchMove={ev => {
|
||||
if (sliding && !disableSlider)
|
||||
setCurrentX(ev.touches.item(0).clientX)
|
||||
}}
|
||||
onTouchEnd={() => {
|
||||
if (!disableSlider) backToBase()
|
||||
}}
|
||||
onMouseDown={ev => {
|
||||
if (!disableSlider) {
|
||||
setSliding(true)
|
||||
setStartX(ev.clientX)
|
||||
}
|
||||
onMouseLeave={() => {
|
||||
if (sliderPercentage !== 1 && !disableSlider) backToBase()
|
||||
}}
|
||||
>
|
||||
{/* <PaperAirplaneIcon
|
||||
<div
|
||||
className={clsx(
|
||||
'dark:bg-purple-50 bg-purple-400 p-2 rounded-xl w-11 z-30 transform-gpu transition-colors',
|
||||
{
|
||||
'transition-all': !sliding,
|
||||
}
|
||||
)}
|
||||
style={{
|
||||
transform: `translate3d(${sliderPercentage * 228}px, 0, 0)`,
|
||||
}}
|
||||
onTouchStart={ev => {
|
||||
if (!disableSlider) {
|
||||
setSliding(true)
|
||||
setStartX(ev.touches.item(0).clientX)
|
||||
}
|
||||
}}
|
||||
onTouchMove={ev => {
|
||||
if (sliding && !disableSlider)
|
||||
setCurrentX(ev.touches.item(0).clientX)
|
||||
}}
|
||||
onTouchEnd={() => {
|
||||
if (!disableSlider) backToBase()
|
||||
}}
|
||||
onMouseDown={ev => {
|
||||
if (!disableSlider) {
|
||||
setSliding(true)
|
||||
setStartX(ev.clientX)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* <PaperAirplaneIcon
|
||||
className="h-7 dark:text-gray-900 translate-x-0.5"
|
||||
style={{
|
||||
transform: `translateX(var(--tw-translate-x)) rotate(${
|
||||
|
@ -140,26 +145,27 @@ const Send: NextPage = () => {
|
|||
}deg)`,
|
||||
}}
|
||||
/> */}
|
||||
<span
|
||||
className="dark:text-gray-900 text-purple-50 text-3xl font-medium flex justify-center select-none"
|
||||
style={{
|
||||
transform: `scale3d(${1 + 0.4 * sliderPercentage}, ${
|
||||
1 + 0.4 * sliderPercentage
|
||||
}, 1)`,
|
||||
}}
|
||||
>
|
||||
Ӿ
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
className="dark:text-gray-900 text-purple-50 text-3xl font-medium flex justify-center select-none"
|
||||
style={{
|
||||
transform: `scale3d(${1 + 0.4 * sliderPercentage}, ${
|
||||
1 + 0.4 * sliderPercentage
|
||||
}, 1)`,
|
||||
}}
|
||||
className="absolute dark:text-purple-50 text-purple-400 text-2xl left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 select-none transition-colors"
|
||||
style={{ opacity: 0.7 - 0.7 * sliderPercentage, zIndex: -10 }}
|
||||
>
|
||||
Ӿ
|
||||
slide to send
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
className="absolute dark:text-purple-50 text-purple-400 text-2xl left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 select-none transition-colors"
|
||||
style={{ opacity: 0.7 - 0.7 * sliderPercentage, zIndex: -10 }}
|
||||
>
|
||||
slide to send
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { PaperAirplaneIcon } from '@heroicons/react/solid'
|
||||
import clsx from 'clsx'
|
||||
import type { NextPage } from 'next'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
|
@ -26,32 +27,37 @@ const ReadQrCode: NextPage = () => {
|
|||
const [address, setAddress] = useState('')
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full min-h-0 gap-8 pb-4">
|
||||
<span className="flex items-center gap-2">
|
||||
<PaperAirplaneIcon className=" dark:text-purple-50 h-7 xs:h-10 text-gray-900 rotate-[30deg] transition-colors translate-x-1" />
|
||||
<h1 className="text-3xl sm:text-5xl">send</h1>
|
||||
</span>
|
||||
<video
|
||||
className={clsx('rounded flex-1 shadow-md min-h-0', {
|
||||
hidden: !videoLive,
|
||||
})}
|
||||
ref={videoRef}
|
||||
/>
|
||||
{!videoLive && (
|
||||
<div className="w-full flex-1 h-64 rounded dark:bg-gray-800 bg-purple-50 animate-pulse"></div>
|
||||
)}
|
||||
<div className="flex flex-col justify-self-end items-center gap-2 text-gray-900 dark:text-purple-50">
|
||||
<span className="text-2xl">or</span>
|
||||
<form
|
||||
onSubmit={e => {
|
||||
e.preventDefault()
|
||||
if (address !== '') push(`/send?address=${address}`)
|
||||
}}
|
||||
>
|
||||
<AddressInput value={address} onChange={setAddress} />
|
||||
</form>
|
||||
<>
|
||||
<Head>
|
||||
<title>zep⚡️ - read qrcode</title>
|
||||
</Head>
|
||||
<div className="flex flex-col h-full min-h-0 gap-8 pb-4">
|
||||
<span className="flex items-center gap-2">
|
||||
<PaperAirplaneIcon className=" dark:text-purple-50 h-7 xs:h-10 text-gray-900 rotate-[30deg] transition-colors translate-x-1" />
|
||||
<h1 className="text-3xl sm:text-5xl">send</h1>
|
||||
</span>
|
||||
<video
|
||||
className={clsx('rounded flex-1 shadow-md min-h-0', {
|
||||
hidden: !videoLive,
|
||||
})}
|
||||
ref={videoRef}
|
||||
/>
|
||||
{!videoLive && (
|
||||
<div className="w-full flex-1 h-64 rounded dark:bg-gray-800 bg-purple-50 animate-pulse"></div>
|
||||
)}
|
||||
<div className="flex flex-col justify-self-end items-center gap-2 text-gray-900 dark:text-purple-50">
|
||||
<span className="text-2xl">or</span>
|
||||
<form
|
||||
onSubmit={e => {
|
||||
e.preventDefault()
|
||||
if (address !== '') push(`/send?address=${address}`)
|
||||
}}
|
||||
>
|
||||
<AddressInput value={address} onChange={setAddress} />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +1,29 @@
|
|||
import type { NextPage } from 'next'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
const Done: NextPage = () => {
|
||||
const { push } = useRouter()
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-start h-full px-4 text-center gap-2 text-gray-900 transition-colors dark:text-purple-50">
|
||||
<h1 className="font-extrabold text-9xl">3</h1>
|
||||
<p className="text-3xl">you're done!</p>
|
||||
<p className="mb-5 text-xl">
|
||||
all the buttons are now enabled and you can start using <b>zep</b> and{' '}
|
||||
<b>nano</b>!
|
||||
</p>
|
||||
<button
|
||||
className="px-5 py-2 text-xl font-bold bg-purple-400 transition-colors text-purple-50 rounded dark:bg-gray-800"
|
||||
onClick={() => push('/dashboard')}
|
||||
>
|
||||
go!
|
||||
</button>
|
||||
</div>
|
||||
<>
|
||||
<Head>
|
||||
<title>zep⚡️ - welcome</title>
|
||||
</Head>
|
||||
<div className="flex flex-col items-center justify-start h-full px-4 text-center gap-2 text-gray-900 transition-colors dark:text-purple-50">
|
||||
<h1 className="font-extrabold text-9xl">3</h1>
|
||||
<p className="text-3xl">you're done!</p>
|
||||
<p className="mb-5 text-xl">
|
||||
all the buttons are now enabled and you can start using <b>zep</b> and{' '}
|
||||
<b>nano</b>!
|
||||
</p>
|
||||
<Link href="/dashboard">
|
||||
<a className="px-5 py-2 text-xl font-bold bg-purple-400 transition-colors text-purple-50 rounded dark:bg-gray-800">
|
||||
go! <span className="hidden">to your dashboard</span>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,45 +1,51 @@
|
|||
import type { NextPage } from 'next'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
const Welcome: NextPage = () => {
|
||||
return (
|
||||
<div className="flex flex-col justify-center w-full h-full text-center text-gray-900 transition-colors dark:text-purple-50">
|
||||
<h1 className="mb-3 text-5xl font-extrabold">hey!</h1>
|
||||
<p className="mb-3 text-xl font-medium">
|
||||
do you already have a
|
||||
<br />
|
||||
<b>nano</b> passphrase?
|
||||
</p>
|
||||
<>
|
||||
<Head>
|
||||
<title>zep⚡️ - welcome</title>
|
||||
</Head>
|
||||
<div className="flex flex-col justify-center w-full h-full text-center text-gray-900 transition-colors dark:text-purple-50">
|
||||
<h1 className="mb-3 text-5xl font-extrabold">hey!</h1>
|
||||
<p className="mb-3 text-xl font-medium">
|
||||
do you already have a
|
||||
<br />
|
||||
<b>nano</b> passphrase?
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col justify-center w-full gap-3 mb-6">
|
||||
<Link href="/welcome/seedOrPassphrase">
|
||||
<a className="px-5 py-2 text-lg font-bold text-purple-50 transition-all rounded shadow-lg bg-purple-400 dark:bg-gray-800 dark:text-purple-50 hover:bg-purple-300 dark:hover:bg-gray-800 hover:shadow-md active:shadow">
|
||||
i have a passphrase/seed
|
||||
</a>
|
||||
</Link>
|
||||
<Link href="/welcome/new">
|
||||
<a className="px-5 py-2 text-lg font-bold text-purple-50 transition-all rounded shadow-lg bg-purple-400 dark:bg-gray-800 dark:text-purple-50 hover:bg-purple-300 dark:hover:bg-gray-800 hover:shadow-md active:shadow">
|
||||
what's a passphrase?
|
||||
</a>
|
||||
</Link>
|
||||
<div className="flex flex-col justify-center w-full gap-3 mb-6">
|
||||
<Link href="/welcome/seedOrPassphrase">
|
||||
<a className="px-5 py-2 text-lg font-bold text-purple-50 transition-all rounded shadow-lg bg-purple-400 dark:bg-gray-800 dark:text-purple-50 hover:bg-purple-300 dark:hover:bg-gray-800 hover:shadow-md active:shadow">
|
||||
i have a passphrase/seed
|
||||
</a>
|
||||
</Link>
|
||||
<Link href="/welcome/new">
|
||||
<a className="px-5 py-2 text-lg font-bold text-purple-50 transition-all rounded shadow-lg bg-purple-400 dark:bg-gray-800 dark:text-purple-50 hover:bg-purple-300 dark:hover:bg-gray-800 hover:shadow-md active:shadow">
|
||||
what's a passphrase?
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
<aside className="mb-3 text-xs">
|
||||
your passphrase will{' '}
|
||||
<b className="font-extrabold">
|
||||
<em>never</em>
|
||||
</b>{' '}
|
||||
leave your device,
|
||||
<br />
|
||||
and will only be decrypted for a few moments to send nano
|
||||
</aside>
|
||||
<p className="text-xs">
|
||||
<em>
|
||||
psst: you can already see all the cool buttons below, but
|
||||
they're all disabled for now
|
||||
</em>
|
||||
</p>
|
||||
</div>
|
||||
<aside className="mb-3 text-xs">
|
||||
your passphrase will{' '}
|
||||
<b className="font-extrabold">
|
||||
<em>never</em>
|
||||
</b>{' '}
|
||||
leave your device,
|
||||
<br />
|
||||
and will only be decrypted for a few moments to send nano
|
||||
</aside>
|
||||
<p className="text-xs">
|
||||
<em>
|
||||
psst: you can already see all the cool buttons below, but they're
|
||||
all disabled for now
|
||||
</em>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { NextPage } from 'next'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
|
@ -24,48 +25,54 @@ const New: NextPage = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-start h-full px-4 text-center gap-2 text-gray-900 dark:text-purple-50 transition-colors">
|
||||
<h1 className="font-extrabold text-9xl">{!storing ? 1 : 2}</h1>
|
||||
{!storing ? (
|
||||
<>
|
||||
<p className="text-lg">
|
||||
generate a <b>passphrase</b> and copy it to your clipboard
|
||||
</p>
|
||||
<button
|
||||
className="px-5 py-2 text-xl font-bold bg-purple-400 transition-colors text-purple-50 rounded dark:bg-gray-800"
|
||||
onClick={storeSeedLazy}
|
||||
>
|
||||
generate passphrase
|
||||
</button>
|
||||
<p className="text-xl">
|
||||
<b>
|
||||
store this passphrase securely: you'll need it in case
|
||||
something happens to this device
|
||||
</b>
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="text-lg">
|
||||
store the <b>passphrase</b> securely in zep
|
||||
</p>
|
||||
<button
|
||||
className="px-5 py-2 text-xl font-bold bg-purple-400 transition-colors text-purple-50 rounded dark:bg-gray-800"
|
||||
onClick={onStoreClick}
|
||||
>
|
||||
store passphrase
|
||||
</button>
|
||||
<aside className="text-xs">
|
||||
again, your passphrase will{' '}
|
||||
<b className="font-extrabold">
|
||||
<em>never</em>
|
||||
</b>{' '}
|
||||
leave your device, and will only be decrypted for a brief moment on
|
||||
each transaction
|
||||
</aside>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<>
|
||||
<Head>
|
||||
<title>zep⚡️ - welcome</title>
|
||||
</Head>
|
||||
<div className="flex flex-col items-center justify-start h-full px-4 text-center gap-2 text-gray-900 dark:text-purple-50 transition-colors">
|
||||
<h1 className="font-extrabold text-9xl">{!storing ? 1 : 2}</h1>
|
||||
{!storing ? (
|
||||
<>
|
||||
<p className="text-lg">
|
||||
generate a <b>passphrase</b> and copy it to your clipboard
|
||||
</p>
|
||||
<button
|
||||
className="px-5 py-2 text-xl font-bold bg-purple-400 transition-colors text-purple-50 rounded dark:bg-gray-800"
|
||||
onClick={storeSeedLazy}
|
||||
>
|
||||
generate passphrase
|
||||
</button>
|
||||
<p className="text-xl">
|
||||
<b>
|
||||
store this passphrase securely: you'll need it in case
|
||||
something happens to this device
|
||||
</b>
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="text-lg">
|
||||
store the <b>passphrase</b> securely in zep
|
||||
</p>
|
||||
<button
|
||||
aria-label="Store passphrase securely on device storage"
|
||||
className="px-5 py-2 text-xl font-bold bg-purple-400 transition-colors text-purple-50 rounded dark:bg-gray-800"
|
||||
onClick={onStoreClick}
|
||||
>
|
||||
store passphrase
|
||||
</button>
|
||||
<aside className="text-xs">
|
||||
again, your passphrase will{' '}
|
||||
<b className="font-extrabold">
|
||||
<em>never</em>
|
||||
</b>{' '}
|
||||
leave your device, and will only be decrypted for a brief moment
|
||||
on each transaction
|
||||
</aside>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import clsx from 'clsx'
|
||||
import { wallet } from 'nanocurrency-web'
|
||||
import type { NextPage } from 'next'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
|
@ -93,106 +94,117 @@ const SeedOrPassphrase: NextPage = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-start h-full px-4 text-center gap-6 text-gray-900 dark:text-purple-50 transition-colors">
|
||||
{registering ? (
|
||||
<>
|
||||
<h1 className="font-extrabold text-3xl">register your biometrics</h1>
|
||||
<button
|
||||
className="px-5 py-2 text-xl font-bold bg-purple-400 transition-colors text-purple-50 rounded dark:bg-gray-800"
|
||||
onClick={onRegisterClick}
|
||||
>
|
||||
register biometrics
|
||||
</button>
|
||||
<p>
|
||||
<em>this will be used to encrypt your seed inside your device</em>
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h1 className="font-extrabold text-3xl">input your secret</h1>
|
||||
<p className="text-lg">
|
||||
store the <b>secret</b> securely in zep
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
<>
|
||||
<Head>
|
||||
<title>zep⚡️ - welcome</title>
|
||||
</Head>
|
||||
<div className="flex flex-col items-center justify-start h-full px-4 text-center gap-6 text-gray-900 dark:text-purple-50 transition-colors">
|
||||
{registering ? (
|
||||
<>
|
||||
<h1 className="font-extrabold text-3xl">
|
||||
register your biometrics
|
||||
</h1>
|
||||
<button
|
||||
className={clsx(
|
||||
'px-4 py-1 text-lg font-bold bg-purple-400 transition-all text-purple-50 rounded dark:bg-gray-800',
|
||||
isPassphrase ? 'scale-110' : 'opacity-80'
|
||||
)}
|
||||
onClick={() => setIsPassphrase(true)}
|
||||
aria-label="Register biometrics"
|
||||
className="px-5 py-2 text-xl font-bold bg-purple-400 transition-colors text-purple-50 rounded dark:bg-gray-800"
|
||||
onClick={onRegisterClick}
|
||||
>
|
||||
passphrase
|
||||
register biometrics
|
||||
</button>
|
||||
<button
|
||||
className={clsx(
|
||||
'px-4 py-1 text-lg font-bold bg-purple-400 transition-all text-purple-50 rounded dark:bg-gray-800',
|
||||
!isPassphrase ? 'scale-110' : 'opacity-80'
|
||||
)}
|
||||
onClick={() => setIsPassphrase(false)}
|
||||
>
|
||||
seed
|
||||
</button>
|
||||
</div>
|
||||
{!isPassphrase ? (
|
||||
<SeedInput value={seedInput} onChange={setSeedInput} />
|
||||
) : (
|
||||
<ol className="flex w-full gap-2 overflow-x-auto">
|
||||
<li className="min-w-[150px]">
|
||||
<MnemonicInput
|
||||
onPaste={onPaste}
|
||||
number={1}
|
||||
value={passphraseInputs[0] ?? ''}
|
||||
onChange={value => {
|
||||
console.log(value)
|
||||
if (value.split(' ').length !== 24)
|
||||
setpassphraseInputs(prev => {
|
||||
const newInputs = [...prev]
|
||||
newInputs[0] = value
|
||||
return newInputs
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
{new Array(24).fill(0, 0, 23).map((_, idx) => (
|
||||
<li key={idx} className="min-w-[150px]">
|
||||
<p>
|
||||
<em>this will be used to encrypt your seed inside your device</em>
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h1 className="font-extrabold text-3xl">input your secret</h1>
|
||||
<p className="text-lg">
|
||||
store the <b>secret</b> securely in zep
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
<button
|
||||
aria-label="Use passphrase"
|
||||
className={clsx(
|
||||
'px-4 py-1 text-lg font-bold bg-purple-400 transition-all text-purple-50 rounded dark:bg-gray-800',
|
||||
isPassphrase ? 'scale-110' : 'opacity-80'
|
||||
)}
|
||||
onClick={() => setIsPassphrase(true)}
|
||||
>
|
||||
passphrase
|
||||
</button>
|
||||
<button
|
||||
aria-label="Use seed"
|
||||
className={clsx(
|
||||
'px-4 py-1 text-lg font-bold bg-purple-400 transition-all text-purple-50 rounded dark:bg-gray-800',
|
||||
!isPassphrase ? 'scale-110' : 'opacity-80'
|
||||
)}
|
||||
onClick={() => setIsPassphrase(false)}
|
||||
>
|
||||
seed
|
||||
</button>
|
||||
</div>
|
||||
{!isPassphrase ? (
|
||||
<SeedInput value={seedInput} onChange={setSeedInput} />
|
||||
) : (
|
||||
<ol className="flex w-full gap-2 overflow-x-auto">
|
||||
<li className="min-w-[150px]">
|
||||
<MnemonicInput
|
||||
onPaste={onPaste}
|
||||
number={idx + 2}
|
||||
value={passphraseInputs[idx] ?? ''}
|
||||
number={1}
|
||||
value={passphraseInputs[0] ?? ''}
|
||||
onChange={value => {
|
||||
console.log(value)
|
||||
if (value.split(' ').length !== 24)
|
||||
setpassphraseInputs(prev => {
|
||||
const newInputs = [...prev]
|
||||
newInputs[idx] = value
|
||||
newInputs[0] = value
|
||||
return newInputs
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
)}
|
||||
<button
|
||||
className="px-5 py-2 text-xl font-bold bg-purple-400 transition-all text-purple-50 rounded dark:bg-gray-800 disabled:cursor-default disabled:opacity-60"
|
||||
disabled={
|
||||
(seedInput === '' && !isPassphrase) ||
|
||||
(passphraseInputs.length !== 24 && isPassphrase)
|
||||
}
|
||||
onClick={onStoreClick}
|
||||
>
|
||||
store passphrase
|
||||
</button>
|
||||
<aside className="text-xs">
|
||||
again, your passphrase will{' '}
|
||||
<b className="font-extrabold">
|
||||
<em>never</em>
|
||||
</b>{' '}
|
||||
leave your device, and will only be decrypted for a brief moment on
|
||||
each transaction
|
||||
</aside>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{new Array(24).fill(0, 0, 23).map((_, idx) => (
|
||||
<li key={idx} className="min-w-[150px]">
|
||||
<MnemonicInput
|
||||
onPaste={onPaste}
|
||||
number={idx + 2}
|
||||
value={passphraseInputs[idx] ?? ''}
|
||||
onChange={value => {
|
||||
if (value.split(' ').length !== 24)
|
||||
setpassphraseInputs(prev => {
|
||||
const newInputs = [...prev]
|
||||
newInputs[idx] = value
|
||||
return newInputs
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
)}
|
||||
<button
|
||||
aria-label="Store passphrase securely on device storage"
|
||||
className="px-5 py-2 text-xl font-bold bg-purple-400 transition-all text-purple-50 rounded dark:bg-gray-800 disabled:cursor-default disabled:opacity-60"
|
||||
disabled={
|
||||
(seedInput === '' && !isPassphrase) ||
|
||||
(passphraseInputs.length !== 24 && isPassphrase)
|
||||
}
|
||||
onClick={onStoreClick}
|
||||
>
|
||||
store passphrase
|
||||
</button>
|
||||
<aside className="text-xs">
|
||||
again, your passphrase will{' '}
|
||||
<b className="font-extrabold">
|
||||
<em>never</em>
|
||||
</b>{' '}
|
||||
leave your device, and will only be decrypted for a brief moment
|
||||
on each transaction
|
||||
</aside>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Reference in a new issue