diff --git a/.changeset/dull-kids-pump.md b/.changeset/dull-kids-pump.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/dull-kids-pump.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/apps/dev-wallet/src/App/Layout/Layout.tsx b/packages/apps/dev-wallet/src/App/Layout/Layout.tsx index a97f105a86..f64b9a160c 100644 --- a/packages/apps/dev-wallet/src/App/Layout/Layout.tsx +++ b/packages/apps/dev-wallet/src/App/Layout/Layout.tsx @@ -5,7 +5,6 @@ import { MonoWorkspaces, } from '@kadena/kode-icons/system'; -import { Aside } from '@/Components/Aside/Aside'; import { NetworkSelector } from '@/Components/NetworkSelector/NetworkSelector'; import { BreadCrumbs } from '@/pages/BreadCrumbs/BreadCrumbs'; import { Themes, useTheme } from '@kadena/kode-ui'; @@ -41,7 +40,6 @@ export const Layout: FC = () => { return ( <> } topBanner={} breadcrumbs={} location={innerLocation} diff --git a/packages/apps/dev-wallet/src/Components/AccountInput/AccountInput.tsx b/packages/apps/dev-wallet/src/Components/AccountInput/AccountInput.tsx index 765ea87ded..2926de437d 100644 --- a/packages/apps/dev-wallet/src/Components/AccountInput/AccountInput.tsx +++ b/packages/apps/dev-wallet/src/Components/AccountInput/AccountInput.tsx @@ -61,6 +61,7 @@ export function AccountInput({ /> )} @@ -90,7 +91,6 @@ export function AccountInput({ contract, (key) => key, ); - console.log(accounts); setDiscovering(false); if (accounts.length > 1) { setDiscoveredAccounts(accounts); diff --git a/packages/apps/dev-wallet/src/Components/Aside/Aside.tsx b/packages/apps/dev-wallet/src/Components/Aside/Aside.tsx deleted file mode 100644 index 2771bbb0e5..0000000000 --- a/packages/apps/dev-wallet/src/Components/Aside/Aside.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { FC, lazy, ReactElement, Suspense, useEffect, useState } from 'react'; -import { useLocation } from 'react-router-dom'; - -const importView = async (key: string) => { - switch (key) { - case 'AddContact': - return lazy(() => - import(`./views/AddContact`).catch(() => import(`./views/Error`)), - ); - - case 'KeySource': - return lazy(() => - import(`./views/KeySource`).catch(() => import(`./views/Error`)), - ); - - case 'NewAsset': - return lazy(() => - import(`./views/NewAsset`).catch(() => import(`./views/Error`)), - ); - - default: - return lazy(() => import(`./views/Error`)); - } -}; - -export const Aside: FC = () => { - const [view, setView] = useState(); - const location = useLocation(); - - const loadView = async (data: Record) => { - const Result: FC = await importView(data.aside); - setView(); - }; - useEffect(() => { - if (!location.hash) return; - - const hashArray = location.hash - .slice(1) - .split('&') - .reduce>((acc, val) => { - const arr = val.split('='); - if (arr.length < 2) return acc; - - //make sure that the component name is capitalized - const value = arr[1].charAt(0).toUpperCase() + String(arr[1]).slice(1); - - acc[arr[0]] = value; - return acc; - }, {}); - - loadView(hashArray); - }, [location.hash]); - - console.log(view); - return ( -
- {view} -
- ); -}; diff --git a/packages/apps/dev-wallet/src/Components/Aside/views/AddContact.tsx b/packages/apps/dev-wallet/src/Components/Aside/views/AddContact.tsx deleted file mode 100644 index a576cc1c65..0000000000 --- a/packages/apps/dev-wallet/src/Components/Aside/views/AddContact.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useWallet } from '@/modules/wallet/wallet.hook'; -import { ContactForm } from '@/pages/contacts/Components/ContactForm'; -import { useLayout } from '@kadena/kode-ui/patterns'; -import { useEffect } from 'react'; - -const AddContact = ({ contactId }: { contactId: string }) => { - const { handleSetAsideExpanded, setAsideTitle } = useLayout(); - const { getContact } = useWallet(); - - useEffect(() => { - setAsideTitle(contactId ? 'Edit Contact' : 'New Contact'); - }, []); - - return ( - <> - handleSetAsideExpanded(false)} - onDone={() => handleSetAsideExpanded(false)} - input={getContact(contactId)} - /> - - ); -}; - -export default AddContact; diff --git a/packages/apps/dev-wallet/src/Components/Aside/views/Error.tsx b/packages/apps/dev-wallet/src/Components/Aside/views/Error.tsx deleted file mode 100644 index acdd038e90..0000000000 --- a/packages/apps/dev-wallet/src/Components/Aside/views/Error.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const Error = () => { - return
There was an error loading the view
; -}; - -export default Error; diff --git a/packages/apps/dev-wallet/src/Components/Aside/views/KeySource.tsx b/packages/apps/dev-wallet/src/Components/Aside/views/KeySource.tsx deleted file mode 100644 index df71a04bc8..0000000000 --- a/packages/apps/dev-wallet/src/Components/Aside/views/KeySource.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { useHDWallet } from '@/modules/key-source/hd-wallet/hd-wallet'; -import { keySourceManager } from '@/modules/key-source/key-source-manager'; -import { WebAuthnService } from '@/modules/key-source/web-authn/webauthn'; -import { useWallet } from '@/modules/wallet/wallet.hook'; - -import { AddKeySourceForm } from '@/pages/keys/Components/AddKeySourceForm'; -import { Notification, Stack } from '@kadena/kode-ui'; -import { useLayout } from '@kadena/kode-ui/patterns'; -import { useEffect, useState } from 'react'; - -const KeySource = () => { - const { handleSetAsideExpanded, setAsideTitle } = useLayout(); - const { keySources, profile, askForPassword } = useWallet(); - const [error, setError] = useState(null); - const { createHDWallet } = useHDWallet(); - - useEffect(() => { - setAsideTitle('Add Key Source'); - }, []); - - async function createWebAuthn() { - if (!profile) { - throw new Error('No profile found'); - } - if (keySources.find((keySource) => keySource.source === 'web-authn')) { - // basically its possible to have multiple web-authn sources - // but for now just for simplicity we will only allow one - alert('WebAuthn already created'); - throw new Error('WebAuthn already created'); - } - const service = (await keySourceManager.get( - 'web-authn', - )) as WebAuthnService; - - await service.register(profile.uuid); - } - - const registerHDWallet = - (type: 'HD-BIP44' | 'HD-chainweaver') => async () => { - const password = await askForPassword(); - if (!password || !profile) { - return; - } - await createHDWallet(profile?.uuid, type, password); - }; - - return ( - <> - {error && ( - - - {error} - - - )} - - handleSetAsideExpanded(false)} - onSave={async (sourcesToInstall) => { - try { - await Promise.all( - sourcesToInstall.map(async (source) => { - switch (source) { - case 'HD-BIP44': - await registerHDWallet('HD-BIP44')(); - break; - case 'HD-chainweaver': - await registerHDWallet('HD-chainweaver')(); - break; - case 'web-authn': - await createWebAuthn(); - break; - default: - throw new Error('Unsupported key source'); - } - }), - ); - } catch (error: any) { - setError(error?.message ?? JSON.stringify(error)); - } finally { - handleSetAsideExpanded(false); - } - keySourceManager.disconnect(); - }} - installed={keySources.map((k) => k.source)} - /> - - ); -}; - -export default KeySource; diff --git a/packages/apps/dev-wallet/src/Components/Aside/views/NewAsset.tsx b/packages/apps/dev-wallet/src/Components/Aside/views/NewAsset.tsx deleted file mode 100644 index ad030e08f9..0000000000 --- a/packages/apps/dev-wallet/src/Components/Aside/views/NewAsset.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { AddToken } from '@/Components/Assets/AddToken'; -import { useLayout } from '@kadena/kode-ui/patterns'; -import { useEffect } from 'react'; - -const NewAsset = () => { - const { setAsideTitle } = useLayout(); - - useEffect(() => { - setAsideTitle('Add new Asset'); - }, []); - - return ; -}; - -export default NewAsset; diff --git a/packages/apps/dev-wallet/src/Components/Assets/AddToken.tsx b/packages/apps/dev-wallet/src/Components/Assets/AddTokenForm.tsx similarity index 61% rename from packages/apps/dev-wallet/src/Components/Assets/AddToken.tsx rename to packages/apps/dev-wallet/src/Components/Assets/AddTokenForm.tsx index 943f8868e3..48d6561cab 100644 --- a/packages/apps/dev-wallet/src/Components/Assets/AddToken.tsx +++ b/packages/apps/dev-wallet/src/Components/Assets/AddTokenForm.tsx @@ -3,7 +3,13 @@ import { useWallet } from '@/modules/wallet/wallet.hook'; import { queryAllChainsClient } from '@kadena/client-utils/core'; import { composePactCommand, execution } from '@kadena/client/fp'; import { Button, Notification, Stack, TextField } from '@kadena/kode-ui'; -import { useLayout } from '@kadena/kode-ui/patterns'; +import { + RightAside, + RightAsideContent, + RightAsideFooter, + RightAsideHeader, + useLayout, +} from '@kadena/kode-ui/patterns'; import { useState } from 'react'; import { useForm } from 'react-hook-form'; @@ -12,7 +18,7 @@ interface TokenForm { symbol: string; } -export function AddToken() { +export function AddTokenForm({ isOpen }: { isOpen: boolean }) { const { register, handleSubmit } = useForm({ defaultValues: { contract: '', @@ -21,7 +27,7 @@ export function AddToken() { }); const { activeNetwork } = useWallet(); - const { handleSetAsideExpanded } = useLayout(); + const { setIsRightAsideExpanded } = useLayout(); const [error, setError] = useState(null); async function onSubmit(data: TokenForm) { @@ -57,25 +63,42 @@ export function AddToken() { } as const; await accountRepository.addFungible(token); - handleSetAsideExpanded(false); + setIsRightAsideExpanded(false); } catch (e: any) { setError(e?.message || e); } } return ( -
- - - - {error && {error}} - + + + + + + + + {error && {error}} + + + + - - -
+ + + ); } diff --git a/packages/apps/dev-wallet/src/Components/Assets/Assets.tsx b/packages/apps/dev-wallet/src/Components/Assets/Assets.tsx index 7f5dbf7e95..76b751766b 100644 --- a/packages/apps/dev-wallet/src/Components/Assets/Assets.tsx +++ b/packages/apps/dev-wallet/src/Components/Assets/Assets.tsx @@ -1,11 +1,11 @@ import { Fungible, IAccount } from '@/modules/account/account.repository'; -import { createAsideUrl } from '@/utils/createAsideUrl'; import { MonoWallet } from '@kadena/kode-icons/system'; -import { Heading, Link as LinkUI, Stack, Text } from '@kadena/kode-ui'; +import { Button, Heading, Stack, Text } from '@kadena/kode-ui'; +import { useLayout } from '@kadena/kode-ui/patterns'; import { PactNumber } from '@kadena/pactjs'; import classNames from 'classnames'; import { useMemo } from 'react'; -import { Link } from 'react-router-dom'; +import { AddTokenForm } from './AddTokenForm'; import { assetBoxClass } from './style.css'; export function Assets({ @@ -21,6 +21,7 @@ export function Assets({ fungibles: Fungible[]; showAddToken?: boolean; }) { + const { setIsRightAsideExpanded, isRightAsideExpanded } = useLayout(); const assets = useMemo(() => { return fungibles.map((item) => { const acs = accounts.filter((a) => a.contract === item.contract); @@ -35,47 +36,51 @@ export function Assets({ }, [accounts, fungibles]); return ( - - - Your Assets - {showAddToken && ( - - Add new asset - - )} - - - {assets.map((asset) => ( - setSelectedContract(asset.contract)} - > - - - + <> + + + + Your Assets + {showAddToken && ( + + )} + + + {assets.map((asset) => ( + setSelectedContract(asset.contract)} + > + + + + + {asset.symbol} + + + {asset.balance} - {asset.symbol} - - {asset.balance} - - - ))} + ))} + - + ); } diff --git a/packages/apps/dev-wallet/src/Components/KeysetDialog/KeySetDialog.tsx b/packages/apps/dev-wallet/src/Components/KeysetDialog/KeySetDialog.tsx index 066a55fff4..1e882e2092 100644 --- a/packages/apps/dev-wallet/src/Components/KeysetDialog/KeySetDialog.tsx +++ b/packages/apps/dev-wallet/src/Components/KeysetDialog/KeySetDialog.tsx @@ -108,7 +108,7 @@ export function KeySetDialog({ {addedKeys.length === 0 && No keys added yet!} {addedKeys.map((key) => ( - + void; onDone: (contect: IContact) => void; + isOpen: boolean; }) { const prompt = usePrompt(); const { activeNetwork } = useWallet(); @@ -36,8 +43,8 @@ export function ContactForm({ control, getValues, handleSubmit, - reset, formState: { isValid }, + reset, } = useForm({ defaultValues: input ?? { name: '', @@ -54,7 +61,7 @@ export function ContactForm({ discoverdAccount: undefined, }, ); - }, [input]); + }, [input?.uuid]); const createContact = useCallback( async ({ discoverdAccount, ...data }: IContactFormData) => { @@ -98,58 +105,56 @@ export function ContactForm({ if (!activeNetwork) return null; return ( - <> -
- - - + + + + + + + - - - KDA Account + + + KDA Account + + ( + + { + field.onChange(value); + }} + /> + + )} + /> - ( - - { - field.onChange(value); - }} - /> - - )} - /> + {error && ( + + {error} + + )} - {error && ( - - {error} - - )} - - - + + {input?.uuid && ( - +
- + ); } diff --git a/packages/apps/dev-wallet/src/pages/contacts/contacts.tsx b/packages/apps/dev-wallet/src/pages/contacts/contacts.tsx index ffd259e093..a808948568 100644 --- a/packages/apps/dev-wallet/src/pages/contacts/contacts.tsx +++ b/packages/apps/dev-wallet/src/pages/contacts/contacts.tsx @@ -1,9 +1,11 @@ import { ConfirmDeletion } from '@/Components/ConfirmDeletion/ConfirmDeletion'; import { ListItem } from '@/Components/ListItem/ListItem'; import { usePrompt } from '@/Components/PromptProvider/Prompt'; -import { contactRepository } from '@/modules/contact/contact.repository'; +import { + contactRepository, + IContact, +} from '@/modules/contact/contact.repository'; import { useWallet } from '@/modules/wallet/wallet.hook'; -import { createAsideUrl } from '@/utils/createAsideUrl'; import { shorten } from '@/utils/helpers'; import { MonoAccountBalanceWallet, @@ -15,17 +17,23 @@ import { ContextMenu, ContextMenuItem, Heading, - Link as LinkUI, Stack, Text, } from '@kadena/kode-ui'; import { useLayout } from '@kadena/kode-ui/patterns'; -import { Link, useNavigate } from 'react-router-dom'; +import { useState } from 'react'; import { panelClass } from '../home/style.css'; +import { ContactForm } from './Components/ContactForm'; export function Contacts() { - useLayout({ - appContext: undefined, + const { setIsRightAsideExpanded, isRightAsideExpanded } = useLayout({ + appContext: { + visual: , + label: 'Add Contact', + onPress: () => { + setIsRightAsideExpanded(true); + }, + }, breadCrumbs: [ { label: 'Contacts', @@ -34,87 +42,100 @@ export function Contacts() { }, ], }); - const navigate = useNavigate(); + const [editContact, setEditContact] = useState(); const { contacts } = useWallet(); const prompt = usePrompt(); + const closeForm = () => { + setIsRightAsideExpanded(false); + setEditContact(undefined); + }; + return ( - - - Contacts + <> + - - Add Contact - - - - {contacts.map((contact) => ( - - - - {contact.name} - {contact.email && ({contact.email})} - - + + + Contacts + + + + + {contacts.map((contact) => ( + + - {' '} - {shorten(contact.account.address)} + {contact.name} + {contact.email && ({contact.email})} + + + {' '} + {shorten(contact.account.address)} + - } - variant="transparent" - isCompact + } + variant="transparent" + isCompact + /> + } + > + { + setIsRightAsideExpanded(true); + setEditContact(contact); + }} /> - } - > - { - navigate( - createAsideUrl('AddContact', { - contactId: contact.uuid, - }), - ); - }} - /> - { - const confirm = await prompt((resolve, reject) => ( - reject()} - onDelete={() => resolve(true)} - title="Delete Contact" - description="Are you sure you want to delete this contact?" - /> - )); - if (confirm) { - await contactRepository.deleteContact(contact.uuid); - } - }} - /> - + { + const confirm = await prompt((resolve, reject) => ( + reject()} + onDelete={() => resolve(true)} + title="Delete Contact" + description="Are you sure you want to delete this contact?" + /> + )); + if (confirm) { + await contactRepository.deleteContact(contact.uuid); + } + }} + /> + + - - - ))} +
+ ))} +
-
+ ); } diff --git a/packages/apps/dev-wallet/src/pages/create-account/create-account.tsx b/packages/apps/dev-wallet/src/pages/create-account/create-account.tsx index a917685dfd..587c5616a6 100644 --- a/packages/apps/dev-wallet/src/pages/create-account/create-account.tsx +++ b/packages/apps/dev-wallet/src/pages/create-account/create-account.tsx @@ -24,25 +24,28 @@ import { Text, TextField, } from '@kadena/kode-ui'; +import { useLayout } from '@kadena/kode-ui/patterns'; import classNames from 'classnames'; import { useState } from 'react'; import { Navigate, useSearchParams } from 'react-router-dom'; import { panelClass } from '../home/style.css.ts'; -import { CreateKeySetDialog } from '../keys/Components/CreateKeySetDialog.tsx'; +import { CreateKeySetForm } from '../keys/Components/CreateKeySetForm.tsx'; import { buttonListClass } from './style.css.ts'; +type IKeySetType = + | { + item: IKeySet; + type: 'keyset'; + } + | { + item: IKeyItem; + type: 'key'; + }; + type AccountType = 'k:account' | 'w:account' | 'r:account'; export function CreateAccount() { - const [selectedItem, setSelectedItem] = useState< - | { - item: IKeySet; - type: 'keyset'; - } - | { - item: IKeyItem; - type: 'key'; - } - >(); + const { setIsRightAsideExpanded, isRightAsideExpanded } = useLayout(); + const [selectedItem, setSelectedItem] = useState(); const [created, setCreated] = useState(null); const [accountType, setAccountType] = useState('k:account'); const [searchParams] = useSearchParams(); @@ -72,8 +75,6 @@ export function CreateAccount() { const usedKeysets = filteredAccounts.map((account) => account.keyset?.uuid); - const [showCreateKeyset, setShowCreateKeyset] = useState(false); - const createAccountByKeyset = async (keyset: IKeySet) => { if (!profile || !activeNetwork || !contract) { throw new Error('Profile or active network not found'); @@ -154,271 +155,282 @@ export function CreateAccount() { ); return ( - - - Create Account - - - setAlias(e.target.value)} /> + <> + setIsRightAsideExpanded(false)} + onDone={(keyset: IKeySet) => { + setSelectedItem({ + item: keyset, + type: 'keyset', + }); + }} + /> - Choose the account type - { - setSelectedItem(undefined); - setAccountType(type as AccountType); - }} - > - + + Create Account + + + setAlias(e.target.value)} + /> + + Choose the account type + { + setSelectedItem(undefined); + setAccountType(type as AccountType); + }} + > + + + k: account - This account will be guarded by a keyset reference. this is - more suitable if you want to change the guard later without - creating a new account. Creating this type of account - requires namespace generation so its not supported yet with - most of the wallets + This account will be guarded by a single key, this is the + most relevant way if you are creating a personal account; + Also this type of account supposed widely by kadena + ecosystem. + + + Tip: The account guard is immutable and can't be changed. + + + + w: account - Tip: Since the guard can be changed, this type of account is - more flexible. + This account will be guarded by a keyset or (single key when + key type is web-authn). This will offers a way to create + shared account or more secure account guarded by several + keys. + + + Tip: The account guard is immutable and can't be changed. - - - - - {contract && ( - - {accountType === 'w:account' && ( - - {showCreateKeyset && ( - setShowCreateKeyset(false)} - onDone={(keyset) => { - setSelectedItem({ - item: keyset, - type: 'keyset', - }); - }} - /> - )} - Keysets + + + + r: account (not supposed yet) + - - Create or use a keyset - - - - {filteredKeysets.length === 0 - ? 'There is not keyset available to select, create a new one' - : 'Select on of the following keysets to create account'} + + This account will be guarded by a keyset reference. this + is more suitable if you want to change the guard later + without creating a new account. Creating this type of + account requires namespace generation so its not supported + yet with most of the wallets - - - {filteredKeysets.map((keyset) => { - return ( - - setSelectedItem({ - item: keyset, - type: 'keyset', - }) - } - > - + Tip: Since the guard can be changed, this type of account + is more flexible. + + + + + + + {contract && ( + + {accountType === 'w:account' && ( + + Keysets + + + Create or use a keyset + + + + {filteredKeysets.length === 0 + ? 'There is not keyset available to select, create a new one' + : 'Select on of the following keysets to create account'} + + + + {filteredKeysets.map((keyset) => { + return ( + + setSelectedItem({ + item: keyset, + type: 'keyset', + }) + } > - - {} + + + {} + - - - ); - })} + + ); + })} + - - )} - {['k:account', 'w:account'].includes(accountType) && - filteredKeySources.length && ( - - Keys - - {filteredKeySources.map((keySource) => { - const filteredKeys = keySource.keys.filter( - (key) => !usedKeys.includes(key.publicKey), - ); - return ( - + )} + {['k:account', 'w:account'].includes(accountType) && + filteredKeySources.length && ( + + Keys + + {filteredKeySources.map((keySource) => { + const filteredKeys = keySource.keys.filter( + (key) => !usedKeys.includes(key.publicKey), + ); + return ( - {keySource.source} - - - - {filteredKeys.length === 0 - ? 'There is not key available to select, create a new one' - : 'Select on of the following keys to create account'} - - - - {keySource.keys - .filter( - (key) => !usedKeys.includes(key.publicKey), - ) - .map((key) => { - return ( - - setSelectedItem({ - item: key, - type: 'key', - }) - } - > - - - + + {keySource.source} + + + + + {filteredKeys.length === 0 + ? 'There is not key available to select, create a new one' + : 'Select on of the following keys to create account'} + + + + {keySource.keys + .filter( + (key) => !usedKeys.includes(key.publicKey), + ) + .map((key) => { + return ( + + setSelectedItem({ + item: key, + type: 'key', + }) + } + > + + + + - - - ); - })} + + ); + })} + - - ); - })} + ); + })} + - - )} - - )} - - - - + )} + + )} + + + + + - - + + ); } diff --git a/packages/apps/dev-wallet/src/pages/home/home-page.tsx b/packages/apps/dev-wallet/src/pages/home/home-page.tsx index d6d0c77738..20e4302ef2 100644 --- a/packages/apps/dev-wallet/src/pages/home/home-page.tsx +++ b/packages/apps/dev-wallet/src/pages/home/home-page.tsx @@ -4,6 +4,7 @@ import { useWallet } from '@/modules/wallet/wallet.hook'; import { panelClass } from '@/pages/home/style.css.ts'; import { useAsync } from '@/utils/useAsync'; import { IPactCommand } from '@kadena/client'; +import { MonoWallet } from '@kadena/kode-icons/system'; import { Box, Heading, Stack, TabItem, Tabs, Text } from '@kadena/kode-ui'; import { useLayout } from '@kadena/kode-ui/patterns'; import { Link } from 'react-router-dom'; @@ -12,13 +13,18 @@ import { TransactionList } from '../transactions/transactions'; export function HomePage() { const { profile, activeNetwork } = useWallet(); + const { setIsRightAsideExpanded } = useLayout(); useLayout({ breadCrumbs: [], - appContext: undefined, + appContext: { + visual: , + label: 'Add Asset', + onPress: () => { + setIsRightAsideExpanded(true); + }, + }, }); - console.log('activeNetwork', activeNetwork); - const [transactions] = useAsync( async (profile, activeNetwork) => { if (profile?.uuid && activeNetwork?.uuid) { diff --git a/packages/apps/dev-wallet/src/pages/keys/Components/AddKeySourceForm.tsx b/packages/apps/dev-wallet/src/pages/keys/Components/AddKeySourceForm.tsx index ffa9ae02de..2c6ef0e352 100644 --- a/packages/apps/dev-wallet/src/pages/keys/Components/AddKeySourceForm.tsx +++ b/packages/apps/dev-wallet/src/pages/keys/Components/AddKeySourceForm.tsx @@ -1,18 +1,27 @@ import { KeySourceType } from '@/modules/wallet/wallet.repository'; import { Button, Checkbox, Divider, Stack, Text } from '@kadena/kode-ui'; +import { + RightAside, + RightAsideContent, + RightAsideFooter, + RightAsideHeader, +} from '@kadena/kode-ui/patterns'; import { useState } from 'react'; export function AddKeySourceForm({ - onClose, + close, onSave, installed, + isOpen, }: { installed: KeySourceType[]; - onClose: () => void; + close: () => void; onSave: (sourcesToInstall: KeySourceType[]) => void; + isOpen: boolean; }) { const [localInstalled, setLocalInstalled] = useState(installed); + const onChange = (source: KeySourceType) => (isSelected: boolean) => { if (isSelected) { setLocalInstalled([...localInstalled, source]); @@ -25,46 +34,44 @@ export function AddKeySourceForm({ onChange: onChange(source), isSelected: localInstalled.includes(source), }); + return ( - <> - - - BIP44 algorithm - - This is the default and recommended algorithm for generating keys. - - - - - - Legacy algorithm (chainweaver 1) - - - This is a legacy algorithm used in chainweaver 1, it's not - recommended to use it for new wallets, use this only if you have a - wallet created in chainweaver 1 - + + + + + + BIP44 algorithm + + This is the default and recommended algorithm for generating keys. + + + + + + Legacy algorithm (chainweaver 1) + + + This is a legacy algorithm used in chainweaver 1, it's not + recommended to use it for new wallets, use this only if you have a + wallet created in chainweaver 1 + + + + + + WebAuthn External keys (Experimental) + + + This is an experimental feature that allows you to use your + compatible devices to generate keys, The keys are stored in the + device and cant be recovered with the wallet if lost the device + + - - - - WebAuthn External keys (Experimental) - - - This is an experimental feature that allows you to use your - compatible devices to generate keys, The keys are stored in the - device and cant be recovered with the wallet if lost the device - - - - - - - - + + ); } diff --git a/packages/apps/dev-wallet/src/pages/keys/Components/CreateKeySetDialog.tsx b/packages/apps/dev-wallet/src/pages/keys/Components/CreateKeySetForm.tsx similarity index 93% rename from packages/apps/dev-wallet/src/pages/keys/Components/CreateKeySetDialog.tsx rename to packages/apps/dev-wallet/src/pages/keys/Components/CreateKeySetForm.tsx index 7ab154bed6..4569c97120 100644 --- a/packages/apps/dev-wallet/src/pages/keys/Components/CreateKeySetDialog.tsx +++ b/packages/apps/dev-wallet/src/pages/keys/Components/CreateKeySetForm.tsx @@ -1,5 +1,4 @@ import { Key } from '@/Components/Key/Key'; -import { displayContentsClass } from '@/Components/Sidebar/style.css'; import { accountRepository, IKeySet, @@ -14,10 +13,6 @@ import { Button, Combobox, ComboboxItem, - Dialog, - DialogContent, - DialogFooter, - DialogHeader, Heading, Notification, Select, @@ -26,6 +21,12 @@ import { Text, TextField, } from '@kadena/kode-ui'; +import { + RightAside, + RightAsideContent, + RightAsideFooter, + RightAsideHeader, +} from '@kadena/kode-ui/patterns'; import { useState } from 'react'; import { Controller, useForm } from 'react-hook-form'; import { keyColumnClass, keyItemClass } from './style.css'; @@ -38,14 +39,14 @@ interface IKeysetForm { alias: string; } -export function CreateKeySetDialog({ - isOpen, +export function CreateKeySetForm({ close, onDone, + isOpen, }: { - isOpen: boolean; close: () => void; onDone?: (keyset: IKeySet) => void; + isOpen: boolean; }) { const { register, control, getValues, setValue, handleSubmit } = useForm({ @@ -59,6 +60,7 @@ export function CreateKeySetDialog({ }); const [error, setError] = useState(null); const { keySources, createKey, profile, keysets, contacts } = useWallet(); + const flattenKeys = keySources .map((keySource) => keySource.keys.map((key) => ({ key, keySource }))) .flat(); @@ -120,22 +122,11 @@ export function CreateKeySetDialog({ } return ( - { - if (!open) { - close(); - } - }} - > -
- Create Key Set - - + + + + + Add keys to the keyset by using the following options @@ -394,8 +385,6 @@ export function CreateKeySetDialog({ - - {error && ( @@ -411,22 +400,23 @@ export function CreateKeySetDialog({ > {addedKeys.length < 2 && 'At least 2 keys are required'} {addedKeys.length >= 2 && `Selected Keys: ${addedKeys.length}`} - - - - - + + + + + +
-
+ ); } diff --git a/packages/apps/dev-wallet/src/pages/keys/Components/KeySets.tsx b/packages/apps/dev-wallet/src/pages/keys/Components/KeySets.tsx index b5df595951..15fb42863d 100644 --- a/packages/apps/dev-wallet/src/pages/keys/Components/KeySets.tsx +++ b/packages/apps/dev-wallet/src/pages/keys/Components/KeySets.tsx @@ -3,55 +3,55 @@ import { useWallet } from '@/modules/wallet/wallet.hook'; import { shorten } from '@/utils/helpers'; import { MonoKey } from '@kadena/kode-icons/system'; import { Button, Heading, Stack, Text } from '@kadena/kode-ui'; -import { useState } from 'react'; -import { CreateKeySetDialog } from './CreateKeySetDialog'; +import { useLayout } from '@kadena/kode-ui/patterns'; +import { CreateKeySetForm } from './CreateKeySetForm'; export function KeySets() { + const { setIsRightAsideExpanded, isRightAsideExpanded } = useLayout(); const { keysets } = useWallet(); - const [showCreateKeyset, setShowCreateKeyset] = useState(false); return ( - - {showCreateKeyset && ( - setShowCreateKeyset(false)} - /> - )} - - Key Sets - - + <> + setIsRightAsideExpanded(false)} + /> - {keysets - .filter(({ guard }) => guard.keys.length >= 2) - .map((keySet) => ( - - - - {keySet.alias} - {keySet.principal} - - - - {keySet.guard.pred}: - {keySet.guard.keys.map((key) => ( - - - - - {shorten(key)}{' '} + + Key Sets + + + + {keysets + .filter(({ guard }) => guard.keys.length >= 2) + .map((keySet) => ( + + + + {keySet.alias} + {keySet.principal} - ))} - - - ))} + + + {keySet.guard.pred}: + {keySet.guard.keys.map((key) => ( + + + + + {shorten(key)}{' '} + + ))} + + + ))} + -
+ ); } diff --git a/packages/apps/dev-wallet/src/pages/keys/Components/Keys.tsx b/packages/apps/dev-wallet/src/pages/keys/Components/Keys.tsx index eea2e60853..1adcef170f 100644 --- a/packages/apps/dev-wallet/src/pages/keys/Components/Keys.tsx +++ b/packages/apps/dev-wallet/src/pages/keys/Components/Keys.tsx @@ -1,6 +1,8 @@ import { ListItem } from '@/Components/ListItem/ListItem.tsx'; +import { useHDWallet } from '@/modules/key-source/hd-wallet/hd-wallet.tsx'; +import { keySourceManager } from '@/modules/key-source/key-source-manager'; +import { WebAuthnService } from '@/modules/key-source/web-authn/webauthn'; import { useWallet } from '@/modules/wallet/wallet.hook'; -import { createAsideUrl } from '@/utils/createAsideUrl.ts'; import { shorten } from '@/utils/helpers.ts'; import { MonoAdd, MonoMoreVert } from '@kadena/kode-icons/system'; import { @@ -9,32 +11,93 @@ import { ContextMenuDivider, ContextMenuItem, Heading, - Link as LinkUI, Stack, Text, } from '@kadena/kode-ui'; -import { Link } from 'react-router-dom'; +import { useLayout } from '@kadena/kode-ui/patterns'; +import { useState } from 'react'; import { panelClass } from '../../home/style.css.ts'; +import { AddKeySourceForm } from './AddKeySourceForm.tsx'; export function Keys() { - const { keySources, createKey } = useWallet(); + const { keySources, profile, askForPassword, createKey } = useWallet(); + const { setIsRightAsideExpanded, isRightAsideExpanded } = useLayout(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [_, setError] = useState(null); + const { createHDWallet } = useHDWallet(); + async function createWebAuthn() { + if (!profile) { + throw new Error('No profile found'); + } + if (keySources.find((keySource) => keySource.source === 'web-authn')) { + // basically its possible to have multiple web-authn sources + // but for now just for simplicity we will only allow one + alert('WebAuthn already created'); + throw new Error('WebAuthn already created'); + } + const service = (await keySourceManager.get( + 'web-authn', + )) as WebAuthnService; + + await service.register(profile.uuid); + } + + const registerHDWallet = + (type: 'HD-BIP44' | 'HD-chainweaver') => async () => { + const password = await askForPassword(); + if (!password || !profile) { + return; + } + await createHDWallet(profile?.uuid, type, password); + }; return ( <> + setIsRightAsideExpanded(false)} + onSave={async (sourcesToInstall) => { + setIsRightAsideExpanded(false); + try { + await Promise.all( + sourcesToInstall.map(async (source) => { + switch (source) { + case 'HD-BIP44': + await registerHDWallet('HD-BIP44')(); + break; + case 'HD-chainweaver': + await registerHDWallet('HD-chainweaver')(); + break; + case 'web-authn': + await createWebAuthn(); + break; + default: + throw new Error('Unsupported key source'); + } + }), + ); + } catch (error: any) { + setError(error?.message ?? JSON.stringify(error)); + } + keySourceManager.disconnect(); + }} + installed={keySources.map((k) => k.source)} + /> Your Keys - { + setIsRightAsideExpanded(true); + }} > Add key Source - + @@ -83,7 +146,7 @@ export function Keys() { {keySource.keys.map((key) => ( - + & { + uuid?: undefined; + }) + | INetwork; + +interface INewNetwork { + uuid: UUID | undefined; + networkId: string; + name: string; + hosts: { + url: string; + submit: boolean; + read: boolean; + confirm: boolean; + isHealthy?: boolean; + nodeVersion?: string; + }[]; +} + +export const getNewNetwork = (): INetworkWithOptionalUuid => ({ + uuid: undefined, + networkId: '', + name: '', + hosts: [ + { + url: '', + submit: false, + read: false, + confirm: false, + }, + ], +}); + +export function NetworkForm({ + network, + onClose, + onSave: onDone, + isOpen, +}: { + network: INetworkWithOptionalUuid; + onSave: (network: INetworkWithOptionalUuid) => void; + onClose: () => void; + isOpen: boolean; +}) { + const { + control, + register, + handleSubmit, + getValues, + setValue, + watch, + formState, + reset, + } = useForm({ + defaultValues: network, + mode: 'all', + }); + const { fields, append, remove } = useFieldArray({ control, name: 'hosts' }); + async function create(updNetwork: INewNetwork) { + const hosts = updNetwork.hosts.map( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ({ isHealthy, nodeVersion, ...host }) => host, + ); + onDone({ + ...updNetwork, + hosts, + }); + } + + useEffect(() => { + reset(network ?? getNewNetwork()); + }, [network.uuid]); + + const hosts = watch('hosts'); + const networkId = watch('networkId'); + + const isNetworkIdValid = + hosts.length > 0 && + networkId.length > 0 && + hosts.every((host) => host.nodeVersion === networkId); + + useEffect(() => { + if (networkId) { + const hosts = getValues('hosts'); + hosts.forEach(({ url }, index) => + fetchNetworkId(url).then((nodeVersion) => { + setValue(`hosts.${index}.nodeVersion`, nodeVersion); + setValue(`hosts.${index}.isHealthy`, nodeVersion !== undefined); + }), + ); + } + }, [networkId, getValues, setValue]); + + return ( + +
+ + + + + + + Host Urls + + + + {fields.map((field, index) => { + const nodeVersion = watch(`hosts.${index}.nodeVersion`); + const isHealthy = watch(`hosts.${index}.isHealthy`); + return ( + + + + { + const host = getValues(`hosts.${index}.url`); + if (!host) { + return; + } + fetchNetworkId(host).then((nodeVersion) => { + setValue( + `hosts.${index}.nodeVersion`, + nodeVersion, + ); + setValue( + `hosts.${index}.isHealthy`, + nodeVersion !== undefined, + ); + }); + }, + })} + /> + + + + {nodeVersion && nodeVersion !== networkId ? ( + + + + the host networkId is + {nodeVersion} + {' '} + but networkId field is {networkId} + + + ) : null} + + ); + })} + + + + + + + + +
+ ); +} diff --git a/packages/apps/dev-wallet/src/pages/networks/Components/network-form.tsx b/packages/apps/dev-wallet/src/pages/networks/Components/network-form.tsx deleted file mode 100644 index 071a943998..0000000000 --- a/packages/apps/dev-wallet/src/pages/networks/Components/network-form.tsx +++ /dev/null @@ -1,192 +0,0 @@ -import { displayContentsClass } from '@/Components/Sidebar/style.css'; -import { INetwork } from '@/modules/network/network.repository'; -import { fetchNetworkId } from '@/modules/network/network.service'; -import { UUID } from '@/modules/types'; -import { Label } from '@/pages/transaction/components/helpers'; -import { - failureClass, - pendingClass, - successClass, -} from '@/pages/transaction/components/style.css'; -import { MonoCircle, MonoDelete, MonoWarning } from '@kadena/kode-icons/system'; -import { Button, Heading, Stack, Text, TextField } from '@kadena/kode-ui'; -import classNames from 'classnames'; -import { useEffect } from 'react'; -import { useFieldArray, useForm } from 'react-hook-form'; - -export type INetworkWithOptionalUuid = - | (Omit & { - uuid?: undefined; - }) - | INetwork; - -interface INewNetwork { - uuid: UUID | undefined; - networkId: string; - name: string; - hosts: { - url: string; - submit: boolean; - read: boolean; - confirm: boolean; - isHealthy?: boolean; - nodeVersion?: string; - }[]; -} - -export function NetworkForm({ - network, - onSave: onDone, -}: { - network: INetworkWithOptionalUuid; - onSave: (network: INetworkWithOptionalUuid) => void; -}) { - const { - control, - register, - handleSubmit, - getValues, - setValue, - watch, - formState, - } = useForm({ - defaultValues: network, - mode: 'all', - }); - const { fields, append, remove } = useFieldArray({ control, name: 'hosts' }); - async function create(updNetwork: INewNetwork) { - const hosts = updNetwork.hosts.map( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - ({ isHealthy, nodeVersion, ...host }) => host, - ); - onDone({ - ...updNetwork, - hosts, - }); - } - - const hosts = watch('hosts'); - const networkId = watch('networkId'); - - const isNetworkIdValid = - hosts.length > 0 && - networkId.length > 0 && - hosts.every((host) => host.nodeVersion === networkId); - - useEffect(() => { - if (networkId) { - const hosts = getValues('hosts'); - hosts.forEach(({ url }, index) => - fetchNetworkId(url).then((nodeVersion) => { - setValue(`hosts.${index}.nodeVersion`, nodeVersion); - setValue(`hosts.${index}.isHealthy`, nodeVersion !== undefined); - }), - ); - } - }, [networkId, getValues, setValue]); - - return ( - <> - -
- - - - Host Urls - - - - {fields.map((field, index) => { - const nodeVersion = watch(`hosts.${index}.nodeVersion`); - const isHealthy = watch(`hosts.${index}.isHealthy`); - return ( - - - - { - const host = getValues(`hosts.${index}.url`); - if (!host) { - return; - } - fetchNetworkId(host).then((nodeVersion) => { - setValue(`hosts.${index}.nodeVersion`, nodeVersion); - setValue( - `hosts.${index}.isHealthy`, - nodeVersion !== undefined, - ); - }); - }, - })} - /> - - - - {nodeVersion && nodeVersion !== networkId ? ( - - - - the host networkId is {nodeVersion} but - networkId field is {networkId} - - - ) : null} - - ); - })} - - - -
- - ); -} diff --git a/packages/apps/dev-wallet/src/pages/networks/networks.tsx b/packages/apps/dev-wallet/src/pages/networks/networks.tsx index e9a0e938c2..a784050e35 100644 --- a/packages/apps/dev-wallet/src/pages/networks/networks.tsx +++ b/packages/apps/dev-wallet/src/pages/networks/networks.tsx @@ -3,41 +3,19 @@ import { networkRepository } from '@/modules/network/network.repository'; import { useWallet } from '@/modules/wallet/wallet.hook'; import { createAsideUrl } from '@/utils/createAsideUrl'; import { MonoWifiTethering, MonoWorkspaces } from '@kadena/kode-icons/system'; -import { - Button, - Dialog, - DialogContent, - DialogHeader, - Heading, - Link, - Stack, - Text, -} from '@kadena/kode-ui'; +import { Button, Heading, Link, Stack, Text } from '@kadena/kode-ui'; import { useLayout } from '@kadena/kode-ui/patterns'; import { useState } from 'react'; import { panelClass } from '../home/style.css'; import { + getNewNetwork, INetworkWithOptionalUuid, NetworkForm, -} from './Components/network-form'; - -const getNewNetwork = (): INetworkWithOptionalUuid => ({ - uuid: undefined, - networkId: '', - name: '', - hosts: [ - { - url: '', - submit: false, - read: false, - confirm: false, - }, - ], -}); +} from './Components/NetworkForm'; export function Networks() { const { networks } = useWallet(); - const [showNetworkModal, setShowNetworkModal] = useState(false); + const { setIsRightAsideExpanded, isRightAsideExpanded } = useLayout(); const [selectedNetwork, setSelectedNetwork] = useState(() => getNewNetwork()); useLayout({ @@ -55,29 +33,25 @@ export function Networks() { return ( <> - - Add Network - - { - if (updNetwork.uuid) { - await networkRepository.updateNetwork(updNetwork); - } else { - await networkRepository.addNetwork({ - ...updNetwork, - uuid: crypto.randomUUID(), - }); - } - setShowNetworkModal(false); - }} - network={selectedNetwork} - /> - - + { + setIsRightAsideExpanded(false); + }} + onSave={async (updNetwork) => { + if (updNetwork.uuid) { + await networkRepository.updateNetwork(updNetwork); + } else { + await networkRepository.addNetwork({ + ...updNetwork, + uuid: crypto.randomUUID(), + }); + } + setIsRightAsideExpanded(false); + }} + network={selectedNetwork} + /> + { setSelectedNetwork(getNewNetwork()); - setShowNetworkModal(true); + setTimeout(() => { + setIsRightAsideExpanded(true); + }, 0); }} > Add Network @@ -126,7 +102,9 @@ export function Networks() { variant="outlined" onPress={() => { setSelectedNetwork(network); - setShowNetworkModal(true); + setTimeout(() => { + setIsRightAsideExpanded(true); + }, 0); }} > Edit diff --git a/packages/apps/dev-wallet/src/pages/transfer-v2/Steps/Result.tsx b/packages/apps/dev-wallet/src/pages/transfer-v2/Steps/Result.tsx index c219521d4e..3bc423fd88 100644 --- a/packages/apps/dev-wallet/src/pages/transfer-v2/Steps/Result.tsx +++ b/packages/apps/dev-wallet/src/pages/transfer-v2/Steps/Result.tsx @@ -92,15 +92,15 @@ export function Result({ Transfers {Object.entries(groupedTransfers).map(([receiver, tx]) => ( - - + + Receiver: {shorten(receiver)} Total Amount: {tx[0].purpose.data.totalAmount} {tx.map((t) => { return ( - + Tx: {shorten(t.hash)} Chain: {t.purpose.data.chainId} diff --git a/packages/libs/kode-ui/src/patterns/SideBarLayout/SideBar.tsx b/packages/libs/kode-ui/src/patterns/SideBarLayout/SideBar.tsx index bf0f6cb8ce..dc9dab3abc 100644 --- a/packages/libs/kode-ui/src/patterns/SideBarLayout/SideBar.tsx +++ b/packages/libs/kode-ui/src/patterns/SideBarLayout/SideBar.tsx @@ -53,9 +53,10 @@ export const SideBar: FC = ({ return ( <> - handleExpand(e as unknown as PressEvent)} />