diff --git a/.nvmrc b/.nvmrc index 790e1105..a81debae 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v20.10.0 +v20.12.2 diff --git a/package.json b/package.json index d38b6d76..cbef26b5 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "test:unit": "turbo run test:unit", "test:e2e:extension": "turbo run test:e2e --filter=@palladxyz/extension", "format": "prettier --write \"**/*.{ts,tsx,md}\" && pnpm lint:fix", + "f": "pnpm format", "cleanup": "turbo run cleanup && rimraf node_modules", "prepare": "husky install", "preinstall": "npx only-allow pnpm" @@ -48,5 +49,5 @@ "typescript": "^5.3.3", "vitest": "^1.1.0" }, - "packageManager": "pnpm@8.12.1" + "packageManager": "pnpm@8.15.6" } diff --git a/packages/features/src/about/routes/about.tsx b/packages/features/src/about/routes/about.tsx new file mode 100644 index 00000000..746c79ed --- /dev/null +++ b/packages/features/src/about/routes/about.tsx @@ -0,0 +1,8 @@ +import { useNavigate } from 'react-router-dom' + +import { AboutView } from '../views/about' + +export const AboutRoute = () => { + const navigate = useNavigate() + return navigate(-1)} /> +} diff --git a/packages/features/src/about/views/About.stories.tsx b/packages/features/src/about/views/about.stories.tsx similarity index 65% rename from packages/features/src/about/views/About.stories.tsx rename to packages/features/src/about/views/about.stories.tsx index ba8300f7..85ee1b9b 100644 --- a/packages/features/src/about/views/About.stories.tsx +++ b/packages/features/src/about/views/about.stories.tsx @@ -2,7 +2,7 @@ import { StoryDefault } from '@ladle/react' import { AboutView } from './about' -export const View = () => +export const View = () => console.log('go back')} /> export default { title: 'Dashboard / About' diff --git a/packages/features/src/about/views/about.tsx b/packages/features/src/about/views/about.tsx index 06a7d8ba..c2d54cf6 100644 --- a/packages/features/src/about/views/about.tsx +++ b/packages/features/src/about/views/about.tsx @@ -1,5 +1,3 @@ -import { useNavigate } from 'react-router-dom' - import Logo from '@/common/assets/logo.svg' import { AppLayout } from '@/components/app-layout' import { MetaField } from '@/components/meta-field' @@ -20,15 +18,15 @@ const AppMeta = [ } ] -export const AboutView = () => { - const navigate = useNavigate() +type AboutViewProps = { + onGoBack: () => void +} + +export const AboutView = ({ onGoBack }: AboutViewProps) => { return (
- navigate(-1) }} - /> +
diff --git a/packages/features/src/address-book/routes/address-book.tsx b/packages/features/src/address-book/routes/address-book.tsx new file mode 100644 index 00000000..98054e3f --- /dev/null +++ b/packages/features/src/address-book/routes/address-book.tsx @@ -0,0 +1,16 @@ +import { useNavigate } from 'react-router-dom' + +import { useAddressBookStore } from '@/common/store/address-book' + +import { AddressBookView } from '../views/address-book' + +export const AddressBookRoute = () => { + const navigate = useNavigate() + const contacts = useAddressBookStore((state) => state.contacts) + return ( + navigate('/contacts/new')} + /> + ) +} diff --git a/packages/features/src/address-book/routes/new-address.tsx b/packages/features/src/address-book/routes/new-address.tsx new file mode 100644 index 00000000..47f09a81 --- /dev/null +++ b/packages/features/src/address-book/routes/new-address.tsx @@ -0,0 +1,8 @@ +import { useNavigate } from 'react-router-dom' + +import { NewAddressView } from '../views/new-address' + +export const NewAddressRoute = () => { + const navigate = useNavigate() + return navigate(-1)} /> +} diff --git a/packages/features/src/address-book/views/address-book.stories.tsx b/packages/features/src/address-book/views/address-book.stories.tsx index 8bb3831f..502a9ece 100644 --- a/packages/features/src/address-book/views/address-book.stories.tsx +++ b/packages/features/src/address-book/views/address-book.stories.tsx @@ -2,7 +2,9 @@ import { StoryDefault } from '@ladle/react' import { AddressBookView } from './address-book' -export const View = () => +export const View = () => ( + console.log('back')} /> +) export default { title: 'Dashboard / Address Book' diff --git a/packages/features/src/address-book/views/address-book.tsx b/packages/features/src/address-book/views/address-book.tsx index 2509fe94..8909518d 100644 --- a/packages/features/src/address-book/views/address-book.tsx +++ b/packages/features/src/address-book/views/address-book.tsx @@ -1,7 +1,6 @@ import { PlusIcon } from 'lucide-react' -import { useNavigate } from 'react-router-dom' -import { useAddressBookStore } from '@/common/store/address-book' +import { Contact } from '@/common/types' import { AppLayout } from '@/components/app-layout' import { ViewHeading } from '@/components/view-heading' @@ -12,9 +11,15 @@ const DonatePallad = { address: 'B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS' } -export const AddressBookView = () => { - const navigate = useNavigate() - const contacts = useAddressBookStore((state) => state.contacts) +type AddressBookViewProps = { + contacts: Contact[] + onAddClicked: () => void +} + +export const AddressBookView = ({ + contacts, + onAddClicked +}: AddressBookViewProps) => { return (
@@ -27,7 +32,7 @@ export const AddressBookView = () => { Add Address
), - onClick: () => navigate('/contacts/new'), + onClick: onAddClicked, testId: 'addressBook__addAddressButton' }} /> diff --git a/packages/features/src/address-book/views/new-address.stories.tsx b/packages/features/src/address-book/views/new-address.stories.tsx index 4f85897f..6e6f04e8 100644 --- a/packages/features/src/address-book/views/new-address.stories.tsx +++ b/packages/features/src/address-book/views/new-address.stories.tsx @@ -1,8 +1,10 @@ -import { StoryDefault } from '@ladle/react/lib/app/exports' +import { StoryDefault } from '@ladle/react' import { NewAddressView } from './new-address' -export const View = () => +export const View = () => ( + console.log('back')} /> +) export default { title: 'Dashboard / Address Book/ New Address' diff --git a/packages/features/src/address-book/views/new-address.tsx b/packages/features/src/address-book/views/new-address.tsx index b7872f25..cabf5a81 100644 --- a/packages/features/src/address-book/views/new-address.tsx +++ b/packages/features/src/address-book/views/new-address.tsx @@ -1,19 +1,17 @@ -import { useNavigate } from 'react-router-dom' - import { AppLayout } from '@/components/app-layout' import { ViewHeading } from '@/components/view-heading' import { NewAddressForm } from '../components/new-address-form' -export const NewAddressView = () => { - const navigate = useNavigate() +type NewAddressViewProps = { + onGoBack: () => void +} + +export const NewAddressView = ({ onGoBack }: NewAddressViewProps) => { return (
- navigate(-1) }} - /> +
diff --git a/packages/features/src/common/hooks/use-account.ts b/packages/features/src/common/hooks/use-account.ts index 8525c2e0..d26a3bb8 100644 --- a/packages/features/src/common/hooks/use-account.ts +++ b/packages/features/src/common/hooks/use-account.ts @@ -20,11 +20,11 @@ export const useAccount = () => { const setVaultStateUninitialized = useAppStore( (state) => state.setVaultStateUninitialized ) + const publicKey = currentWallet.credential.credential?.address as string const fetchWallet = async () => { await _syncWallet() return getAccountsInfo(network, publicKey) // TODO: replace with getBalance } - const publicKey = currentWallet.credential.credential?.address as string const swr = useSWR( publicKey ? [publicKey, 'account', network] : null, async () => await fetchWallet(), @@ -47,8 +47,8 @@ export const useAccount = () => { [publicKey] ) const stakeDelegated = - currentWallet.accountInfo['MINA'].publicKey !== - currentWallet.accountInfo['MINA'].delegate + currentWallet.accountInfo['MINA']?.publicKey !== + currentWallet.accountInfo['MINA']?.delegate const copyWalletAddress = async () => { await navigator.clipboard.writeText(publicKey ?? '') toast({ diff --git a/packages/features/src/common/store/transaction.ts b/packages/features/src/common/store/transaction.ts index 745e11e9..a0dc5b55 100644 --- a/packages/features/src/common/store/transaction.ts +++ b/packages/features/src/common/store/transaction.ts @@ -1,17 +1,16 @@ +import { Mina } from '@palladxyz/mina-core' import { create } from 'zustand' import { OutgoingTransaction } from '../types' -type TxKind = 'transaction' | 'staking' - type TransactionState = { outgoingTransaction: OutgoingTransaction | null - kind: TxKind + kind: Mina.TransactionKind } type TransactionMutators = { set: (outgoingTransaction: OutgoingTransaction) => void - setKind: (kind: TxKind) => void + setKind: (kind: Mina.TransactionKind) => void } type TransactionStore = TransactionState & TransactionMutators @@ -22,7 +21,7 @@ const initialState = { export const useTransactionStore = create()((set) => ({ ...initialState, - kind: 'transaction', + kind: Mina.TransactionKind.PAYMENT, set(outgoingTransaction) { return set({ outgoingTransaction }) }, diff --git a/packages/features/src/common/types.ts b/packages/features/src/common/types.ts index f3d74f1c..897f7bf6 100644 --- a/packages/features/src/common/types.ts +++ b/packages/features/src/common/types.ts @@ -6,11 +6,6 @@ type Epoch = number type ChainId = string type PublicKey = string -export enum TxKind { - STAKE_DELEGATION = 'STAKE_DELEGATION', - PAYMENT = 'PAYMENT' -} - export enum TxSide { INCOMING = 'INCOMING', OUTGOING = 'OUTGOING' diff --git a/packages/features/src/components/app-layout.tsx b/packages/features/src/components/app-layout.tsx index cb133948..e6913d7a 100644 --- a/packages/features/src/components/app-layout.tsx +++ b/packages/features/src/components/app-layout.tsx @@ -1,5 +1,7 @@ import React from 'react' +import { useAccount } from '@/common/hooks/use-account' + import { BottomNavigation } from './bottom-navigation' import { CommandMenu } from './command-menu' @@ -8,6 +10,7 @@ interface AppLayoutProps { } export const AppLayout = ({ children }: AppLayoutProps) => { + const { lockWallet } = useAccount() const [commandMenuOpen, setCommandMenuOpen] = React.useState(false) const openCommandMenu = () => setCommandMenuOpen(true) return ( @@ -15,7 +18,11 @@ export const AppLayout = ({ children }: AppLayoutProps) => { className="flex flex-col flex-1 bg-background gap-4" data-testid="appLayout" > - +
{children}
diff --git a/packages/features/src/components/command-menu.tsx b/packages/features/src/components/command-menu.tsx index 081f5e1f..8865baea 100644 --- a/packages/features/src/components/command-menu.tsx +++ b/packages/features/src/components/command-menu.tsx @@ -22,16 +22,18 @@ import { CommandList } from '@/components/ui/command' -import { useAccount } from '../common/hooks/use-account' - interface CommandMenuProps { open: boolean setOpen: (open: boolean) => void + lockWallet: () => void } -export const CommandMenu = ({ open, setOpen }: CommandMenuProps) => { +export const CommandMenu = ({ + open, + setOpen, + lockWallet +}: CommandMenuProps) => { const navigate = useNavigate() - const { lockWallet } = useAccount() const COMMAND_GROUPS = [ { diff --git a/packages/features/src/lock/routes/unlock-wallet.tsx b/packages/features/src/lock/routes/unlock-wallet.tsx new file mode 100644 index 00000000..74406a61 --- /dev/null +++ b/packages/features/src/lock/routes/unlock-wallet.tsx @@ -0,0 +1,73 @@ +import { zodResolver } from '@hookform/resolvers/zod' +import { + getSecurePersistence, + getSessionPersistence +} from '@palladxyz/persistence' +import { useVault } from '@palladxyz/vault' +import { FormEvent, useEffect, useState } from 'react' +import { useForm } from 'react-hook-form' +import { useNavigate } from 'react-router-dom' +import { z } from 'zod' + +import { passwordSchema } from '@/common/lib/validation' + +import { UnlockWalletView } from '../views/unlock-wallet' + +const formSchema = z.object({ + spendingPassword: passwordSchema +}) + +export const UnlockWalletRoute = () => { + const [restartAlertVisible, setRestartAlertVisible] = useState(false) + const [showPassword, setShowPassword] = useState(false) + const navigate = useNavigate() + const unlockWalletForm = useForm({ + defaultValues: { + spendingPassword: '' + }, + resolver: zodResolver(formSchema) + }) + const onSubmit = async ({ + spendingPassword + }: { + spendingPassword: string + }) => { + await getSessionPersistence().setItem('spendingPassword', spendingPassword) + await useVault.persist.rehydrate() + setTimeout(() => { + unlockWalletForm.setError('spendingPassword', { + type: 'wrongPassword', + message: 'The spending password is wrong' + }) + }, 100) + } + const togglePassword = (event: FormEvent) => { + event.preventDefault() + setShowPassword(!showPassword) + } + useEffect(() => { + const unsub = useVault.persist?.onFinishHydration(async () => { + const authenticated = + (await getSecurePersistence().getItem('foo')) === 'bar' + if (!authenticated) { + await getSessionPersistence().removeItem('spendingPassword') + return unlockWalletForm.setError('spendingPassword', { + type: 'wrongPassword', + message: 'The spending password is wrong' + }) + } + navigate('/dashboard') + }) + return () => unsub?.() + }, []) + return ( + + ) +} diff --git a/packages/features/src/lock/views/unlock-wallet.stories.tsx b/packages/features/src/lock/views/unlock-wallet.stories.tsx index 24e36f53..8bdcb1ec 100644 --- a/packages/features/src/lock/views/unlock-wallet.stories.tsx +++ b/packages/features/src/lock/views/unlock-wallet.stories.tsx @@ -1,8 +1,23 @@ import { StoryDefault } from '@ladle/react' +import { useState } from 'react' +import { useForm } from 'react-hook-form' import { UnlockWalletView } from './unlock-wallet' -export const View = () => +export const View = () => { + const [restartAlertVisible, setRestartAlertVisible] = useState(false) + const form = useForm() + return ( + console.log('submit')} + restartAlertVisible={restartAlertVisible} + setRestartAlertVisible={setRestartAlertVisible} + showPassword={false} + togglePassword={() => console.log('toggle')} + /> + ) +} export default { title: 'Dashboard / Unlock Wallet' diff --git a/packages/features/src/lock/views/unlock-wallet.tsx b/packages/features/src/lock/views/unlock-wallet.tsx index 494ce0df..98d27908 100644 --- a/packages/features/src/lock/views/unlock-wallet.tsx +++ b/packages/features/src/lock/views/unlock-wallet.tsx @@ -1,16 +1,6 @@ -import { zodResolver } from '@hookform/resolvers/zod' -import { - getSecurePersistence, - getSessionPersistence -} from '@palladxyz/persistence' -import { useVault } from '@palladxyz/vault' import { EyeIcon, EyeOffIcon } from 'lucide-react' -import { FormEvent, useEffect, useState } from 'react' -import { useForm } from 'react-hook-form' -import { useNavigate } from 'react-router-dom' -import { z } from 'zod' +import { UseFormReturn } from 'react-hook-form' -import { passwordSchema } from '@/common/lib/validation' import { ButtonArrow } from '@/components/button-arrow' import { FormError } from '@/components/form-error' import { RestartWalletAlert } from '@/components/restart-wallet-alert' @@ -25,139 +15,108 @@ import { import { WizardLayout } from '@/components/wizard-layout' import { cn } from '@/lib/utils' -const formSchema = z.object({ - spendingPassword: passwordSchema -}) +type UnlockWalletData = { + spendingPassword: string +} -export const UnlockWalletView = () => { - const [restartAlertVisible, setRestartAlertVisible] = useState(false) - const [showPassword, setShowPassword] = useState(false) - const navigate = useNavigate() - const { - register, - handleSubmit, - formState: { errors, dirtyFields }, - setError - } = useForm({ - defaultValues: { - spendingPassword: '' - }, - resolver: zodResolver(formSchema) - }) - const onSubmit = async ({ - spendingPassword - }: { - spendingPassword: string - }) => { - await getSessionPersistence().setItem('spendingPassword', spendingPassword) - await useVault.persist.rehydrate() - setTimeout(() => { - setError('spendingPassword', { - type: 'wrongPassword', - message: 'The spending password is wrong' - }) - }, 100) - } - const togglePassword = (event: FormEvent) => { - event.preventDefault() - setShowPassword(!showPassword) - } - useEffect(() => { - const unsub = useVault.persist?.onFinishHydration(async () => { - const authenticated = - (await getSecurePersistence().getItem('foo')) === 'bar' - if (!authenticated) { - await getSessionPersistence().removeItem('spendingPassword') - return setError('spendingPassword', { - type: 'wrongPassword', - message: 'The spending password is wrong' - }) - } - navigate('/dashboard') - }) - return () => unsub?.() - }, []) - return ( - - Unlock - - - } - > - -
- -
void + form: UseFormReturn + onSubmit: (data: UnlockWalletData) => unknown + showPassword: boolean + togglePassword: (event: any) => void +} + +export const UnlockWalletView = ({ + restartAlertVisible, + setRestartAlertVisible, + form, + onSubmit, + showPassword, + togglePassword +}: UnlockWalletViewProps) => ( + + Unlock + + + } + > + +
+ + +
+
+) diff --git a/packages/features/src/not-found/routes/not-found.tsx b/packages/features/src/not-found/routes/not-found.tsx new file mode 100644 index 00000000..43f3a289 --- /dev/null +++ b/packages/features/src/not-found/routes/not-found.tsx @@ -0,0 +1,13 @@ +import { useNavigate } from 'react-router-dom' + +import { NotFoundView } from '../views/not-found' + +export const NotFoundRoute = () => { + const navigate = useNavigate() + return ( + navigate(-1)} + onGoToDashboard={() => navigate('/')} + /> + ) +} diff --git a/packages/features/src/not-found/views/not-found.stories.tsx b/packages/features/src/not-found/views/not-found.stories.tsx index e5f95eae..e28f150e 100644 --- a/packages/features/src/not-found/views/not-found.stories.tsx +++ b/packages/features/src/not-found/views/not-found.stories.tsx @@ -2,7 +2,12 @@ import { StoryDefault } from '@ladle/react' import { NotFoundView } from './not-found' -export const View = () => +export const View = () => ( + console.log('go back')} + onGoToDashboard={() => console.log('go to dash')} + /> +) export default { title: 'Dashboard / NotFound' diff --git a/packages/features/src/not-found/views/not-found.tsx b/packages/features/src/not-found/views/not-found.tsx index b2a21b8f..fe4a66f5 100644 --- a/packages/features/src/not-found/views/not-found.tsx +++ b/packages/features/src/not-found/views/not-found.tsx @@ -1,23 +1,25 @@ -import { useNavigate } from 'react-router-dom' - import { AppLayout } from '@/components/app-layout' import { Button } from '@/components/ui/button' import { ViewHeading } from '@/components/view-heading' -export const NotFoundView = () => { - const navigate = useNavigate() +type NotFoundViewProps = { + onGoToDashboard: () => void + onGoBack: () => void +} + +export const NotFoundView = ({ + onGoBack, + onGoToDashboard +}: NotFoundViewProps) => { return (
- navigate(-1) }} - /> +
Sorry, but we couldn't find this page
- +
diff --git a/packages/features/src/onboarding/components/wallet-info-form.tsx b/packages/features/src/onboarding/components/wallet-info-form.tsx index c8dffff5..18712e1a 100644 --- a/packages/features/src/onboarding/components/wallet-info-form.tsx +++ b/packages/features/src/onboarding/components/wallet-info-form.tsx @@ -14,9 +14,11 @@ import { Label } from '@/components/ui/label' import { WizardLayout } from '@/components/wizard-layout' import { cn } from '@/lib/utils' +import { WalletInfoData } from '../types' + interface WalletInfoFormProps { title: string - onSubmit: (data: { spendingPassword: string; walletName: string }) => void + onSubmit: (data: WalletInfoData) => void } const formSchema = z.object({ diff --git a/packages/features/src/onboarding/routes/create-wallet.tsx b/packages/features/src/onboarding/routes/create-wallet.tsx new file mode 100644 index 00000000..66c52cd0 --- /dev/null +++ b/packages/features/src/onboarding/routes/create-wallet.tsx @@ -0,0 +1,34 @@ +import { generateMnemonicWords } from '@palladxyz/key-management' +import { useNavigate } from 'react-router-dom' +import { shallow } from 'zustand/shallow' + +import { useOnboardingStore } from '@/common/store/onboarding' + +import { CreateWalletView } from '../views/create-wallet' + +export const CreateWalletRoute = () => { + const navigate = useNavigate() + const { setMnemonic, setWalletName, setSpendingPassword } = + // TODO: fix this useOnboardingStore it is deprecated + useOnboardingStore( + (state) => ({ + setSpendingPassword: state.setSpendingPassword, + setMnemonic: state.setMnemonic, + setWalletName: state.setWalletName + }), + shallow + ) + const onSubmit = ({ + spendingPassword, + walletName + }: { + spendingPassword: string + walletName: string + }) => { + setSpendingPassword(spendingPassword) + setMnemonic(generateMnemonicWords(128).join(' ')) + setWalletName(walletName) + return navigate('/onboarding/writedown') + } + return +} diff --git a/packages/features/src/onboarding/routes/mnemonic-confirmation.tsx b/packages/features/src/onboarding/routes/mnemonic-confirmation.tsx new file mode 100644 index 00000000..b78d9be3 --- /dev/null +++ b/packages/features/src/onboarding/routes/mnemonic-confirmation.tsx @@ -0,0 +1,94 @@ +import { + MinaPayload, + MinaSpecificArgs, + Network +} from '@palladxyz/key-management' +import { getSessionPersistence } from '@palladxyz/persistence' +import { DEFAULT_NETWORK, KeyAgents, useVault } from '@palladxyz/vault' +import { useMemo, useState } from 'react' +import { useForm } from 'react-hook-form' +import { useMixpanel } from 'react-mixpanel-browser' +import { useNavigate } from 'react-router-dom' + +import { useAppStore } from '@/common/store/app' +import { useOnboardingStore } from '@/common/store/onboarding' + +import { MnemonicConfirmationView } from '../views/mnemonic-confirmation' + +const getConfirmationIndex = () => { + return Math.floor(Math.random() * 12) +} + +export const MnemonicConfirmationRoute = () => { + const mixpanel = useMixpanel() + const [restoring, setRestoring] = useState(false) + const restoreWallet = useVault((state) => state.restoreWallet) + const [confirmationIndex] = useState(getConfirmationIndex()) + const setVaultStateInitialized = useAppStore( + (state) => state.setVaultStateInitialized + ) + const navigate = useNavigate() + const { spendingPassword, walletName, mnemonic } = useOnboardingStore( + (state) => ({ + spendingPassword: state.spendingPassword, + walletName: state.walletName, + mnemonic: state.mnemonic + }) + ) + const mnemonicSplit = useMemo(() => mnemonic?.split(' '), [mnemonic]) + const mnemonicConfirmationForm = useForm({ + defaultValues: { + mnemonicWord: '' + } + }) + const mnemonicWord = mnemonicConfirmationForm.watch('mnemonicWord') + const confirmationValid = useMemo( + () => mnemonicSplit?.[confirmationIndex] === mnemonicWord, + [mnemonicWord, mnemonicSplit, confirmationIndex] + ) + const onSubmit = async () => { + if (!walletName) return + if (!spendingPassword) return + if (!mnemonic) return + getSessionPersistence().setItem('spendingPassword', spendingPassword) + await useVault.persist.rehydrate() + const restoreArgs: MinaSpecificArgs = { + network: Network.Mina, + accountIndex: 0, + addressIndex: 0, + networkType: 'testnet' // TODO: make this configurable + } + try { + setRestoring(true) + await restoreWallet( + new MinaPayload(), + restoreArgs, + DEFAULT_NETWORK, + { + mnemonicWords: mnemonic.split(' '), + getPassphrase: () => + new Promise((resolve) => + resolve(Buffer.from(spendingPassword)) + ) + }, + walletName, + KeyAgents.InMemory, + 'Test' // TODO: make this a configurable credential name or random if not provided + ) + mixpanel.track('WalletCreated') + setVaultStateInitialized() + return navigate('/onboarding/finish') + } finally { + setRestoring(false) + } + } + return ( + + ) +} diff --git a/packages/features/src/onboarding/routes/mnemonic-input.tsx b/packages/features/src/onboarding/routes/mnemonic-input.tsx new file mode 100644 index 00000000..bd63417f --- /dev/null +++ b/packages/features/src/onboarding/routes/mnemonic-input.tsx @@ -0,0 +1,87 @@ +import { + MinaPayload, + MinaSpecificArgs, + Network, + validateMnemonic, + wordlist +} from '@palladxyz/key-management' +import { getSessionPersistence } from '@palladxyz/persistence' +import { DEFAULT_NETWORK, KeyAgents, useVault } from '@palladxyz/vault' +import { useState } from 'react' +import { SubmitHandler, useForm } from 'react-hook-form' +import { useMixpanel } from 'react-mixpanel-browser' +import { useNavigate } from 'react-router-dom' +import { shallow } from 'zustand/shallow' + +import { useAppStore } from '@/common/store/app' +import { useOnboardingStore } from '@/common/store/onboarding' + +import { MnemonicInputData } from '../types' +import { MnemonicInputView } from '../views/mnemonic-input' + +export const MnemonicInputRoute = () => { + const mixpanel = useMixpanel() + const [restoring, setRestoring] = useState(false) + const restoreWallet = useVault((state) => state.restoreWallet) + const navigate = useNavigate() + const { walletName, spendingPassword } = useOnboardingStore( + // TODO: fix this useOnboardingStore it is deprecated + (state) => ({ + spendingPassword: state.spendingPassword, + walletName: state.walletName + }), + shallow + ) + const setVaultStateInitialized = useAppStore( + (state) => state.setVaultStateInitialized + ) + const [safetyConfirmed, onSafetyConfirmed] = useState(false) + const mnemonicInputForm = useForm() + const mnemonic = mnemonicInputForm.watch('mnemonic') + const mnemonicValid = validateMnemonic(mnemonic?.join(' '), wordlist) + const onSubmit: SubmitHandler = async (data) => { + if (!walletName) return + if (!spendingPassword) return + getSessionPersistence().setItem('spendingPassword', spendingPassword) + await useVault.persist.rehydrate() + const restoreArgs: MinaSpecificArgs = { + network: Network.Mina, + accountIndex: 0, + addressIndex: 0, + networkType: 'testnet' // TODO: make this configurable if the user restores to mainnet it needs to be 'mainnet + } + try { + setRestoring(true) + await restoreWallet( + new MinaPayload(), + restoreArgs, + DEFAULT_NETWORK, + { + mnemonicWords: data.mnemonic, + getPassphrase: () => + new Promise((resolve) => + resolve(Buffer.from(spendingPassword)) + ) + }, + walletName, + KeyAgents.InMemory, + 'Test' // TODO: make this a configurable credential name or random if not provided + ) + mixpanel.track('WalletRestored') + setVaultStateInitialized() + return navigate('/onboarding/finish') + } finally { + setRestoring(false) + } + } + return ( + + ) +} diff --git a/packages/features/src/onboarding/routes/mnemonic-writedown.tsx b/packages/features/src/onboarding/routes/mnemonic-writedown.tsx new file mode 100644 index 00000000..73af5a94 --- /dev/null +++ b/packages/features/src/onboarding/routes/mnemonic-writedown.tsx @@ -0,0 +1,25 @@ +import { useState } from 'react' +import { useNavigate } from 'react-router-dom' + +import { useOnboardingStore } from '@/common/store/onboarding' + +import { MnemonicWritedownView } from '../views/mnemonic-writedown' + +export const MnemonicWritedownRoute = () => { + const navigate = useNavigate() + const mnemonicWords = useOnboardingStore( + (state) => state.mnemonic?.split(' ') + ) + const [safetyConfirmed, setSafetyConfirmed] = useState(false) + const [mnemonicWritten, setMnemonicWritten] = useState(false) + return ( + navigate('/onboarding/confirmation')} + /> + ) +} diff --git a/packages/features/src/onboarding/routes/restore-wallet.tsx b/packages/features/src/onboarding/routes/restore-wallet.tsx new file mode 100644 index 00000000..c58ee452 --- /dev/null +++ b/packages/features/src/onboarding/routes/restore-wallet.tsx @@ -0,0 +1,30 @@ +import React from 'react' +import { useNavigate } from 'react-router-dom' +import { shallow } from 'zustand/shallow' + +import { useOnboardingStore } from '@/common/store/onboarding' + +import { RestoreWalletView } from '../views/restore-wallet' + +export const RestoreWalletRoute = () => { + const navigate = useNavigate() + const { setWalletName, setSpendingPassword } = useOnboardingStore( + (state) => ({ + setWalletName: state.setWalletName, + setSpendingPassword: state.setSpendingPassword + }), + shallow + ) + const onSubmit = ({ + spendingPassword, + walletName + }: { + spendingPassword: string + walletName: string + }) => { + setSpendingPassword(spendingPassword) + setWalletName(walletName) + return navigate('/onboarding/mnemonic') + } + return +} diff --git a/packages/features/src/onboarding/routes/start.tsx b/packages/features/src/onboarding/routes/start.tsx new file mode 100644 index 00000000..e17c9da1 --- /dev/null +++ b/packages/features/src/onboarding/routes/start.tsx @@ -0,0 +1,46 @@ +import { + getSecurePersistence, + getSessionPersistence +} from '@palladxyz/persistence' +import { useEffect, useState } from 'react' +import { useNavigate } from 'react-router-dom' + +import { useAppStore } from '@/common/store/app' + +import { StartView } from '../views/start' + +export const StartRoute = () => { + const [appInitialized, setAppInitialized] = useState(false) + const isStoreInitialized = useAppStore((state) => state.isInitialized()) + const navigate = useNavigate() + useEffect(() => { + const initialRedirect = async () => { + if (!isStoreInitialized) return setAppInitialized(true) + let spendingPassword + try { + spendingPassword = + (await getSessionPersistence().getItem('spendingPassword')) || '' + } catch { + return navigate('/unlock') + } + const spendingPasswordSet = spendingPassword?.length > 0 + if (!spendingPasswordSet) return navigate('/unlock') + let authenticated + try { + authenticated = (await getSecurePersistence().getItem('foo')) === 'bar' + } catch { + authenticated = false + } + if (!authenticated) return navigate('/unlock') + return navigate('/dashboard') + } + initialRedirect() + }, [isStoreInitialized]) + if (!appInitialized) return null + return ( + navigate('/onboarding/create')} + onRestoreClicked={() => navigate('/onboarding/restore')} + /> + ) +} diff --git a/packages/features/src/onboarding/routes/stay-connected.tsx b/packages/features/src/onboarding/routes/stay-connected.tsx new file mode 100644 index 00000000..395eb8f6 --- /dev/null +++ b/packages/features/src/onboarding/routes/stay-connected.tsx @@ -0,0 +1,8 @@ +import { useNavigate } from 'react-router-dom' + +import { StayConnectedView } from '../views/stay-connected' + +export const StayConnectedRoute = () => { + const navigate = useNavigate() + return navigate('/dashboard')} /> +} diff --git a/packages/features/src/onboarding/types.ts b/packages/features/src/onboarding/types.ts new file mode 100644 index 00000000..4c3b17b9 --- /dev/null +++ b/packages/features/src/onboarding/types.ts @@ -0,0 +1,7 @@ +export type WalletInfoData = { spendingPassword: string; walletName: string } +export type MnemonicInputData = { + mnemonic: string[] +} +export type MnemonicConfirmationData = { + mnemonicWord: string +} diff --git a/packages/features/src/onboarding/views/create-wallet.stories.tsx b/packages/features/src/onboarding/views/create-wallet.stories.tsx index 5c9036cb..e0b83f0e 100644 --- a/packages/features/src/onboarding/views/create-wallet.stories.tsx +++ b/packages/features/src/onboarding/views/create-wallet.stories.tsx @@ -2,7 +2,9 @@ import { StoryDefault } from '@ladle/react' import { CreateWalletView } from './create-wallet' -export const View = () => +export const View = () => ( + console.log(data)} /> +) export default { title: 'Onboarding / Create Wallet' diff --git a/packages/features/src/onboarding/views/create-wallet.tsx b/packages/features/src/onboarding/views/create-wallet.tsx index be663cc3..96c9dde0 100644 --- a/packages/features/src/onboarding/views/create-wallet.tsx +++ b/packages/features/src/onboarding/views/create-wallet.tsx @@ -1,34 +1,10 @@ -import { generateMnemonicWords } from '@palladxyz/key-management' -import { useNavigate } from 'react-router-dom' -import { shallow } from 'zustand/shallow' - -import { useOnboardingStore } from '@/common/store/onboarding' - import { WalletInfoForm } from '../components/wallet-info-form' +import { WalletInfoData } from '../types' -export const CreateWalletView = () => { - const navigate = useNavigate() - const { setMnemonic, setWalletName, setSpendingPassword } = - // TODO: fix this useOnboardingStore it is deprecated - useOnboardingStore( - (state) => ({ - setSpendingPassword: state.setSpendingPassword, - setMnemonic: state.setMnemonic, - setWalletName: state.setWalletName - }), - shallow - ) - const onSubmit = ({ - spendingPassword, - walletName - }: { - spendingPassword: string - walletName: string - }) => { - setSpendingPassword(spendingPassword) - setMnemonic(generateMnemonicWords(128).join(' ')) - setWalletName(walletName) - return navigate('/onboarding/writedown') - } - return +type CreateWalletViewProps = { + onSubmit: (data: WalletInfoData) => void } + +export const CreateWalletView = ({ onSubmit }: CreateWalletViewProps) => ( + +) diff --git a/packages/features/src/onboarding/views/mnemonic-confirmation.stories.tsx b/packages/features/src/onboarding/views/mnemonic-confirmation.stories.tsx index 5cbdc93d..7e14a23d 100644 --- a/packages/features/src/onboarding/views/mnemonic-confirmation.stories.tsx +++ b/packages/features/src/onboarding/views/mnemonic-confirmation.stories.tsx @@ -1,8 +1,21 @@ import { StoryDefault } from '@ladle/react' +import { useForm } from 'react-hook-form' +import { MnemonicConfirmationData } from '../types' import { MnemonicConfirmationView } from './mnemonic-confirmation' -export const View = () => +export const View = () => { + const form = useForm() + return ( + console.log('confirmed')} + restoring={false} + /> + ) +} export default { title: 'Onboarding / Mnemonic Confirmation' diff --git a/packages/features/src/onboarding/views/mnemonic-confirmation.tsx b/packages/features/src/onboarding/views/mnemonic-confirmation.tsx index 96f3c0b6..4d0b6c27 100644 --- a/packages/features/src/onboarding/views/mnemonic-confirmation.tsx +++ b/packages/features/src/onboarding/views/mnemonic-confirmation.tsx @@ -1,18 +1,6 @@ -import { - MinaPayload, - MinaSpecificArgs, - Network -} from '@palladxyz/key-management' -import { getSessionPersistence } from '@palladxyz/persistence' -import { DEFAULT_NETWORK, KeyAgents, useVault } from '@palladxyz/vault' import { Loader2Icon } from 'lucide-react' -import { useMemo, useState } from 'react' -import { useForm } from 'react-hook-form' -import { useMixpanel } from 'react-mixpanel-browser' -import { useNavigate } from 'react-router-dom' +import { UseFormReturn } from 'react-hook-form' -import { useAppStore } from '@/common/store/app' -import { useOnboardingStore } from '@/common/store/onboarding' import { ButtonArrow } from '@/components/button-arrow' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' @@ -20,109 +8,57 @@ import { Label } from '@/components/ui/label' import { WizardLayout } from '@/components/wizard-layout' import { cn } from '@/lib/utils' -const getConfirmationIndex = () => { - return Math.floor(Math.random() * 12) +import { MnemonicConfirmationData } from '../types' + +type MnemonicConfirmationViewProps = { + onSubmit: () => unknown + form: UseFormReturn + restoring: boolean + confirmationIndex: number + confirmationValid: boolean } -export const MnemonicConfirmationView = () => { - const mixpanel = useMixpanel() - const [restoring, setRestoring] = useState(false) - const restoreWallet = useVault((state) => state.restoreWallet) - const [confirmationIndex] = useState(getConfirmationIndex()) - const setVaultStateInitialized = useAppStore( - (state) => state.setVaultStateInitialized - ) - const navigate = useNavigate() - const { spendingPassword, walletName, mnemonic } = useOnboardingStore( - (state) => ({ - spendingPassword: state.spendingPassword, - walletName: state.walletName, - mnemonic: state.mnemonic - }) - ) - const mnemonicSplit = useMemo(() => mnemonic?.split(' '), [mnemonic]) - const { register, handleSubmit, watch } = useForm({ - defaultValues: { - mnemonicWord: '' - } - }) - const mnemonicWord = watch('mnemonicWord') - const isValid = useMemo( - () => mnemonicSplit?.[confirmationIndex] === mnemonicWord, - [mnemonicWord, mnemonicSplit, confirmationIndex] - ) - const onSubmit = async () => { - if (!walletName) return - if (!spendingPassword) return - if (!mnemonic) return - getSessionPersistence().setItem('spendingPassword', spendingPassword) - await useVault.persist.rehydrate() - const restoreArgs: MinaSpecificArgs = { - network: Network.Mina, - accountIndex: 0, - addressIndex: 0, - networkType: 'testnet' // TODO: make this configurable - } - try { - setRestoring(true) - await restoreWallet( - new MinaPayload(), - restoreArgs, - DEFAULT_NETWORK, - { - mnemonicWords: mnemonic.split(' '), - getPassphrase: () => - new Promise((resolve) => - resolve(Buffer.from(spendingPassword)) - ) - }, - walletName, - KeyAgents.InMemory, - 'Test' // TODO: make this a configurable credential name or random if not provided - ) - mixpanel.track('WalletCreated') - setVaultStateInitialized() - return navigate('/onboarding/finish') - } finally { - setRestoring(false) +export const MnemonicConfirmationView = ({ + onSubmit, + form, + restoring, + confirmationIndex, + confirmationValid +}: MnemonicConfirmationViewProps) => ( + + {restoring && } + Next + + } - } - return ( - - {restoring && } - Next - - - } + > +
- - - -
-
- ) -} + + + +
+) diff --git a/packages/features/src/onboarding/views/mnemonic-input.stories.tsx b/packages/features/src/onboarding/views/mnemonic-input.stories.tsx index 30f58197..b1559fe8 100644 --- a/packages/features/src/onboarding/views/mnemonic-input.stories.tsx +++ b/packages/features/src/onboarding/views/mnemonic-input.stories.tsx @@ -1,8 +1,24 @@ import { StoryDefault } from '@ladle/react' +import { useState } from 'react' +import { useForm } from 'react-hook-form' +import { MnemonicInputData } from '../types' import { MnemonicInputView } from './mnemonic-input' -export const View = () => +export const View = () => { + const [safetyConfirmed, onSafetyConfirmed] = useState(false) + const form = useForm() + return ( + console.log(data)} + restoring={false} + /> + ) +} export default { title: 'Onboarding / Mnemonic Input' diff --git a/packages/features/src/onboarding/views/mnemonic-input.tsx b/packages/features/src/onboarding/views/mnemonic-input.tsx index dc38188f..ed14c795 100644 --- a/packages/features/src/onboarding/views/mnemonic-input.tsx +++ b/packages/features/src/onboarding/views/mnemonic-input.tsx @@ -1,21 +1,7 @@ -import { - MinaPayload, - MinaSpecificArgs, - Network, - validateMnemonic, - wordlist -} from '@palladxyz/key-management' -import { getSessionPersistence } from '@palladxyz/persistence' -import { DEFAULT_NETWORK, KeyAgents, useVault } from '@palladxyz/vault' +import { wordlist } from '@palladxyz/key-management' import { Loader2Icon } from 'lucide-react' -import { useState } from 'react' -import { SubmitHandler, useForm } from 'react-hook-form' -import { useMixpanel } from 'react-mixpanel-browser' -import { useNavigate } from 'react-router-dom' -import { shallow } from 'zustand/shallow' +import { UseFormReturn } from 'react-hook-form' -import { useAppStore } from '@/common/store/app' -import { useOnboardingStore } from '@/common/store/onboarding' import { Autocomplete } from '@/components/autocomplete' import { ButtonArrow } from '@/components/button-arrow' import { SecurityCheck } from '@/components/security-check' @@ -24,9 +10,7 @@ import { Label } from '@/components/ui/label' import { WizardLayout } from '@/components/wizard-layout' import { cn } from '@/lib/utils' -type MnemonicInputForm = { - mnemonic: string[] -} +import { MnemonicInputData } from '../types' const MNEMONIC_LENGTH = 12 const mnemonicIterator = Array.from( @@ -34,123 +18,82 @@ const mnemonicIterator = Array.from( (_, i) => i + 1 ) -export const MnemonicInputView = () => { - const mixpanel = useMixpanel() - const [restoring, setRestoring] = useState(false) - const restoreWallet = useVault((state) => state.restoreWallet) - const navigate = useNavigate() - const { walletName, spendingPassword } = useOnboardingStore( - // TODO: fix this useOnboardingStore it is deprecated - (state) => ({ - spendingPassword: state.spendingPassword, - walletName: state.walletName - }), - shallow - ) - const setVaultStateInitialized = useAppStore( - (state) => state.setVaultStateInitialized - ) - const [noOneIsLooking, setNoOneIsLooking] = useState(false) - const { register, handleSubmit, watch, setValue } = - useForm() - const mnemonic = watch('mnemonic') - const mnemonicValid = validateMnemonic(mnemonic?.join(' '), wordlist) - const onSubmit: SubmitHandler = async (data) => { - if (!walletName) return - if (!spendingPassword) return - getSessionPersistence().setItem('spendingPassword', spendingPassword) - await useVault.persist.rehydrate() - const restoreArgs: MinaSpecificArgs = { - network: Network.Mina, - accountIndex: 0, - addressIndex: 0, - networkType: 'testnet' // TODO: make this configurable if the user restores to mainnet it needs to be 'mainnet - } - try { - setRestoring(true) - await restoreWallet( - new MinaPayload(), - restoreArgs, - DEFAULT_NETWORK, - { - mnemonicWords: data.mnemonic, - getPassphrase: () => - new Promise((resolve) => - resolve(Buffer.from(spendingPassword)) - ) - }, - walletName, - KeyAgents.InMemory, - 'Test' // TODO: make this a configurable credential name or random if not provided - ) - mixpanel.track('WalletRestored') - setVaultStateInitialized() - return navigate('/onboarding/finish') - } finally { - setRestoring(false) +type MnemonicInputViewProps = { + onSafetyConfirmed: (value: boolean) => void + safetyConfirmed: boolean + form: UseFormReturn + onSubmit: (data: any) => unknown + mnemonicValid: boolean + restoring: boolean +} + +export const MnemonicInputView = ({ + onSafetyConfirmed, + safetyConfirmed, + form, + onSubmit, + mnemonicValid, + restoring +}: MnemonicInputViewProps) => ( + + {restoring && } + Next + + } - } - return ( - - {restoring && } - Next - - - } - > -
- {noOneIsLooking ? ( -
- -
- {mnemonicIterator.map((wordLabel, i) => ( - setValue(`mnemonic.${i}`, value)} - autoFocus={i === 0} - onEnterPressed={() => { - if (i === mnemonicIterator.length - 1) return - const nextElement = document.querySelector( - `[name="mnemonic.${i + 1}"]` - ) - if (!nextElement) return - return (nextElement as HTMLElement).focus() - }} - onPaste={(event) => { - if (i !== 0) return - const value = event.clipboardData.getData('Text') - event.currentTarget.blur() - const mnemonic = value.split(' ') - if (mnemonic.length !== 12) return - mnemonic.forEach((word, i) => { - setValue(`mnemonic.${i}`, word) - }) - }} - data-testid={`onboarding__mnemonicField.${i}`} - {...register(`mnemonic.${i}`)} - /> - ))} -
+ > +
+ {safetyConfirmed ? ( +
+ +
+ {mnemonicIterator.map((wordLabel, i) => ( + form.setValue(`mnemonic.${i}`, value)} + autoFocus={i === 0} + onEnterPressed={() => { + if (i === mnemonicIterator.length - 1) return + const nextElement = document.querySelector( + `[name="mnemonic.${i + 1}"]` + ) + if (!nextElement) return + return (nextElement as HTMLElement).focus() + }} + onPaste={(event) => { + if (i !== 0) return + const value = event.clipboardData.getData('Text') + event.currentTarget.blur() + const mnemonic = value.split(' ') + if (mnemonic.length !== 12) return + mnemonic.forEach((word, i) => { + form.setValue(`mnemonic.${i}`, word) + }) + }} + data-testid={`onboarding__mnemonicField.${i}`} + {...form.register(`mnemonic.${i}`)} + /> + ))}
- ) : ( -
- setNoOneIsLooking(true)} /> -
- )} -
- - ) -} +
+ ) : ( +
+ onSafetyConfirmed(true)} /> +
+ )} +
+ +) diff --git a/packages/features/src/onboarding/views/mnemonic-writedown.stories.tsx b/packages/features/src/onboarding/views/mnemonic-writedown.stories.tsx index b7492cb9..cb3c9c2c 100644 --- a/packages/features/src/onboarding/views/mnemonic-writedown.stories.tsx +++ b/packages/features/src/onboarding/views/mnemonic-writedown.stories.tsx @@ -1,18 +1,24 @@ import { StoryDefault } from '@ladle/react' -import { useEffect } from 'react' - -import { useOnboardingStore } from '@/common/store/onboarding' +import { useState } from 'react' import { MnemonicWritedownView } from './mnemonic-writedown' +const TEST_MNEMONIC = + 'habit hope tip crystal because grunt nation idea electric witness alert like' + export const View = () => { - const setMnemonic = useOnboardingStore((state) => state.setMnemonic) - useEffect(() => { - setMnemonic( - 'habit hope tip crystal because grunt nation idea electric witness alert like' - ) - }, []) - return + const [safetyConfirmed, setSafetyConfirmed] = useState(false) + const [mnemonicWritten, setMnemonicWritten] = useState(false) + return ( + console.log('confirmed')} + /> + ) } export default { diff --git a/packages/features/src/onboarding/views/mnemonic-writedown.tsx b/packages/features/src/onboarding/views/mnemonic-writedown.tsx index 1717b44b..0fba29d2 100644 --- a/packages/features/src/onboarding/views/mnemonic-writedown.tsx +++ b/packages/features/src/onboarding/views/mnemonic-writedown.tsx @@ -1,7 +1,3 @@ -import { useState } from 'react' -import { useNavigate } from 'react-router-dom' - -import { useOnboardingStore } from '@/common/store/onboarding' import { SecurityCheck } from '@/components/security-check' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' @@ -10,64 +6,74 @@ import { Label } from '@/components/ui/label' import { WizardLayout } from '@/components/wizard-layout' import { cn } from '@/lib/utils' -export const MnemonicWritedownView = () => { - const navigate = useNavigate() - const mnemonic = useOnboardingStore((state) => state.mnemonic?.split(' ')) - const [noOneIsLooking, setNoOneIsLooking] = useState(false) - const [mnemonicWritten, setMnemonicWritten] = useState(false) - return ( - navigate('/onboarding/confirmation')} - data-testid="onboarding__nextButton" - > - Next - - } - > -
- {noOneIsLooking ? ( -
- -
- {mnemonic?.map((word, i) => ( - - {word} - - ))} -
-
- setMnemonicWritten(!mnemonicWritten)} - data-testid="onboarding__mnemonicWrittenCheckbox" - /> - -
+type MnemonicWritedownViewProps = { + onConfirm: () => void + onSafetyConfirmed: (value: boolean) => void + safetyConfirmed: boolean + mnemonicWords: string[] + mnemonicWritten: boolean + setMnemonicWritten: (value: boolean) => void +} + +export const MnemonicWritedownView = ({ + onConfirm, + onSafetyConfirmed, + safetyConfirmed, + mnemonicWords, + mnemonicWritten, + setMnemonicWritten +}: MnemonicWritedownViewProps) => ( + + Next + + } + > +
+ {safetyConfirmed ? ( +
+ +
+ {mnemonicWords.map((word, i) => ( + + {word} + + ))}
- ) : ( -
- setNoOneIsLooking(true)} /> +
+ setMnemonicWritten(!mnemonicWritten)} + data-testid="onboarding__mnemonicWrittenCheckbox" + /> +
- )} -
- - ) -} +
+ ) : ( +
+ onSafetyConfirmed(true)} /> +
+ )} +
+
+) diff --git a/packages/features/src/onboarding/views/restore-wallet.stories.tsx b/packages/features/src/onboarding/views/restore-wallet.stories.tsx index 4c32baac..6e0f5a6a 100644 --- a/packages/features/src/onboarding/views/restore-wallet.stories.tsx +++ b/packages/features/src/onboarding/views/restore-wallet.stories.tsx @@ -2,7 +2,9 @@ import { StoryDefault } from '@ladle/react' import { RestoreWalletView } from './restore-wallet' -export const View = () => +export const View = () => ( + console.log(data)} /> +) export default { title: 'Onboarding / Restore Wallet' diff --git a/packages/features/src/onboarding/views/restore-wallet.tsx b/packages/features/src/onboarding/views/restore-wallet.tsx index 911a915a..5b4e6a86 100644 --- a/packages/features/src/onboarding/views/restore-wallet.tsx +++ b/packages/features/src/onboarding/views/restore-wallet.tsx @@ -1,29 +1,10 @@ -import { useNavigate } from 'react-router-dom' -import { shallow } from 'zustand/shallow' - -import { useOnboardingStore } from '@/common/store/onboarding' - import { WalletInfoForm } from '../components/wallet-info-form' +import { WalletInfoData } from '../types' -export const RestoreWalletView = () => { - const navigate = useNavigate() - const { setWalletName, setSpendingPassword } = useOnboardingStore( - (state) => ({ - setWalletName: state.setWalletName, - setSpendingPassword: state.setSpendingPassword - }), - shallow - ) - const onSubmit = ({ - spendingPassword, - walletName - }: { - spendingPassword: string - walletName: string - }) => { - setSpendingPassword(spendingPassword) - setWalletName(walletName) - return navigate('/onboarding/mnemonic') - } - return +type RestoreWalletViewProps = { + onSubmit: (data: WalletInfoData) => void } + +export const RestoreWalletView = ({ onSubmit }: RestoreWalletViewProps) => ( + +) diff --git a/packages/features/src/onboarding/views/Start.stories.tsx b/packages/features/src/onboarding/views/start.stories.tsx similarity index 50% rename from packages/features/src/onboarding/views/Start.stories.tsx rename to packages/features/src/onboarding/views/start.stories.tsx index 113841c0..2bb9786b 100644 --- a/packages/features/src/onboarding/views/Start.stories.tsx +++ b/packages/features/src/onboarding/views/start.stories.tsx @@ -2,7 +2,12 @@ import { StoryDefault } from '@ladle/react' import { StartView } from './start' -export const View = () => +export const View = () => ( + console.log('create')} + onRestoreClicked={() => console.log('restore')} + /> +) export default { title: 'Onboarding / Start' diff --git a/packages/features/src/onboarding/views/start.tsx b/packages/features/src/onboarding/views/start.tsx index a4a51100..536c3b3b 100644 --- a/packages/features/src/onboarding/views/start.tsx +++ b/packages/features/src/onboarding/views/start.tsx @@ -1,74 +1,45 @@ -import { - getSecurePersistence, - getSessionPersistence -} from '@palladxyz/persistence' -import { useEffect, useState } from 'react' -import { useNavigate } from 'react-router-dom' - -import { useAppStore } from '@/common/store/app' import { ButtonArrow } from '@/components/button-arrow' import { Button } from '@/components/ui/button' import { WizardLayout } from '@/components/wizard-layout' -export const StartView = () => { - const [appInitialized, setAppInitialized] = useState(false) - const isStoreInitialized = useAppStore((state) => state.isInitialized()) - const navigate = useNavigate() - useEffect(() => { - const initialRedirect = async () => { - if (!isStoreInitialized) return setAppInitialized(true) - let spendingPassword - try { - spendingPassword = - (await getSessionPersistence().getItem('spendingPassword')) || '' - } catch { - return navigate('/unlock') - } - const spendingPasswordSet = spendingPassword?.length > 0 - if (!spendingPasswordSet) return navigate('/unlock') - let authenticated - try { - authenticated = (await getSecurePersistence().getItem('foo')) === 'bar' - } catch { - authenticated = false - } - if (!authenticated) return navigate('/unlock') - return navigate('/dashboard') - } - initialRedirect() - }, [isStoreInitialized]) - if (!appInitialized) return null - return ( - - - -
- } - > -
- -

- Your gateway to the Minaverse. -

-
- - ) +type StartViewProps = { + onRestoreClicked: () => void + onCreateClicked: () => void } + +export const StartView = ({ + onRestoreClicked, + onCreateClicked +}: StartViewProps) => ( + + + +
+ } + > +
+ +

+ Your gateway to the Minaverse. +

+
+
+) diff --git a/packages/features/src/onboarding/views/stay-connected.stories.tsx b/packages/features/src/onboarding/views/stay-connected.stories.tsx index c570ff1f..12392fa4 100644 --- a/packages/features/src/onboarding/views/stay-connected.stories.tsx +++ b/packages/features/src/onboarding/views/stay-connected.stories.tsx @@ -2,7 +2,9 @@ import { StoryDefault } from '@ladle/react' import { StayConnectedView } from './stay-connected' -export const View = () => +export const View = () => ( + console.log('dashboard')} /> +) export default { title: 'Onboarding / Stay Connected' diff --git a/packages/features/src/onboarding/views/stay-connected.tsx b/packages/features/src/onboarding/views/stay-connected.tsx index 9fa327bf..c0f50eb2 100644 --- a/packages/features/src/onboarding/views/stay-connected.tsx +++ b/packages/features/src/onboarding/views/stay-connected.tsx @@ -1,48 +1,48 @@ import { Link2Icon, TwitterIcon } from 'lucide-react' -import { useNavigate } from 'react-router-dom' import { Button } from '@/components/ui/button' import { WizardLayout } from '@/components/wizard-layout' const DISCORD_URL = 'https://discord.gg/ExzzfTGUnB' -const TWITTER_URL = 'https://twitter.com/pallad_xyz' +const TWITTER_URL = 'https://twitter.com/pallad_' -export const StayConnectedView = () => { - const navigate = useNavigate() - return ( - - - - } - > -
-
- That's it. Before moving to Dashboard consider joining our Community. -
- -
-
- ) +type StayConnectedRoute = { + onGoToDashboard: () => void } + +export const StayConnectedView = ({ onGoToDashboard }: StayConnectedRoute) => ( + + + + } + > +
+
+ That's it. Before moving to Dashboard consider joining our Community. +
+ +
+
+) diff --git a/packages/features/src/overview/components/asset-list.tsx b/packages/features/src/overview/components/asset-list.tsx index 2cccd71b..be6a68a3 100644 --- a/packages/features/src/overview/components/asset-list.tsx +++ b/packages/features/src/overview/components/asset-list.tsx @@ -7,9 +7,12 @@ import { Avatar, AvatarFallback } from '@/components/ui/avatar' import { Skeleton } from '@/components/ui/skeleton' import { ViewHeading } from '@/components/view-heading' -export const AssetList = () => { +type AssetListProps = { + account: ReturnType +} + +export const AssetList = ({ account }: AssetListProps) => { const navigate = useNavigate() - const { isLoading: accountLoading, minaBalance } = useAccount() return (
{ }} noHorizontalPadding /> - {accountLoading ? ( + {account.isLoading ? ( ) : (
@@ -31,7 +34,7 @@ export const AssetList = () => {

MINA

-

{minaBalance?.toString()}

+

{account.minaBalance?.toString()}

)}
diff --git a/packages/features/src/overview/components/overview-card.tsx b/packages/features/src/overview/components/overview-card.tsx index cd13ce81..ef3d8ea2 100644 --- a/packages/features/src/overview/components/overview-card.tsx +++ b/packages/features/src/overview/components/overview-card.tsx @@ -1,6 +1,4 @@ -import { useFiatPrice } from '@palladxyz/offchain-data' import { CopyIcon, MoveUpRightIcon, QrCodeIcon } from 'lucide-react' -import { useMemo } from 'react' import { useNavigate } from 'react-router-dom' import { useAccount } from '@/common/hooks/use-account' @@ -15,31 +13,22 @@ import { import { AvatarMenu } from './avatar-menu' -export const OverviewCard = () => { +type OverviewCardProps = { + account: ReturnType + fiatBalance: number +} + +export const OverviewCard = ({ account, fiatBalance }: OverviewCardProps) => { const navigate = useNavigate() - const { - minaBalance, - gradientBackground, - publicKey, - copyWalletAddress, - network - } = useAccount() - const { data: fiatPriceData } = useFiatPrice() - const fiatBalance = useMemo(() => { - if (!minaBalance) return - const rawFiatPrice = fiatPriceData?.['mina-protocol']?.usd || 0 - if (!rawFiatPrice) return - return Number(minaBalance) * rawFiatPrice - }, [minaBalance, fiatPriceData]) - if (!publicKey) return null + if (!account.publicKey) return null return (
@@ -48,7 +37,7 @@ export const OverviewCard = () => { className="text-lg font-semibold" data-testid="dashboard__minaBalance" > - {minaBalance?.toString()} MINA + {account.minaBalance?.toString()} MINA
~{fiatBalance?.toFixed(4)} USD @@ -61,9 +50,9 @@ export const OverviewCard = () => { className="text-sm font-semibold dark:text-blue-400 text-blue-600" data-testid="dashboard__addressTruncated" > - {publicKey && + {account.publicKey && truncateString({ - value: publicKey, + value: account.publicKey, firstCharCount: 8, endCharCount: 8 })} @@ -72,7 +61,7 @@ export const OverviewCard = () => {
diff --git a/packages/features/src/overview/routes/overview.tsx b/packages/features/src/overview/routes/overview.tsx new file mode 100644 index 00000000..23bc2a93 --- /dev/null +++ b/packages/features/src/overview/routes/overview.tsx @@ -0,0 +1,18 @@ +import { useFiatPrice } from '@palladxyz/offchain-data' +import { useMemo } from 'react' + +import { useAccount } from '@/common/hooks/use-account' + +import { OverviewView } from '../views/overview' + +export const OverviewRoute = () => { + const account = useAccount() + const { data: fiatPriceData } = useFiatPrice() + const fiatBalance = useMemo(() => { + if (!account.minaBalance) return 0 + const rawFiatPrice = fiatPriceData?.['mina-protocol']?.usd || 0 + if (!rawFiatPrice) return 0 + return Number(account.minaBalance) * rawFiatPrice ?? 0 + }, [account.minaBalance, fiatPriceData]) + return +} diff --git a/packages/features/src/overview/views/Overview.stories.tsx b/packages/features/src/overview/views/overview.stories.tsx similarity index 64% rename from packages/features/src/overview/views/Overview.stories.tsx rename to packages/features/src/overview/views/overview.stories.tsx index 084e4e70..7331f7f5 100644 --- a/packages/features/src/overview/views/Overview.stories.tsx +++ b/packages/features/src/overview/views/overview.stories.tsx @@ -1,11 +1,11 @@ import { StoryDefault } from '@ladle/react' -import { useStoriesWallet } from '../../../.ladle/utils' import { OverviewView } from './overview' export const View = () => { - useStoriesWallet() - return + return ( + + ) } export default { diff --git a/packages/features/src/overview/views/overview.tsx b/packages/features/src/overview/views/overview.tsx index 0922ded6..2efe53e4 100644 --- a/packages/features/src/overview/views/overview.tsx +++ b/packages/features/src/overview/views/overview.tsx @@ -1,5 +1,3 @@ -import { useFiatPrice } from '@palladxyz/offchain-data' - import { useAccount } from '@/common/hooks/use-account' import { AppLayout } from '@/components/app-layout' import { Skeleton } from '@/components/ui/skeleton' @@ -7,15 +5,21 @@ import { Skeleton } from '@/components/ui/skeleton' import { AssetList } from '../components/asset-list' import { OverviewCard } from '../components/overview-card' -export const OverviewView = () => { - const { isLoading: accountLoading } = useAccount() - const { isLoading: priceLoading } = useFiatPrice() - const loading = accountLoading || priceLoading +type OverviewViewProps = { + account: ReturnType + fiatBalance: number +} + +export const OverviewView = ({ account, fiatBalance }: OverviewViewProps) => { return (
- {loading ? : } - + {account.isLoading ? ( + + ) : ( + + )} +
) diff --git a/packages/features/src/receive/routes/receive.tsx b/packages/features/src/receive/routes/receive.tsx new file mode 100644 index 00000000..ee676a4b --- /dev/null +++ b/packages/features/src/receive/routes/receive.tsx @@ -0,0 +1,21 @@ +import { useTheme } from 'next-themes' +import { useNavigate } from 'react-router-dom' + +import { useAccount } from '@/common/hooks/use-account' + +import { ReceiveView } from '../views/receive' + +export const ReceiveRoute = () => { + const { theme } = useTheme() + const navigate = useNavigate() + const { copyWalletAddress, publicKey, gradientBackground } = useAccount() + return ( + navigate(-1)} + /> + ) +} diff --git a/packages/features/src/receive/views/Receive.stories.tsx b/packages/features/src/receive/views/Receive.stories.tsx deleted file mode 100644 index c50d1532..00000000 --- a/packages/features/src/receive/views/Receive.stories.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { StoryDefault } from '@ladle/react' - -import { useStoriesWallet } from '../../../.ladle/utils' -import { ReceiveView } from './receive' - -export const View = () => { - useStoriesWallet() - return -} - -export default { - title: 'Dashboard / Receive' -} satisfies StoryDefault diff --git a/packages/features/src/receive/views/receive.stories.tsx b/packages/features/src/receive/views/receive.stories.tsx new file mode 100644 index 00000000..44c2fa93 --- /dev/null +++ b/packages/features/src/receive/views/receive.stories.tsx @@ -0,0 +1,19 @@ +import { StoryDefault } from '@ladle/react' + +import { ReceiveView } from './receive' + +export const View = () => { + return ( + console.log('copy address')} + onGoBack={() => console.log('go back')} + theme="dark" + /> + ) +} + +export default { + title: 'Dashboard / Receive' +} satisfies StoryDefault diff --git a/packages/features/src/receive/views/receive.tsx b/packages/features/src/receive/views/receive.tsx index bdca5f64..e3a7b956 100644 --- a/packages/features/src/receive/views/receive.tsx +++ b/packages/features/src/receive/views/receive.tsx @@ -1,8 +1,5 @@ -import { useTheme } from 'next-themes' import QRCode from 'react-qr-code' -import { useNavigate } from 'react-router-dom' -import { useAccount } from '@/common/hooks/use-account' import { AppLayout } from '@/components/app-layout' import { Card } from '@/components/ui/card' import { @@ -12,47 +9,53 @@ import { } from '@/components/ui/tooltip' import { ViewHeading } from '@/components/view-heading' -export const ReceiveView = () => { - const { theme } = useTheme() - const navigate = useNavigate() - const { copyWalletAddress, publicKey, gradientBackground } = useAccount() - return ( - -
- navigate(-1) }} - /> -
- {publicKey && ( -
- -
-
- )} - - - - {publicKey} - - - -

Copy Address

-
-
-
-
- - ) +type ReceiveViewProps = { + theme: string + publicKey: string + gradientBackground: string + onGoBack: () => void + onCopyWalletAddress: () => void } + +export const ReceiveView = ({ + theme, + publicKey, + gradientBackground, + onGoBack, + onCopyWalletAddress +}: ReceiveViewProps) => ( + +
+ +
+ {publicKey && ( +
+ +
+
+ )} + + + + {publicKey} + + + +

Copy Address

+
+
+
+
+ +) diff --git a/packages/features/src/router.tsx b/packages/features/src/router.tsx index 1be5f51f..684f0d95 100644 --- a/packages/features/src/router.tsx +++ b/packages/features/src/router.tsx @@ -6,30 +6,30 @@ import { MemoryRouter, Route, Routes } from 'react-router-dom' import { Toaster } from '@/components/ui/toaster' import { TooltipProvider } from '@/components/ui/tooltip' -import { AboutView } from './about/views/about' -import { AddressBookView } from './address-book/views/address-book' -import { NewAddressView } from './address-book/views/new-address' -import { UnlockWalletView } from './lock/views/unlock-wallet' -import { NotFoundView } from './not-found/views/not-found' -import { CreateWalletView } from './onboarding/views/create-wallet' -import { MnemonicConfirmationView } from './onboarding/views/mnemonic-confirmation' -import { MnemonicInputView } from './onboarding/views/mnemonic-input' -import { MnemonicWritedownView } from './onboarding/views/mnemonic-writedown' -import { RestoreWalletView } from './onboarding/views/restore-wallet' -import { StartView } from './onboarding/views/start' -import { StayConnectedView } from './onboarding/views/stay-connected' -import { OverviewView } from './overview/views/overview' -import { ReceiveView } from './receive/views/receive' -import { SendView } from './send/views/send' -import { TransactionErrorView } from './send/views/transaction-error' -import { TransactionSuccessView } from './send/views/transaction-success' -import { TransactionSummaryView } from './send/views/transaction-summary' -import { SettingsView } from './settings/views/settings' -import { BlockProducersView } from './staking/views/block-producers' -import { DelegateView } from './staking/views/delegate' -import { StakingOverviewView } from './staking/views/staking-overview' -import { TransactionDetailsView } from './transactions/views/transaction-details' -import { TransactionsView } from './transactions/views/transactions' +import { AboutRoute } from './about/routes/about' +import { AddressBookRoute } from './address-book/routes/address-book' +import { NewAddressRoute } from './address-book/routes/new-address' +import { UnlockWalletRoute } from './lock/routes/unlock-wallet' +import { NotFoundRoute } from './not-found/routes/not-found' +import { CreateWalletRoute } from './onboarding/routes/create-wallet' +import { MnemonicConfirmationRoute } from './onboarding/routes/mnemonic-confirmation' +import { MnemonicInputRoute } from './onboarding/routes/mnemonic-input' +import { MnemonicWritedownRoute } from './onboarding/routes/mnemonic-writedown' +import { RestoreWalletRoute } from './onboarding/routes/restore-wallet' +import { StartRoute } from './onboarding/routes/start' +import { StayConnectedRoute } from './onboarding/routes/stay-connected' +import { OverviewRoute } from './overview/routes/overview' +import { ReceiveRoute } from './receive/routes/receive' +import { SendRoute } from './send/routes/send' +import { TransactionErrorRoute } from './send/routes/transaction-error' +import { TransactionSuccessRoute } from './send/routes/transaction-success' +import { TransactionSummaryRoute } from './send/routes/transaction-summary' +import { SettingsRoute } from './settings/routes/settings' +import { BlockProducersRoute } from './staking/routes/block-producers' +import { DelegateRoute } from './staking/routes/delegate' +import { StakingOverviewRoute } from './staking/routes/staking-overview' +import { TransactionDetailsRoute } from './transactions/routes/transaction-details' +import { TransactionsRoute } from './transactions/routes/transactions' dayjs.extend(relativeTime) @@ -47,60 +47,63 @@ export const Router = () => {
- } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> } + element={} /> } + element={} /> } + element={} /> } + element={} /> - } /> - } /> - } /> + } /> + } /> + } /> } + element={} + /> + } /> + } /> + } /> - } /> - } /> - } /> } + element={} /> - } /> + } /> } + element={} /> } + element={} /> } + element={} /> } + element={} /> - } /> - } /> - } /> + } /> + } /> + } /> diff --git a/packages/features/src/send/components/confirm-transaction-form.tsx b/packages/features/src/send/components/confirm-transaction-form.tsx index 370109b0..262098ae 100644 --- a/packages/features/src/send/components/confirm-transaction-form.tsx +++ b/packages/features/src/send/components/confirm-transaction-form.tsx @@ -103,7 +103,7 @@ export const ConfirmTransactionForm = () => { const submitTxArgs = { signedTransaction: signedTx as unknown as SignedLegacy, kind: - kind === 'staking' + kind === ('staking' as Mina.TransactionKind) ? Mina.TransactionKind.STAKE_DELEGATION : Mina.TransactionKind.PAYMENT, transactionDetails: { @@ -128,11 +128,13 @@ export const ConfirmTransactionForm = () => { }) await syncWallet() mixpanel.track( - kind === 'staking' ? 'PortfolioDelegated' : 'TransactionSent', + kind === ('staking' as Mina.TransactionKind) + ? 'PortfolioDelegated' + : 'TransactionSent', { amount: transaction.amount, fee: transaction.fee, - to: kind === 'staking' && transaction.to + to: kind === ('staking' as Mina.TransactionKind) && transaction.to } ) navigate('/transactions/success', { diff --git a/packages/features/src/send/routes/send.tsx b/packages/features/src/send/routes/send.tsx new file mode 100644 index 00000000..13fac7e6 --- /dev/null +++ b/packages/features/src/send/routes/send.tsx @@ -0,0 +1,8 @@ +import { useNavigate } from 'react-router-dom' + +import { SendView } from '../views/send' + +export const SendRoute = () => { + const navigate = useNavigate() + return navigate(-1)} /> +} diff --git a/packages/features/src/send/routes/transaction-error.tsx b/packages/features/src/send/routes/transaction-error.tsx new file mode 100644 index 00000000..59bc49bc --- /dev/null +++ b/packages/features/src/send/routes/transaction-error.tsx @@ -0,0 +1,8 @@ +import { useNavigate } from 'react-router-dom' + +import { TransactionErrorView } from '../views/transaction-error' + +export const TransactionErrorRoute = () => { + const navigate = useNavigate() + return navigate(-1)} /> +} diff --git a/packages/features/src/send/routes/transaction-success.tsx b/packages/features/src/send/routes/transaction-success.tsx new file mode 100644 index 00000000..aefbf784 --- /dev/null +++ b/packages/features/src/send/routes/transaction-success.tsx @@ -0,0 +1,14 @@ +import { useLocation, useNavigate } from 'react-router-dom' + +import { TransactionSuccessView } from '../views/transaction-success' + +export const TransactionSuccessRoute = () => { + const navigate = useNavigate() + const { state } = useLocation() + return ( + navigate(`/transactions`)} + /> + ) +} diff --git a/packages/features/src/send/routes/transaction-summary.tsx b/packages/features/src/send/routes/transaction-summary.tsx new file mode 100644 index 00000000..b9087911 --- /dev/null +++ b/packages/features/src/send/routes/transaction-summary.tsx @@ -0,0 +1,38 @@ +import { useMemo } from 'react' +import { useNavigate } from 'react-router-dom' + +import { useAccount } from '@/common/hooks/use-account' +import { useTransactionStore } from '@/common/store/transaction' + +import { TransactionSummaryView } from '../views/transaction-summary' + +export const TransactionSummaryRoute = () => { + const navigate = useNavigate() + const { publicKey } = useAccount() + if (!publicKey) return null + const outgoingTransaction = useTransactionStore( + (state) => state.outgoingTransaction + ) + const kind = useTransactionStore((state) => state.kind) + if (!outgoingTransaction) return null + const total = useMemo( + () => + outgoingTransaction?.amount && + outgoingTransaction?.fee && + outgoingTransaction.amount + outgoingTransaction.fee, + [] + ) + return ( + navigate(-1)} + transaction={{ + amount: outgoingTransaction.amount ?? '0', + fee: outgoingTransaction.fee, + from: publicKey, + to: outgoingTransaction.to, + kind: kind, + total: total ?? '0' + }} + /> + ) +} diff --git a/packages/features/src/send/views/Send.stories.tsx b/packages/features/src/send/views/send.stories.tsx similarity index 65% rename from packages/features/src/send/views/Send.stories.tsx rename to packages/features/src/send/views/send.stories.tsx index 2c9a2170..e2422d6a 100644 --- a/packages/features/src/send/views/Send.stories.tsx +++ b/packages/features/src/send/views/send.stories.tsx @@ -2,7 +2,7 @@ import { StoryDefault } from '@ladle/react' import { SendView } from './send' -export const View = () => +export const View = () => console.log('go back')} /> export default { title: 'Dashboard / Send' diff --git a/packages/features/src/send/views/send.tsx b/packages/features/src/send/views/send.tsx index af5dd6b0..fb155cd4 100644 --- a/packages/features/src/send/views/send.tsx +++ b/packages/features/src/send/views/send.tsx @@ -1,19 +1,17 @@ -import { useNavigate } from 'react-router-dom' - import { AppLayout } from '@/components/app-layout' import { ViewHeading } from '@/components/view-heading' import { SendForm } from '../components/send-form' -export const SendView = () => { - const navigate = useNavigate() +type SendViewProps = { + onGoBack: () => void +} + +export const SendView = ({ onGoBack }: SendViewProps) => { return (
- navigate(-1) }} - /> +
diff --git a/packages/features/src/send/views/transaction-error.stories.tsx b/packages/features/src/send/views/transaction-error.stories.tsx index 0ab1b069..086541c5 100644 --- a/packages/features/src/send/views/transaction-error.stories.tsx +++ b/packages/features/src/send/views/transaction-error.stories.tsx @@ -2,7 +2,9 @@ import { StoryDefault } from '@ladle/react' import { TransactionErrorView } from './transaction-error' -export const View = () => +export const View = () => ( + console.log('go back')} /> +) export default { title: 'Dashboard / Send / Error' diff --git a/packages/features/src/send/views/transaction-error.tsx b/packages/features/src/send/views/transaction-error.tsx index c074f6d5..90f48e57 100644 --- a/packages/features/src/send/views/transaction-error.tsx +++ b/packages/features/src/send/views/transaction-error.tsx @@ -1,13 +1,17 @@ import { AlertOctagonIcon } from 'lucide-react' -import { useNavigate } from 'react-router-dom' import colors from 'tailwindcss/colors' // eslint-disable-line import { AppLayout } from '@/components/app-layout' import { TransactionResult } from '../components/transaction-result' -export const TransactionErrorView = () => { - const navigate = useNavigate() +type TransactionErrorViewProps = { + onGoBack: () => void +} + +export const TransactionErrorView = ({ + onGoBack +}: TransactionErrorViewProps) => { return ( { }} button={{ label: 'Try Again', - onClick: () => navigate(-1) + onClick: onGoBack }} /> diff --git a/packages/features/src/send/views/transaction-success.stories.tsx b/packages/features/src/send/views/transaction-success.stories.tsx index 017b44f8..2903067e 100644 --- a/packages/features/src/send/views/transaction-success.stories.tsx +++ b/packages/features/src/send/views/transaction-success.stories.tsx @@ -2,7 +2,12 @@ import { StoryDefault } from '@ladle/react' import { TransactionSuccessView } from './transaction-success' -export const View = () => +export const View = () => ( + console.log('txs')} + /> +) export default { title: 'Dashboard / Send / Success' diff --git a/packages/features/src/send/views/transaction-success.tsx b/packages/features/src/send/views/transaction-success.tsx index 6c6163f4..e9542fed 100644 --- a/packages/features/src/send/views/transaction-success.tsx +++ b/packages/features/src/send/views/transaction-success.tsx @@ -1,14 +1,19 @@ import { ClockIcon } from 'lucide-react' -import { useLocation, useNavigate } from 'react-router-dom' import colors from 'tailwindcss/colors' // eslint-disable-line import { AppLayout } from '@/components/app-layout' import { TransactionResult } from '../components/transaction-result' -export const TransactionSuccessView = () => { - const navigate = useNavigate() - const { state } = useLocation() +type TransactionSuccessViewProps = { + hash: string + onGoToTransactions: () => void +} + +export const TransactionSuccessView = ({ + hash, + onGoToTransactions +}: TransactionSuccessViewProps) => { return ( { icon: ClockIcon, iconColor: colors.sky['500'], label: 'Pending Transaction Hash', - content: state.hash + content: hash }} button={{ label: 'View Transactions', - onClick: () => navigate(`/transactions`) + onClick: onGoToTransactions }} /> diff --git a/packages/features/src/send/views/transaction-summary.stories.tsx b/packages/features/src/send/views/transaction-summary.stories.tsx index 596954be..267bad33 100644 --- a/packages/features/src/send/views/transaction-summary.stories.tsx +++ b/packages/features/src/send/views/transaction-summary.stories.tsx @@ -2,7 +2,19 @@ import { StoryDefault } from '@ladle/react' import { TransactionSummaryView } from './transaction-summary' -export const View = () => +export const View = () => ( + console.log('go back')} + transaction={{ + amount: '25', + fee: '0.1', + from: 'B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS', + to: 'B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS', + kind: 'transaction', + total: '25.1' + }} + /> +) export default { title: 'Dashboard / Send / Summary' diff --git a/packages/features/src/send/views/transaction-summary.tsx b/packages/features/src/send/views/transaction-summary.tsx index 2861fec3..719f8d5e 100644 --- a/packages/features/src/send/views/transaction-summary.tsx +++ b/packages/features/src/send/views/transaction-summary.tsx @@ -1,10 +1,6 @@ import { ArrowDownLeftIcon } from 'lucide-react' -import { useMemo } from 'react' -import { useNavigate } from 'react-router-dom' -import { useAccount } from '@/common/hooks/use-account' import { truncateString } from '@/common/lib/string' -import { useTransactionStore } from '@/common/store/transaction' import { AppLayout } from '@/components/app-layout' import { MetaField } from '@/components/meta-field' import { Card } from '@/components/ui/card' @@ -12,76 +8,70 @@ import { ViewHeading } from '@/components/view-heading' import { ConfirmTransactionForm } from '../components/confirm-transaction-form' -export const TransactionSummaryView = () => { - const navigate = useNavigate() - const { publicKey } = useAccount() - if (!publicKey) return null - const outgoingTransaction = useTransactionStore( - (state) => state.outgoingTransaction - ) - const kind = useTransactionStore((state) => state.kind) - if (!outgoingTransaction) return null - const total = useMemo( - () => - outgoingTransaction?.amount && - outgoingTransaction?.fee && - parseFloat(outgoingTransaction?.amount) + - parseFloat(outgoingTransaction.fee), - [] - ) - return ( - -
- navigate(-1) }} - /> -
- -
- -
- - -
- - - {outgoingTransaction.amount && ( - - )} - - {outgoingTransaction?.amount && ( - // TODO: this will not always be 'MINA' - )} - - -
-
-
- ) +type TransactionSummaryViewProps = { + onGoBack: () => void + transaction: { + from: string + to: string + amount: string + fee: string + kind: string + total: string + } } + +export const TransactionSummaryView = ({ + onGoBack, + transaction +}: TransactionSummaryViewProps) => ( + +
+ +
+ +
+ +
+ + +
+ + + {transaction.amount && ( + + )} + + {transaction.amount && ( + // TODO: this will not always be 'MINA' + )} + + +
+
+
+) diff --git a/packages/features/src/settings/routes/settings.tsx b/packages/features/src/settings/routes/settings.tsx new file mode 100644 index 00000000..8aed42dc --- /dev/null +++ b/packages/features/src/settings/routes/settings.tsx @@ -0,0 +1,50 @@ +import { useTheme } from 'next-themes' +import { useState } from 'react' +import { useNavigate } from 'react-router-dom' + +import { useAppStore } from '@/common/store/app' +import { useToast } from '@/components/ui/use-toast' + +import { SettingsView } from '../views/settings' + +export const SettingsRoute = () => { + const [restartAlertVisible, setRestartAlertVisible] = useState(false) + const { toast } = useToast() + const navigate = useNavigate() + // const switchNetwork = useVault((state) => state.switchNetwork) + const { setTheme, theme } = useTheme() + // const { mutate } = useSWRConfig() + const { network, shareData, setShareData } = useAppStore((state) => ({ + setNetwork: state.setNetwork, + network: state.network, + shareData: state.shareData, + setShareData: state.setShareData + })) + // const handleNetworkSwitch = async (value: Mina.Networks) => { + // await switchNetwork(value) + // await mutate(() => true, undefined, { revalidate: false }) + // toast({ + // title: `Network has been changed to ${ + // Mina.Networks[value.toUpperCase() as keyof typeof Mina.Networks] + // }` + // }) + // } + const handleThemeSwitch = (value: string) => { + setTheme(value) + toast({ + title: `Theme has been changed.` + }) + } + return ( + navigate(-1)} + restartAlertVisible={restartAlertVisible} + setRestartAlertVisible={setRestartAlertVisible} + setShareData={setShareData} + shareData={shareData} + theme={theme ?? 'dark'} + setTheme={handleThemeSwitch} + /> + ) +} diff --git a/packages/features/src/settings/views/Settings.stories.tsx b/packages/features/src/settings/views/Settings.stories.tsx deleted file mode 100644 index b7761043..00000000 --- a/packages/features/src/settings/views/Settings.stories.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { StoryDefault } from '@ladle/react' - -import { SettingsView } from './settings' - -export const View = () => - -export default { - title: 'Dashboard / Settings' -} satisfies StoryDefault diff --git a/packages/features/src/settings/views/settings.stories.tsx b/packages/features/src/settings/views/settings.stories.tsx new file mode 100644 index 00000000..9ca8cfa7 --- /dev/null +++ b/packages/features/src/settings/views/settings.stories.tsx @@ -0,0 +1,20 @@ +import { StoryDefault } from '@ladle/react' + +import { SettingsView } from './settings' + +export const View = () => ( + console.log('go back')} + restartAlertVisible={false} + setRestartAlertVisible={(visible) => console.log({ visible })} + setShareData={(shareData) => console.log({ shareData })} + setTheme={(theme) => console.log({ theme })} + shareData={false} + theme="dark" + /> +) + +export default { + title: 'Dashboard / Settings' +} satisfies StoryDefault diff --git a/packages/features/src/settings/views/settings.tsx b/packages/features/src/settings/views/settings.tsx index 1df0ce99..53ed2bfb 100644 --- a/packages/features/src/settings/views/settings.tsx +++ b/packages/features/src/settings/views/settings.tsx @@ -1,65 +1,47 @@ import { Mina } from '@palladxyz/mina-core' -import { useVault } from '@palladxyz/vault' import { AlertCircleIcon } from 'lucide-react' -import { useTheme } from 'next-themes' -import { useState } from 'react' -import { useNavigate } from 'react-router-dom' -import { useSWRConfig } from 'swr' -import { useAppStore } from '@/common/store/app' import { AppLayout } from '@/components/app-layout' import { RestartWalletAlert } from '@/components/restart-wallet-alert' import { Button } from '@/components/ui/button' import { Label } from '@/components/ui/label' import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group' import { Switch } from '@/components/ui/switch' -import { useToast } from '@/components/ui/use-toast' import { ViewHeading } from '@/components/view-heading' -export const SettingsView = () => { - const [restartAlertVisible, setRestartAlertVisible] = useState(false) - const { toast } = useToast() - const navigate = useNavigate() - const switchNetwork = useVault((state) => state.switchNetwork) - const { setTheme, theme } = useTheme() - const { mutate } = useSWRConfig() - const { network, shareData, setShareData } = useAppStore((state) => ({ - setNetwork: state.setNetwork, - network: state.network, - shareData: state.shareData, - setShareData: state.setShareData - })) - const handleNetworkSwitch = async (value: Mina.Networks) => { - await switchNetwork(value) - await mutate(() => true, undefined, { revalidate: false }) - toast({ - title: `Network has been changed to ${ - Mina.Networks[value.toUpperCase() as keyof typeof Mina.Networks] - }` - }) - } - const handleThemeSwitch = (value: string) => { - setTheme(value) - toast({ - title: `Theme has been changed.` - }) - } - return ( - - -
- navigate(-1) }} - /> -
-
-

Network

- - {/* TODO: Enable after Public Beta
+type SettingsViewProps = { + onGoBack: () => void + restartAlertVisible: boolean + setRestartAlertVisible: (visible: boolean) => void + network: string + theme: string + setTheme: (theme: string) => void + shareData: boolean + setShareData: (shareData: boolean) => void +} + +export const SettingsView = ({ + onGoBack, + restartAlertVisible, + setRestartAlertVisible, + network, + theme, + setTheme, + shareData, + setShareData +}: SettingsViewProps) => ( + + +
+ +
+
+

Network

+ + {/* TODO: Enable after Public Beta
{ />
*/} -
- - -
- {/*
+
+ + +
+ {/*
{ />
*/} - -
-
-

Theme

- -
- - -
-
- - -
-
- - -
-
-
-
-

Privacy

-
- setShareData(value)} - /> - + +
+
+

Theme

+ +
+ +
-
-
-

Danger Zone

- +
+ + +
+
+ + +
+ +
+
+

Privacy

+
+ setShareData(value)} + /> +
+
+

Danger Zone

+ +
- - ) -} +
+ +) diff --git a/packages/features/src/staking/routes/block-producers.tsx b/packages/features/src/staking/routes/block-producers.tsx new file mode 100644 index 00000000..8d3c3c84 --- /dev/null +++ b/packages/features/src/staking/routes/block-producers.tsx @@ -0,0 +1,28 @@ +import { useNavigate } from 'react-router-dom' + +import { BlockProducersView } from '../views/block-producers' + +const MOCKED_PRODUCERS = [ + { + name: 'Pallad', + publicKey: 'B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS', + stake: 24000, + delegatorsCount: 6000 + }, + { + name: 'Pallad 2', + publicKey: 'B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS', + stake: 12000, + delegatorsCount: 3000 + } +] + +export const BlockProducersRoute = () => { + const navigate = useNavigate() + return ( + navigate(-1)} + blockProducers={MOCKED_PRODUCERS} + /> + ) +} diff --git a/packages/features/src/staking/routes/delegate.tsx b/packages/features/src/staking/routes/delegate.tsx new file mode 100644 index 00000000..f0f3a20d --- /dev/null +++ b/packages/features/src/staking/routes/delegate.tsx @@ -0,0 +1,8 @@ +import { useNavigate } from 'react-router-dom' + +import { DelegateView } from '../views/delegate' + +export const DelegateRoute = () => { + const navigate = useNavigate() + return navigate(-1)} /> +} diff --git a/packages/features/src/staking/routes/staking-overview.tsx b/packages/features/src/staking/routes/staking-overview.tsx new file mode 100644 index 00000000..6f2841cb --- /dev/null +++ b/packages/features/src/staking/routes/staking-overview.tsx @@ -0,0 +1,20 @@ +import { useNavigate } from 'react-router-dom' + +import { useAccount } from '@/common/hooks/use-account' +import { useBlockchainSummary } from '@/common/hooks/use-blockchain-summary' + +import { StakingOverviewView } from '../views/staking-overview' + +export const StakingOverviewRoute = () => { + const blockchainSummary = useBlockchainSummary() + const navigate = useNavigate() + const account = useAccount() + return ( + navigate('/staking/delegate')} + stakeDelegated={account.stakeDelegated} + /> + ) +} diff --git a/packages/features/src/staking/views/block-producers.stories.tsx b/packages/features/src/staking/views/block-producers.stories.tsx index ed956db4..4e4b4e05 100644 --- a/packages/features/src/staking/views/block-producers.stories.tsx +++ b/packages/features/src/staking/views/block-producers.stories.tsx @@ -2,7 +2,14 @@ import { StoryDefault } from '@ladle/react/typings-for-build/app/exports' import { BlockProducersView } from './block-producers' -export const View = () => +export const View = () => ( + console.log('go back')} + /> +) export default { title: 'Dashboard / Staking / Producers' diff --git a/packages/features/src/staking/views/block-producers.tsx b/packages/features/src/staking/views/block-producers.tsx index 2f0e9433..1bf9ea22 100644 --- a/packages/features/src/staking/views/block-producers.tsx +++ b/packages/features/src/staking/views/block-producers.tsx @@ -1,37 +1,34 @@ -import { useNavigate } from 'react-router-dom' - import { AppLayout } from '@/components/app-layout' import { Button } from '@/components/ui/button' import { ViewHeading } from '@/components/view-heading' import { BlockProducerTile } from '../components/block-producer-tile' -const MOCKED_PRODUCERS = [ - { - name: 'Pallad', - publicKey: 'B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS', - stake: 24000, - delegatorsCount: 6000 - }, - { - name: 'Pallad 2', - publicKey: 'B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS', - stake: 12000, - delegatorsCount: 3000 - } -] +type BlockProducer = { + name: string + publicKey: string + stake: number + delegatorsCount: number +} + +type BlockProducersViewProps = { + onGoBack: () => void + blockProducers: BlockProducer[] +} -export const BlockProducersView = () => { - const navigate = useNavigate() +export const BlockProducersView = ({ + onGoBack, + blockProducers +}: BlockProducersViewProps) => { return (
navigate(-1) }} + backButton={{ onClick: onGoBack }} />
- {MOCKED_PRODUCERS.map((producer, i) => ( + {blockProducers.map((producer, i) => ( ))} diff --git a/packages/features/src/staking/views/Delegate.stories.tsx b/packages/features/src/staking/views/delegate.stories.tsx similarity index 66% rename from packages/features/src/staking/views/Delegate.stories.tsx rename to packages/features/src/staking/views/delegate.stories.tsx index 8d02fad8..bfccb875 100644 --- a/packages/features/src/staking/views/Delegate.stories.tsx +++ b/packages/features/src/staking/views/delegate.stories.tsx @@ -2,7 +2,9 @@ import { StoryDefault } from '@ladle/react' import { DelegateView } from './delegate' -export const View = () => +export const View = () => ( + console.log('go back')} /> +) export default { title: 'Dashboard / Staking / Delegate' diff --git a/packages/features/src/staking/views/delegate.tsx b/packages/features/src/staking/views/delegate.tsx index b34c1105..7cb1cba1 100644 --- a/packages/features/src/staking/views/delegate.tsx +++ b/packages/features/src/staking/views/delegate.tsx @@ -1,19 +1,17 @@ -import { useNavigate } from 'react-router-dom' - import { AppLayout } from '@/components/app-layout' import { ViewHeading } from '@/components/view-heading' import { DelegateForm } from '../components/delegate-form' -export const DelegateView = () => { - const navigate = useNavigate() +type DelegateViewProps = { + onGoBack: () => void +} + +export const DelegateView = ({ onGoBack }: DelegateViewProps) => { return (
- navigate(-1) }} - /> +
diff --git a/packages/features/src/staking/views/staking-overview.stories.tsx b/packages/features/src/staking/views/staking-overview.stories.tsx index 0d037d8d..582f862f 100644 --- a/packages/features/src/staking/views/staking-overview.stories.tsx +++ b/packages/features/src/staking/views/staking-overview.stories.tsx @@ -2,7 +2,7 @@ import { StoryDefault } from '@ladle/react' import { StakingOverviewView } from './staking-overview' -export const View = () => +export const View = () => export default { title: 'Dashboard / Staking' diff --git a/packages/features/src/staking/views/staking-overview.tsx b/packages/features/src/staking/views/staking-overview.tsx index 867307be..bfbe5f22 100644 --- a/packages/features/src/staking/views/staking-overview.tsx +++ b/packages/features/src/staking/views/staking-overview.tsx @@ -1,5 +1,3 @@ -import { useNavigate } from 'react-router-dom' - import { useAccount } from '@/common/hooks/use-account' import { useBlockchainSummary } from '@/common/hooks/use-blockchain-summary' import { truncateString } from '@/common/lib/string' @@ -12,65 +10,75 @@ import { Skeleton } from '@/components/ui/skeleton' import { ViewHeading } from '@/components/view-heading' import { getAccountUrl } from '@/lib/explorer' -export const StakingOverviewView = () => { - const { data: blockchainSummary, isLoading: blockchainSummaryLoading } = - useBlockchainSummary() - const navigate = useNavigate() - const { stakeDelegated, accountInfo, network } = useAccount() - const currentSlot = `${blockchainSummary?.slot}/7140` - return ( - -
- navigate('/staking/delegate') - } - } - /> -
- {blockchainSummaryLoading ? ( - - ) : ( - - - - - )} - {stakeDelegated ? ( -
- -
- -
-
- ) : ( -
-
- +type StakingOverviewViewProps = { + stakeDelegated: boolean + onChangePool: () => void + blockchainSummary: ReturnType + account: ReturnType +} + +export const StakingOverviewView = ({ + stakeDelegated, + onChangePool, + blockchainSummary, + account +}: StakingOverviewViewProps) => ( + +
+ +
+ {blockchainSummary.isLoading ? ( + + ) : ( + + + + + )} + {stakeDelegated ? ( +
+ +
+
- )} -
+
+ ) : ( +
+
+ +
+ )}
- - ) -} +
+
+) diff --git a/packages/features/src/transactions/components/tx-indicator.tsx b/packages/features/src/transactions/components/tx-indicator.tsx index 15b88232..74e027e9 100644 --- a/packages/features/src/transactions/components/tx-indicator.tsx +++ b/packages/features/src/transactions/components/tx-indicator.tsx @@ -7,7 +7,7 @@ import { RepeatIcon } from 'lucide-react' -import { TxKind, TxSide } from '@/common/types' +import { TxSide } from '@/common/types' interface TxSideIndicatorProps { side: TxSide @@ -21,14 +21,14 @@ export const TxIndicator = ({ kind, side, from, to }: TxSideIndicatorProps) => { let icon - if (kind === TxKind.PAYMENT) { + if (kind === Mina.TransactionKind.PAYMENT) { if (isSentToSelf) { icon = } else { icon = side === TxSide.INCOMING ? : } - } else if (kind === TxKind.STAKE_DELEGATION) { + } else if (kind === Mina.TransactionKind.STAKE_DELEGATION) { icon = } else { icon = diff --git a/packages/features/src/transactions/components/tx-tile.tsx b/packages/features/src/transactions/components/tx-tile.tsx index 15ffba38..50201694 100644 --- a/packages/features/src/transactions/components/tx-tile.tsx +++ b/packages/features/src/transactions/components/tx-tile.tsx @@ -1,7 +1,8 @@ +import { Mina } from '@palladxyz/mina-core' import { useFiatPrice } from '@palladxyz/offchain-data' import { useNavigate } from 'react-router-dom' -import { StructurizedTransaction, TxKind, TxSide } from '@/common/types' +import { StructurizedTransaction, TxSide } from '@/common/types' import { MinaIcon } from '@/components/mina-icon' import { TxIndicator } from './tx-indicator' @@ -16,8 +17,9 @@ const fiatTxValue = (amount, fiatValue) => { } const getTransactionLabel = (tx) => { - if (tx.kind === TxKind.STAKE_DELEGATION) return 'Delegation' - if (tx.from === tx.to && tx.kind === TxKind.PAYMENT) return 'Sent to Self' + if (tx.kind === Mina.TransactionKind.STAKE_DELEGATION) return 'Delegation' + if (tx.from === tx.to && tx.kind === TransactionKind.PAYMENT) + return 'Sent to Self' return tx.side === TxSide.INCOMING ? 'Received' : 'Sent' } diff --git a/packages/features/src/transactions/routes/transaction-details.tsx b/packages/features/src/transactions/routes/transaction-details.tsx new file mode 100644 index 00000000..40dbd8a9 --- /dev/null +++ b/packages/features/src/transactions/routes/transaction-details.tsx @@ -0,0 +1,32 @@ +import { useNavigate, useParams } from 'react-router-dom' + +import { useAccount } from '@/common/hooks/use-account' +import { useTransaction } from '@/common/hooks/use-transaction' + +import { structurizeTransaction } from '../utils/structurize-transactions' +import { TransactionDetailsView } from '../views/transaction-details' + +export const TransactionDetailsRoute = () => { + const { publicKey, network } = useAccount() + const navigate = useNavigate() + const { hash } = useParams() + if (!hash) return null + if (!publicKey) return null + const { data: transactionData, isLoading: transactionLoading } = + useTransaction({ hash }) + const transaction = + transactionData && + structurizeTransaction({ + tx: transactionData as any, + walletPublicKey: publicKey + }) + if (!transaction) return null + return ( + navigate(-1)} + transaction={transaction} + /> + ) +} diff --git a/packages/features/src/transactions/routes/transactions.tsx b/packages/features/src/transactions/routes/transactions.tsx new file mode 100644 index 00000000..c74ac77e --- /dev/null +++ b/packages/features/src/transactions/routes/transactions.tsx @@ -0,0 +1,35 @@ +import { useEffect } from 'react' +import { useNavigate } from 'react-router-dom' + +import { useAccount } from '@/common/hooks/use-account' +import { useTransactions } from '@/common/hooks/use-transactions' +import { usePendingTransactionStore } from '@/common/store/pending-transactions' + +import { TransactionsView } from '../views/transactions' + +export const TransactionsRoute = () => { + const navigate = useNavigate() + const { data: transactions, isLoading: transactionsLoading } = + useTransactions() + const { isLoading: accountLoading } = useAccount() + const loading = transactionsLoading || accountLoading + const pendingTransactions = usePendingTransactionStore( + (state) => state.pendingTransactions + ) + const clearExpired = usePendingTransactionStore((state) => state.clearExpired) + const transactionHashes = transactions?.map((tx) => tx.hash) || [] + const onlyPendingTransactions = pendingTransactions.filter( + (tx) => !transactionHashes.includes(tx.hash) + ) + useEffect(() => { + clearExpired() + }, []) + return ( + navigate(-1)} + transactions={transactions ?? []} + onlyPendingTransactions={onlyPendingTransactions ?? []} + /> + ) +} diff --git a/packages/features/src/transactions/views/transaction-details.stories.tsx b/packages/features/src/transactions/views/transaction-details.stories.tsx index dd8c15b6..c286832a 100644 --- a/packages/features/src/transactions/views/transaction-details.stories.tsx +++ b/packages/features/src/transactions/views/transaction-details.stories.tsx @@ -1,11 +1,27 @@ import { StoryDefault } from '@ladle/react' +import { Mina } from '@palladxyz/mina-core' + +import { TxSide } from '@/common/types' -import { useStoriesWallet } from '../../../.ladle/utils' import { TransactionDetailsView } from './transaction-details' export const View = () => { - useStoriesWallet() - return + return ( + console.log('go back')} + transaction={{ + from: 'B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS', + to: 'B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS', + minaAmount: '5', + dateTime: 'DATE', + hash: 'B62qkYa1o6Mj6uTTjDQCob7FYZspuhkm4RRQhgJg9j4koEBWiSrTQrS', + side: TxSide.OUTGOING, + kind: Mina.TransactionKind.PAYMENT + }} + /> + ) } export default { diff --git a/packages/features/src/transactions/views/transaction-details.tsx b/packages/features/src/transactions/views/transaction-details.tsx index 3a915818..c6ba30ef 100644 --- a/packages/features/src/transactions/views/transaction-details.tsx +++ b/packages/features/src/transactions/views/transaction-details.tsx @@ -1,7 +1,6 @@ -import { useNavigate, useParams } from 'react-router-dom' +import { Mina } from '@palladxyz/mina-core' -import { useAccount } from '@/common/hooks/use-account' -import { useTransaction } from '@/common/hooks/use-transaction' +import { TxSide } from '@/common/types' import { AppLayout } from '@/components/app-layout' import { MetaField } from '@/components/meta-field' import { Skeleton } from '@/components/ui/skeleton' @@ -9,31 +8,35 @@ import { ViewHeading } from '@/components/view-heading' import { getAccountUrl, getTransactionUrl } from '@/lib/explorer' import { TxIndicator } from '../components/tx-indicator' -import { structurizeTransaction } from '../utils/structurize-transactions' -export const TransactionDetailsView = () => { - const { publicKey, network } = useAccount() - const navigate = useNavigate() - const { hash } = useParams() - if (!hash) return null - if (!publicKey) return null - const { - data: transactionData, - isLoading: transactionLoading, - error - } = useTransaction({ hash }) - const transaction = - transactionData && - structurizeTransaction({ - tx: transactionData as any, - walletPublicKey: publicKey - }) - console.log('>>>TXD', transactionData, error) +type TransactionData = { + side: TxSide + kind?: Mina.TransactionKind + minaAmount: string + hash: string + from: string + to: string + dateTime: string +} + +type TransactionDetailsView = { + onGoBack: () => void + loading: boolean + transaction: TransactionData + network: string +} + +export const TransactionDetailsView = ({ + onGoBack, + loading, + transaction, + network +}: TransactionDetailsView) => { const transactionMetaFields = transaction && [ { label: 'Hash', value: transaction.hash, - url: getTransactionUrl({ network, hash }) + url: getTransactionUrl({ network, hash: transaction.hash }) }, { label: 'Amount', value: `${transaction.minaAmount} MINA` }, { @@ -52,10 +55,10 @@ export const TransactionDetailsView = () => {
navigate(-1) }} + backButton={{ onClick: onGoBack }} />
- {transactionLoading ? ( + {loading ? ( ) : ( transaction && ( diff --git a/packages/features/src/transactions/views/Transactions.stories.tsx b/packages/features/src/transactions/views/transactions.stories.tsx similarity index 55% rename from packages/features/src/transactions/views/Transactions.stories.tsx rename to packages/features/src/transactions/views/transactions.stories.tsx index 19128efc..0303ae7c 100644 --- a/packages/features/src/transactions/views/Transactions.stories.tsx +++ b/packages/features/src/transactions/views/transactions.stories.tsx @@ -1,11 +1,16 @@ import { StoryDefault } from '@ladle/react' -import { useStoriesWallet } from '../../../.ladle/utils' import { TransactionsView } from './transactions' export const View = () => { - useStoriesWallet() - return + return ( + console.log('go back')} + onlyPendingTransactions={[]} + transactions={[]} + /> + ) } export default { diff --git a/packages/features/src/transactions/views/transactions.tsx b/packages/features/src/transactions/views/transactions.tsx index 88f3531a..c2837723 100644 --- a/packages/features/src/transactions/views/transactions.tsx +++ b/packages/features/src/transactions/views/transactions.tsx @@ -1,10 +1,6 @@ +import { Mina } from '@palladxyz/mina-core' import { ClockIcon } from 'lucide-react' -import { useEffect } from 'react' -import { useNavigate } from 'react-router-dom' -import { useAccount } from '@/common/hooks/use-account' -import { useTransactions } from '@/common/hooks/use-transactions' -import { usePendingTransactionStore } from '@/common/store/pending-transactions' import { AppLayout } from '@/components/app-layout' import { ListSkeleton } from '@/components/list-skeleton' import { Alert, AlertTitle } from '@/components/ui/alert' @@ -12,49 +8,48 @@ import { ViewHeading } from '@/components/view-heading' import { TransactionsList } from '../components/transactions-list' -export const TransactionsView = () => { - const navigate = useNavigate() - const { data: transactions, isLoading: transactionsLoading } = - useTransactions() - const { isLoading: accountLoading } = useAccount() - const loading = transactionsLoading || accountLoading - const pendingTransactions = usePendingTransactionStore( - (state) => state.pendingTransactions - ) - const clearExpired = usePendingTransactionStore((state) => state.clearExpired) - const transactionHashes = transactions?.map((tx) => tx.hash) || [] - const onlyPendingTransactions = pendingTransactions.filter( - (tx) => !transactionHashes.includes(tx.hash) - ) - useEffect(() => { - clearExpired() - }, []) - return ( - -
- navigate('/send') }} - /> -
- {onlyPendingTransactions.length > 0 && ( - - - - There are transactions waiting to be broadcasted. - - - )} - {loading ? ( - - ) : ( - transactions && - )} -
-
-
- ) +type PendingTransaction = { + hash: string + expireAt: string +} + +type TransactionsViewProps = { + onGoBack: () => void + loading: boolean + transactions: Mina.TransactionBody[] + onlyPendingTransactions: PendingTransaction[] } + +export const TransactionsView = ({ + onGoBack, + loading, + transactions, + onlyPendingTransactions +}: TransactionsViewProps) => ( + +
+ +
+ {onlyPendingTransactions.length > 0 && ( + + + + There are transactions waiting to be broadcasted. + + + )} + {loading ? ( + + ) : ( + transactions && + )} +
+
+
+) diff --git a/packages/vault/src/vault/vaultStore.ts b/packages/vault/src/vault/vaultStore.ts index bf595a90..b56afcea 100644 --- a/packages/vault/src/vault/vaultStore.ts +++ b/packages/vault/src/vault/vaultStore.ts @@ -107,20 +107,20 @@ export const useVault = create< getCurrentNetworkInfo } = get() if (keyAgentName === '') { - throw new WalletError( + console.error( 'keyAgentName is still blank in getCurrentWallet (has not been set yet)' ) } const singleKeyAgentState = getKeyAgent(keyAgentName) if (credentialName === '') { - throw new WalletError( + console.error( 'credentialName is still blank in getCurrentWallet (has not been set yet)' ) } const credential = getCredential(credentialName) const publicKey = credential.credential?.address ?? '' if (publicKey === '') { - throw new WalletError( + console.error( 'publicKey is undefined blank in getCurrentWallet (has not been set yet)' ) }