feat: lots of stuff

This commit is contained in:
Filipe Medeiros 2021-12-01 21:09:28 +00:00
parent aeeb56d605
commit ec32e26cc0
11 changed files with 146 additions and 44 deletions

View file

@ -0,0 +1,30 @@
import { FC } from 'react'
// delete if not needed
export interface Props {
value: string
onChange: (value: string) => void
}
const AddressInput: FC<Props> = ({ value, onChange }) => {
return (
<div className="flex items-center w-full gap-3 text-2xl rounded transition-colors dark:bg-gray-800 bg-purple-50 focus-within:bg-purple-100 border-2 border-purple-200 dark:border-gray-700 py-2 px-4 overflow-hidden dark:focus-within:bg-gray-700">
<label htmlFor="xno-address">@</label>
<input
name="xno-address"
id="xno-address"
maxLength={65}
minLength={65}
className="bg-transparent focus:outline-none w-full"
value={value}
pattern="^(nano|xrb)_[13]{1}[13456789abcdefghijkmnopqrstuwxyz]{59}$"
onChange={({ target: { value, validity } }) => {
console.log(value, validity.patternMismatch)
if (!validity.patternMismatch) onChange(value)
}}
/>
</div>
)
}
export default AddressInput

View file

@ -41,22 +41,20 @@ const Balance: FC<Props> = ({ className }) => {
const xnoBalanceDisplay = rawToNanoDisplay(account?.balance ?? '0')
return (
<div
className={clsx(
'dark:text-purple-50 transition-colors text-gray-900 flex',
className
)}
>
<div className={clsx('dark:text-purple-50 text-gray-900 flex', className)}>
<div
onClick={() =>
setPreference('showCurrencyDash', nextShowCurrency(showCurrencyDash))
}
className="hover:cursor-pointer active:translate-y-0.5"
className="hover:cursor-pointer"
>
<h3 className="text-4xl">
Ӿ
<span className="transition-colors">Ӿ</span>
<span
className={clsx(showXnoBalance ? 'font-medium' : 'font-semibold')}
className={clsx(
'transition-colors',
showXnoBalance ? 'font-medium' : 'font-semibold'
)}
>
{showXnoBalance ? (
account?.balance === null ? (
@ -76,7 +74,7 @@ const Balance: FC<Props> = ({ className }) => {
</span>
</h3>
{showFiatBalance && (
<h4 className="text-xl">
<h4 className="text-xl transition-colors">
$ {(Number(xnoBalance) * xnoPrice).toFixed(2)}
</h4>
)}

View file

@ -117,7 +117,7 @@ const BottomMenu: FC<Props> = ({ className }) => {
<div className="h-16 p-1 border-t-2 border-b-2 border-purple-400 shadow-lg">
<QrcodeIcon className="h-full text-gray-900 dark:text-purple-100 transition-colors" />
</div>
<Link href={isWelcoming ? '#' : '/send/qr'}>
<Link href={isWelcoming ? '#' : '/send/qrOrAddress'}>
<a
className={clsx(
'bg-purple-400 transition-colors px-1 h-16 w-10 hover:bg-purple-400 disabled:hover:bg-purple-400 shadow-md disabled:cursor-default',

View file

@ -1,11 +1,13 @@
import { ChevronUpIcon, ClockIcon } from '@heroicons/react/outline'
import { DownloadIcon, UploadIcon } from '@heroicons/react/solid'
import clsx from 'clsx'
import { FC, useState } from 'react'
import { Unit, convert } from 'nanocurrency'
import { FC, useEffect, useState } from 'react'
import useAccountHistory from '../lib/hooks/useAccountHistory'
import useAccountReceivable from '../lib/hooks/useAccountReceivable'
import useReceiveNano from '../lib/hooks/useReceiveNano'
import showNotification from '../lib/showNotification'
import rawToNanoDisplay from '../lib/xno/rawToNanoDisplay'
export interface Props {}
@ -20,6 +22,7 @@ const RecentTransactions: FC<Props> = () => {
hasMore,
loading: loadingHistory,
hasHistory,
revalidate: refetchHistory,
} = useAccountHistory()
const hasReceivable =
@ -33,11 +36,34 @@ const RecentTransactions: FC<Props> = () => {
const initialLoading = loadingHistory && accountHistory === undefined
const onIncomingClick = async (receivable: {
hash: string
amount: string
from: string
timestamp: string
}) => {
await receive(receivable.hash, receivable.amount)
onBlockReceived(receivable.hash)
refetchHistory()
showNotification({
title: 'received!',
body: `received Ӿ${convert(receivable.amount, {
from: Unit.raw,
to: Unit.Nano,
})} from ${receivable.from}`,
tag: 'received',
})
}
return (
<>
{hasReceivable && (
<section className="flex flex-col w-full gap-3">
<div className="flex items-center justify-between gap-1">
<div
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">
incoming
</h2>
@ -46,7 +72,6 @@ const RecentTransactions: FC<Props> = () => {
{receivableBlocks.length}
</span>
<ChevronUpIcon
onClick={() => setReceivablesExpanded(prev => !prev)}
className={clsx(
'h-8 transition-transform-child origin-center-child',
{
@ -68,10 +93,7 @@ const RecentTransactions: FC<Props> = () => {
>
<button
className="contents"
onClick={async () => {
await receive(receivable.hash, receivable.amount)
onBlockReceived(receivable.hash)
}}
onClick={() => onIncomingClick(receivable)}
>
<ClockIcon className="flex-shrink-0 w-6 text-yellow-400" />
@ -165,7 +187,7 @@ const RecentTransactions: FC<Props> = () => {
)}
</ol>
) : (
<div className="pt-8 text-center text-gray-900 dark:text-purple-50">
<div className="pt-8 text-center text-xl text-gray-900 dark:text-purple-50">
<p className="pb-4">no transactions yet...</p>
<p>
get your first nano

View file

@ -33,7 +33,7 @@ const XnoInput: FC<Props> = ({ value, onChange }) => {
return (
<div className="flex items-center gap-3 text-2xl rounded transition-colors dark:bg-gray-800 bg-purple-50 focus-within:bg-purple-100 border-2 border-purple-200 dark:border-gray-700 py-2 px-4 w-48 overflow-hidden dark:focus-within:bg-gray-700">
<label htmlFor="xnoToSend">Ӿ</label>
<label htmlFor="xno-amount">Ӿ</label>
<input
name="xno-amount"
id="xno-amount"

View file

@ -1,4 +1,3 @@
import { useCallback, useEffect } from 'react'
import useSWRInfinite from 'swr/infinite'
import { useCurrentAccount } from '../context/accountContext'
@ -23,6 +22,7 @@ const useAccountHistory = (pageSize = 20) => {
size,
setSize,
isValidating,
mutate,
} = useSWRInfinite<AccountHistoryResponse>(getKey, (key: string) =>
fetchAccountHistory(
account!.address,
@ -44,6 +44,7 @@ const useAccountHistory = (pageSize = 20) => {
hasMore,
loading: isValidating,
hasHistory,
revalidate: mutate,
}
}

View file

@ -1,7 +1,9 @@
import { Unit, convert } from 'nanocurrency'
import { useCallback } from 'react'
import useSWR from 'swr'
import useSWR, { mutate } from 'swr'
import { useCurrentAccount } from '../context/accountContext'
import showNotification from '../showNotification'
import type { ConfirmationMessage } from '../types'
import fetchAccountReceivable from '../xno/fetchAccountReceivable'
import fetchBlocksInfo from '../xno/fetchBlocksInfo'
@ -43,11 +45,12 @@ const useAccountReceivable = () => {
const onBlockReceived = useCallback(
(hash: string) => {
mutateReceivable(current => {
if (current === undefined || account === undefined) return current
if (current === undefined) return current
return current.filter(block => block.hash !== hash)
})
mutate('history')
},
[mutateReceivable, account]
[mutateReceivable]
)
const addReceivable = useCallback(
@ -61,7 +64,7 @@ const useAccountReceivable = () => {
source: string
}) => {
mutateReceivable(async current => {
if (current === undefined || account === undefined) return current
if (current === undefined) return current
const blocksInfo = await fetchBlocksInfo([hash])
return [
...current,
@ -74,16 +77,28 @@ const useAccountReceivable = () => {
]
})
},
[mutateReceivable, account]
[mutateReceivable]
)
const onNewReceivable = useCallback(
(confirmation: ConfirmationMessage) =>
(confirmation: ConfirmationMessage) => {
addReceivable({
hash: confirmation.message.hash,
amount: confirmation.message.amount,
source: confirmation.message.account,
}),
})
showNotification({
title: 'incoming!',
body: `new incoming transaction: Ӿ${convert(
confirmation.message.amount,
{
from: Unit.raw,
to: Unit.Nano,
}
)} from ${confirmation.message.account}`,
tag: 'received',
})
},
[addReceivable]
)

15
lib/showNotification.ts Normal file
View file

@ -0,0 +1,15 @@
const showNotification = async (params: {
title: string
body: string
tag?: string
}) => {
const sw = await navigator.serviceWorker.getRegistration()
sw?.showNotification(params.title, {
body: params.body,
renotify: params.tag !== undefined,
tag: params.tag,
icon: '/images/icon-small.png',
})
}
export default showNotification

View file

@ -8,6 +8,7 @@ import { useCallback, useEffect, useState } from 'react'
import XnoInput from '../../components/XnoInput'
import useSendNano from '../../lib/hooks/useSendNano'
import showNotification from '../../lib/showNotification'
const Send: NextPage = () => {
const { query, push } = useRouter()
@ -34,15 +35,17 @@ const Send: NextPage = () => {
useEffect(() => {
if (sliderPercentage === 1) {
const sendNano = async () => {
try {
await send(address as string, amount as string)
navigator.serviceWorker
.getRegistration()
.then(sw => sw?.showNotification('sent!'))
push('/dashboard')
} catch {
backToBase()
}
backToBase()
await send(address as string, amount as string)
showNotification({
title: 'sent!',
body: `sent Ӿ${convert(amount as string, {
from: Unit.raw,
to: Unit.Nano,
})} to ${address}`,
tag: 'send',
})
push('/dashboard')
}
sendNano()
}
@ -59,7 +62,7 @@ const Send: NextPage = () => {
return (
<div className="flex flex-col h-full 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] translate-x-1" />
<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

View file

@ -2,8 +2,10 @@ import { PaperAirplaneIcon } from '@heroicons/react/solid'
import clsx from 'clsx'
import type { NextPage } from 'next'
import { useRouter } from 'next/router'
import { useCallback } from 'react'
import { useCallback, useState } from 'react'
import AddressInput from '../../components/AddressInput'
import XnoInput from '../../components/XnoInput'
import useReadQrFromVideo from '../../lib/hooks/useReadQrFromVideo'
import isTxnUrl from '../../lib/xno/isTxnUrl'
import txnUrlToParts from '../../lib/xno/txnUrlToParts'
@ -21,19 +23,34 @@ const ReadQrCode: NextPage = () => {
)
const { videoLive, videoRef } = useReadQrFromVideo(onQrCodeRead)
const [address, setAddress] = useState('')
return (
<div className="flex flex-col h-full min-h-0 gap-8">
<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] translate-x-1" />
<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 shadow-md min-h-0', { hidden: !videoLive })}
className={clsx('rounded flex-1 shadow-md min-h-0', {
hidden: !videoLive,
})}
ref={videoRef}
/>
{!videoLive && (
<div className="w-full h-64 rounded dark:bg-gray-800 bg-purple-50 animate-pulse"></div>
<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>
)
}

View file

@ -5,5 +5,6 @@
"75edd150e09bada0f5c68fab14c0b385239cc396ef6600029dea0f7b66be63fa6a7d8ebe30aaff66634f583c4e68067c0de136e27dddc96be76d2ac5bb14ef71",
"b6203f57196d244f7eb6971cef41a4cd4ce961e81ae8046f4f8274a4057cb77b4ef97ad3c25e14b50598c1243fa3253644b44e89c8648fd9c18b3703d36372bb",
"penalty parrot sure holiday target what human sadness crack picnic idle pluck mean salad frog usual grab aspect pottery faith staff crash umbrella lumber",
"6bfddd0bfd00bf13eb17bae35627a2dfdf49cb56027fc26988faff0ab8a20b31efb16d706ceac8e458d4d5c342f2459d24746521a6e6a62a0adb29b323455ed9"
"6bfddd0bfd00bf13eb17bae35627a2dfdf49cb56027fc26988faff0ab8a20b31efb16d706ceac8e458d4d5c342f2459d24746521a6e6a62a0adb29b323455ed9",
"9bbaa2391a256c8d5c318790b86a142d4b13b1dc6d209567cb854e1b956f1c1385b863229a34e970d1f4c0c4a66f7ac7a17b5fd3a21e72716dddec842e0a6727"
]