ghost-lite/src/containers/AddressBook.tsx
Uncle Fatso af7ca0da51
update to latest CASPER chain version
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2025-08-13 19:44:24 +03:00

162 lines
5.9 KiB
TypeScript

import React, { useEffect, useState } from "react"
import { CirclePlus, Trash, Send } from "lucide-react"
import { useNavigate } from "react-router-dom"
import { ss58Decode } from "@polkadot-labs/hdkd-helpers"
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "../components/ui/accordion"
import { Input } from "../components/ui/input"
import { Button } from "../components/ui/button"
type AddressBookRecord = {
name: string
address: string
}
interface AddressRecordProps {
name: string
address: string
removeRecord: ({ name }: { name: string}) => void
openTransfer: ({ address }: { address: string}) => void
}
const AddressRecord: React.FC<AddressRecordProps> = ({ name, address, removeRecord, openTransfer }) => {
return (
<AccordionItem className="bg-muted rounded px-4" value={name}>
<AccordionTrigger className="w-full hover:no-underline">
<div className="w-[90%] sm:text-base text-xs overflow-hidden whitespace-nowrap overflow-ellipsis text-left">
{name}
</div>
</AccordionTrigger>
<AccordionContent className="flex flex-row gap-4">
<Button
variant="secondary"
size="full"
className="flex-1 text-accent flex text-xs sm:flex hidden"
onClick={() => openTransfer({ address })}
>
<Send className="w-4 h-4 mr-1" />
</Button>
<Input
readOnly
value={address}
aria-label={name}
type="text"
className="sm:w-[300px] flex-6"
/>
<Button
variant="destructive"
size="full"
className="flex-2 text-accent text-xs sm:flex hidden"
onClick={() => removeRecord({ name })}
>
<Trash className="w-4 h-4 mr-2" />
Remove
</Button>
</AccordionContent>
</AccordionItem>
)
}
export const AddressBook = () => {
const navigate = useNavigate()
const [name, setName] = useState<string>("")
const [address, setAddress] = useState<string>("")
const [error, setError] = useState<string | undefined>(undefined)
const [addressBook, setAddressBook] = useState<AddressBookRecord[]>(
JSON.parse(localStorage.getItem('addressBook') ?? '[]') || []
)
useEffect(() => {
localStorage.setItem('addressBook', JSON.stringify(addressBook))
setAddress("")
setName("")
setError(undefined)
}, [addressBook])
const addRecord = ({ name, address }: AddressBookRecord) => {
if (addressBook.find(record => record.name === name)) {
setError("Name already exist in the address book")
return
}
if (addressBook.find(record => record.address === address)) {
setError("Address already exist in the address book")
return
}
try {
const [, prefix] = ss58Decode(address)
if (prefix !== 1995 && prefix !== 1996) {
throw new Error("bad prefix")
}
} catch (e) {
setError("Incorrect Ghost or Casper address provided")
return
}
const newRecord = { name, address }
setAddressBook([...addressBook, newRecord])
}
const removeRecord = ({ name }: { name: string }) => {
const updatedAddressBook = addressBook.filter((record: AddressBookRecord) =>
record.name !== name
)
setAddressBook(updatedAddressBook)
}
const openTransfer = ({ address }: { address: string }) => {
const queryString = new URLSearchParams({ address }).toString()
navigate(`/transactions?${queryString}`)
}
return (
<Accordion type="multiple" className="sm:w-[500px] w-[85%] h-fit flex flex-col flex-1 gap-4 justify-center self-center py-2">
{addressBook.map(({ name, address }: AddressBookRecord, idx: number) => (
<AddressRecord
key={idx}
name={name}
address={address}
removeRecord={removeRecord}
openTransfer={openTransfer}
/>
))}
<div className="bg-muted flex flex-col sm:gap-2 gap-4 w-full rounded px-4 py-6">
<div className="flex sm:flex-row flex-col gap-4">
<Input
value={name}
onChange={e => setName(e.target.value)}
aria-label="New Name"
type="text"
className="w-full sm:text-base text-xs sm:placeholder:text-base placeholder:text-xs"
placeholder="Contact Name"
/>
<Input
value={address}
onChange={e => setAddress(e.target.value)}
aria-label="New Address"
type="text"
className="w-full sm:text-base text-xs sm:placeholder:text-base placeholder:text-xs"
placeholder="Contact Address"
/>
</div>
<Button
variant="secondary"
size="full"
className="flex flex-row justify-center items-center"
onClick={() => addRecord({ name, address })}
>
<CirclePlus className="w-4 h-4 mr-2" />
Add Contact
</Button>
{error && (
<div className="text-xs text-destructive">{error}</div>
)}
</div>
</Accordion>
)
}