Skip to content

Commit

Permalink
Merge pull request #362 from algorandfoundation/use-wallet-prompt
Browse files Browse the repository at this point in the history
feat: use a custom form to prompt for KMD password and mnemonic
  • Loading branch information
PatrickDinh authored Dec 9, 2024
2 parents 9197ac0 + 925fd93 commit 81590b4
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 34 deletions.
50 changes: 24 additions & 26 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@
"@tauri-apps/plugin-dialog": "^2.0.1",
"@tauri-apps/plugin-fs": "^2.0.2",
"@tauri-apps/plugin-shell": "^2.0.1",
"@txnlab/use-wallet": "^3.10.1",
"@txnlab/use-wallet-react": "^3.10.1",
"@txnlab/use-wallet": "^3.11.0",
"@txnlab/use-wallet-react": "^3.11.0",
"@xstate/react": "^4.1.1",
"algosdk": "2.9.0",
"class-variance-authority": "^0.7.0",
Expand Down Expand Up @@ -197,4 +197,4 @@
"ws@>7.0.0 <7.5.9": "7.5.10",
"path-to-regexp@>= 0.2.0 <8.0.0": "8.0.0"
}
}
}
44 changes: 44 additions & 0 deletions src/features/common/components/prompt-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useCallback } from 'react'
import { zfd } from 'zod-form-data'
import { z } from 'zod'
import { Form } from '@/features/forms/components/form'
import { FormActions } from '@/features/forms/components/form-actions'
import { SubmitButton } from '@/features/forms/components/submit-button'
import { CancelButton } from '@/features/forms/components/cancel-button'

type Props = {
message: string
type: 'text' | 'password'
onSubmit: (value: string | null) => void
onCancel: () => void
}

const schema = zfd.formData({
value: zfd.text(z.string().optional()),
})

export function PromptForm({ message, type, onSubmit, onCancel }: Props) {
const submit = useCallback(
async (values: z.infer<typeof schema>) => {
onSubmit(values.value ?? '')
},
[onSubmit]
)

return (
<Form
schema={schema}
onSubmit={submit}
formAction={
<FormActions>
<CancelButton onClick={onCancel} className="w-28" />
<SubmitButton className="w-28">Save</SubmitButton>
</FormActions>
}
>
{(helper) =>
type === 'text' ? helper.textField({ label: message, field: 'value' }) : helper.passwordField({ label: message, field: 'value' })
}
</Form>
)
}
48 changes: 43 additions & 5 deletions src/features/common/components/wallet-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,27 @@ import { WalletProviderInner } from './wallet-provider-inner'
import { defaultKmdWallet, useSelectedKmdWallet } from '@/features/wallet/data/selected-kmd-wallet'
import { NetworkConfigWithId } from '@/features/network/data/types'
import { NetworkId, SupportedWallet, WalletId, WalletIdConfig, WalletManager } from '@txnlab/use-wallet-react'
import { DialogBodyProps, useDialogForm } from '../hooks/use-dialog-form'
import { PromptForm } from './prompt-form'

type Props = PropsWithChildren<{
networkConfig: NetworkConfigWithId
}>

export function WalletProvider({ networkConfig, children }: Props) {
const selectedKmdWallet = useSelectedKmdWallet()
const { open: openKmdPasswordDialog, dialog: kmdPasswordDialog } = useDialogForm({
dialogHeader: 'Connect KMD Wallet',
dialogBody: (props: DialogBodyProps<{ message: string }, string | null>) => (
<PromptForm message={props.data?.message} type="password" onSubmit={props.onSubmit} onCancel={props.onCancel} />
),
})
const { open: openMnemonicDialog, dialog: mnemonicDialog } = useDialogForm({
dialogHeader: 'Connect Mnemonic Wallet',
dialogBody: (props: DialogBodyProps<{ message: string }, string | null>) => (
<PromptForm message={props.data?.message} type="password" onSubmit={props.onSubmit} onCancel={props.onCancel} />
),
})

const key = `${networkConfig.id}-${selectedKmdWallet ?? ''}`

Expand All @@ -24,9 +38,29 @@ export function WalletProvider({ networkConfig, children }: Props) {
baseServer: networkConfig.kmd.server,
token: networkConfig.kmd.token ?? '',
port: String(networkConfig.kmd.port),
promptForPassword: async () => {
const password = await openKmdPasswordDialog({ message: 'Enter KMD Password' })
if (password == null) {
throw new Error('No password provided')
}
return password
},
},
} satisfies WalletIdConfig<WalletId.KMD>)
} else if ([WalletId.MNEMONIC, WalletId.DEFLY, WalletId.PERA, WalletId.EXODUS].includes(id)) {
} else if (id === WalletId.MNEMONIC) {
acc.push({
id,
options: {
promptForMnemonic: async () => {
const passphrase = await openMnemonicDialog({ message: 'Enter 25-word mnemonic passphrase' })
if (!passphrase) {
throw new Error('No passphrase provided')
}
return passphrase
},
},
})
} else if ([WalletId.DEFLY, WalletId.PERA, WalletId.EXODUS].includes(id)) {
acc.push(id)
} else if (id === WalletId.LUTE) {
acc.push({
Expand All @@ -43,7 +77,7 @@ export function WalletProvider({ networkConfig, children }: Props) {
},
[] as unknown as SupportedWallet[]
)
}, [networkConfig.kmd, networkConfig.walletIds, selectedKmdWallet])
}, [networkConfig.walletIds, networkConfig.kmd, selectedKmdWallet, openKmdPasswordDialog, openMnemonicDialog])

const walletManager = useMemo(() => {
return new WalletManager({
Expand All @@ -60,8 +94,12 @@ export function WalletProvider({ networkConfig, children }: Props) {

return (
// The key prop is super important it governs if the provider is reinitialized
<WalletProviderInner key={key} walletManager={walletManager}>
{children}
</WalletProviderInner>
<>
<WalletProviderInner key={key} walletManager={walletManager}>
{children}
</WalletProviderInner>
{kmdPasswordDialog}
{mnemonicDialog}
</>
)
}

0 comments on commit 81590b4

Please sign in to comment.