Skip to content

Commit e1c49e8

Browse files
committed
wip: wallet flow
1 parent 6d244a5 commit e1c49e8

File tree

17 files changed

+753
-522
lines changed

17 files changed

+753
-522
lines changed

api/typeDefs/wallet.js

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { gql } from 'graphql-tag'
22

3-
const sharedSend = 'walletId: ID, templateName: ID, enabled: Boolean!'
4-
5-
const sharedRecv = `${sharedSend}, networkTests: Boolean`
3+
const shared = 'walletId: ID, templateName: ID, enabled: Boolean!'
64

75
const typeDefs = gql`
86
extend type Query {
@@ -24,95 +22,139 @@ const typeDefs = gql`
2422
sendToLnAddr(addr: String!, amount: Int!, maxFee: Int!, comment: String, identifier: Boolean, name: String, email: String): Withdrawl!
2523
cancelInvoice(hash: String!, hmac: String, userCancel: Boolean): Invoice!
2624
dropBolt11(hash: String!): Boolean
27-
removeWallet(id: ID!): Boolean
28-
deleteWalletLogs(protocolId: Int, debug: Boolean): Boolean
29-
setWalletPriorities(priorities: [WalletPriorityUpdate!]!): Boolean
3025
buyCredits(credits: Int!): BuyCreditsPaidAction!
3126
27+
# upserts
3228
upsertWalletSendLNbits(
33-
${sharedSend},
29+
${shared},
3430
url: String!,
3531
apiKey: VaultEntryInput!
3632
): WalletSendLNbits!
3733
3834
upsertWalletRecvLNbits(
39-
${sharedRecv},
35+
${shared},
4036
url: String!,
4137
apiKey: String!
4238
): WalletRecvLNbits!
4339
4440
upsertWalletSendPhoenixd(
45-
${sharedSend},
41+
${shared},
4642
url: String!,
4743
apiKey: VaultEntryInput!
4844
): WalletSendPhoenixd!
4945
5046
upsertWalletRecvPhoenixd(
51-
${sharedRecv},
47+
${shared},
5248
url: String!,
5349
apiKey: String!
5450
): WalletRecvPhoenixd!
5551
5652
upsertWalletSendBlink(
57-
${sharedSend},
53+
${shared},
5854
currency: VaultEntryInput!,
5955
apiKey: VaultEntryInput!
6056
): WalletSendBlink!
6157
6258
upsertWalletRecvBlink(
63-
${sharedRecv},
59+
${shared},
6460
currency: String!,
6561
apiKey: String!
6662
): WalletRecvBlink!
6763
6864
upsertWalletRecvLightningAddress(
69-
${sharedRecv},
65+
${shared},
7066
address: String!
7167
): WalletRecvLightningAddress!
7268
7369
upsertWalletSendNWC(
74-
${sharedSend},
70+
${shared},
7571
url: VaultEntryInput!
7672
): WalletSendNWC!
7773
7874
upsertWalletRecvNWC(
79-
${sharedRecv},
75+
${shared},
8076
url: String!
8177
): WalletRecvNWC!
8278
8379
upsertWalletRecvCLNRest(
84-
${sharedRecv},
80+
${shared},
8581
socket: String!,
8682
rune: String!,
8783
cert: String
8884
): WalletRecvCLNRest!
8985
9086
upsertWalletRecvLNDGRPC(
91-
${sharedRecv},
87+
${shared},
9288
socket: String!,
9389
macaroon: String!,
9490
cert: String
9591
): WalletRecvLNDGRPC!
9692
9793
upsertWalletSendLNC(
98-
${sharedSend},
94+
${shared},
9995
pairingPhrase: VaultEntryInput!,
10096
localKey: VaultEntryInput!,
10197
remoteKey: VaultEntryInput!,
10298
serverHost: VaultEntryInput!
10399
): WalletSendLNC!
104100
105101
upsertWalletSendWebLN(
106-
${sharedSend}
102+
${shared}
107103
): WalletSendWebLN!
108104
105+
# tests
106+
testWalletRecvNWC(
107+
url: String!
108+
): Boolean!
109+
110+
testWalletRecvLightningAddress(
111+
address: String!
112+
): Boolean!
113+
114+
testWalletRecvCLNRest(
115+
socket: String!,
116+
rune: String!,
117+
cert: String
118+
): Boolean!
119+
120+
testWalletRecvLNDGRPC(
121+
socket: String!,
122+
macaroon: String!,
123+
cert: String
124+
): Boolean!
125+
126+
testWalletRecvPhoenixd(
127+
url: String!
128+
apiKey: String!
129+
): Boolean!
130+
131+
testWalletRecvLNbits(
132+
url: String!
133+
apiKey: String!
134+
): Boolean!
135+
136+
testWalletRecvBlink(
137+
currency: String!
138+
apiKey: String!
139+
): Boolean!
140+
141+
# delete
142+
removeWallet(id: ID!): Boolean
109143
removeWalletProtocol(id: ID!): Boolean
144+
145+
# crypto
110146
updateWalletEncryption(keyHash: String!, wallets: [WalletEncryptionUpdate!]!): Boolean
111147
updateKeyHash(keyHash: String!): Boolean
112148
resetWallets(newKeyHash: String!): Boolean
113149
disablePassphraseExport: Boolean
150+
151+
# settings
114152
setWalletSettings(settings: WalletSettingsInput!): Boolean
153+
setWalletPriorities(priorities: [WalletPriorityUpdate!]!): Boolean
154+
155+
# logs
115156
addWalletLog(protocolId: Int, level: String!, message: String!, timestamp: Date!, invoiceId: Int): Boolean
157+
deleteWalletLogs(protocolId: Int, debug: Boolean): Boolean
116158
}
117159
118160
type BuyCreditsResult {

pages/wallets/index.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,6 @@ export default function Wallet () {
113113
<WalletLayoutSubHeader>use real bitcoin</WalletLayoutSubHeader>
114114
<div className='text-center'>
115115
<WalletLayoutLink href='/wallets/logs'>wallet logs</WalletLayoutLink>
116-
<span className='mx-2'></span>
117-
<WalletLayoutLink href='/wallets/settings'>settings</WalletLayoutLink>
118116
{showPassphrase && (
119117
<>
120118
<span className='mx-2'></span>

styles/wallet.module.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@
106106
flex-direction: column;
107107
}
108108

109+
@media (max-width: 768px) {
110+
.form {
111+
margin-top: 1rem;
112+
}
113+
}
114+
109115
.separator {
110116
display: flex;
111117
align-items: center;
@@ -136,3 +142,12 @@
136142
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
137143
container-type: inline-size;
138144
}
145+
146+
.progressNumber {
147+
display: inline-block;
148+
background-color: var(--bs-body-bg);
149+
border-radius: 50%;
150+
width: 24px;
151+
height: 24px;
152+
line-height: 24px;
153+
}

wallets/README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ That's it!
6262

6363
## How to add a new protocol
6464

65+
// TODO: update this documentation with new required test mutations
66+
6567
**1. Update prisma.schema**
6668

6769
- add enum value to `WalletProtocolName` enum
@@ -303,10 +305,10 @@ index 3c1fffd1..af3858a5 100644
303305
--- a/api/typeDefs/wallet.js
304306
+++ b/api/typeDefs/wallet.js
305307
@@ -38,6 +38,7 @@ const typeDefs = gql`
306-
upsertWalletRecvLNDGRPC(walletId: ID, templateId: ID, enabled: Boolean!, networkTests: Boolean, socket: String!, macaroon: String!, cert: String): WalletRecvLNDGRPC!
308+
upsertWalletRecvLNDGRPC(walletId: ID, templateId: ID, enabled: Boolean!, socket: String!, macaroon: String!, cert: String): WalletRecvLNDGRPC!
307309
upsertWalletSendLNC(walletId: ID, templateId: ID, enabled: Boolean!, pairingPhrase: VaultEntryInput!, localKey: VaultEntryInput!, remoteKey: VaultEntryInput!, serverHost: VaultEntryInput!): WalletSendLNC!
308310
upsertWalletSendWebLN(walletId: ID, templateId: ID, enabled: Boolean!): WalletSendWebLN!
309-
+ upsertWalletRecvBolt12(walletId: ID, templateId: ID, enabled: Boolean!, networkTests: Boolean, offer: String!): WalletRecvBolt12!
311+
+ upsertWalletRecvBolt12(walletId: ID, templateId: ID, enabled: Boolean!, offer: String!): WalletRecvBolt12!
310312
removeWalletProtocol(id: ID!): Boolean
311313
updateWalletEncryption(keyHash: String!, wallets: [WalletEncryptionUpdate!]!): Boolean
312314
updateKeyHash(keyHash: String!): Boolean
@@ -340,8 +342,8 @@ index d1a65ff4..138d1a62 100644
340342
`
341343
+
342344
+export const UPSERT_WALLET_RECEIVE_BOLT12 = gql`
343-
+ mutation upsertWalletRecvBolt12($walletId: ID, $templateId: ID, $enabled: Boolean!, $networkTests: Boolean, $offer: String!) {
344-
+ upsertWalletRecvBolt12(walletId: $walletId, templateId: $templateId, enabled: $enabled, networkTests: $networkTests, offer: $offer) {
345+
+ mutation upsertWalletRecvBolt12($walletId: ID, $templateId: ID, $enabled: Boolean!, $offer: String!) {
346+
+ upsertWalletRecvBolt12(walletId: $walletId, templateId: $templateId, enabled: $enabled, offer: $offer) {
345347
+ id
346348
+ }
347349
+ }

wallets/client/components/card.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@ export function WalletCard ({ wallet, draggable = false, index, ...props }) {
5555
}
5656

5757
function WalletLink ({ wallet, children }) {
58-
const support = useWalletSupport(wallet)
59-
const sendRecvParam = support.send ? 'send' : 'receive'
60-
const href = '/wallets' + (isWallet(wallet) ? `/${wallet.id}` : `/${urlify(wallet.name)}`) + `/${sendRecvParam}`
58+
const href = '/wallets' + (isWallet(wallet) ? `/${wallet.id}` : `/${urlify(wallet.name)}`)
6159
return <Link href={href}>{children}</Link>
6260
}
6361

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import classNames from 'classnames'
2+
import { Button } from 'react-bootstrap'
3+
4+
import { useBack, useSkip } from './context'
5+
6+
export function BackButton ({ className }) {
7+
const back = useBack()
8+
return <Button className={classNames('text-muted nav-link fw-bold', className)} variant='link' onClick={back}>back</Button>
9+
}
10+
11+
export function SkipButton ({ className }) {
12+
const skip = useSkip()
13+
return <Button className={classNames('ms-auto me-3 text-muted nav-link fw-bold', className)} variant='link' onClick={skip}>skip</Button>
14+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { isWallet, protocolClientSchema, protocolFields, walletLud16Domain } from '@/wallets/lib/util'
2+
import { createContext, useContext, useEffect, useMemo } from 'react'
3+
4+
export const WalletFormsContext = createContext()
5+
6+
export const Progress = {
7+
SEND: 0,
8+
RECEIVE: 1,
9+
SETTINGS: 2
10+
}
11+
12+
function useWalletFormsContext () {
13+
const context = useContext(WalletFormsContext)
14+
if (!context) {
15+
throw new Error('useContext must be used within a WalletFormsContext')
16+
}
17+
return context
18+
}
19+
20+
export function useWallet () {
21+
const { wallet } = useContext(WalletFormsContext)
22+
return wallet
23+
}
24+
25+
export function useWalletRefetch () {
26+
const { refetch } = useWalletFormsContext()
27+
return refetch
28+
}
29+
30+
export function useProgress () {
31+
const { progress } = useWalletFormsContext()
32+
return progress
33+
}
34+
35+
export function useProtocol () {
36+
const protocols = useWalletProtocols()
37+
const { protocol, setProtocol } = useWalletFormsContext()
38+
39+
useEffect(() => {
40+
// when we move between send and receive, we need to make sure that we've selected a protocol
41+
// that actually exists, so if the protocol is not found, we set it to the first protocol
42+
if (!protocol || !protocols.find(p => p.id === protocol.id)) {
43+
setProtocol(protocols[0])
44+
}
45+
}, [protocol, setProtocol, protocols])
46+
47+
// make sure we always have a protocol, even on first render before useEffect runs
48+
return useMemo(() => protocol || protocols[0], [protocol, protocols])
49+
}
50+
51+
export function useSelectProtocol () {
52+
const { setProtocol } = useWalletFormsContext()
53+
return setProtocol
54+
}
55+
56+
export function useBack () {
57+
const { back } = useWalletFormsContext()
58+
return back
59+
}
60+
61+
export function useSkip () {
62+
const { skip } = useWalletFormsContext()
63+
return skip
64+
}
65+
66+
export function useFormState () {
67+
const { formState } = useWalletFormsContext()
68+
return formState
69+
}
70+
71+
export function useUpdateFormState () {
72+
const { updateFormState } = useWalletFormsContext()
73+
return updateFormState
74+
}
75+
76+
function useProtocolFormState () {
77+
const protocol = useProtocol()
78+
const formState = useFormState()
79+
80+
if (protocol.send) {
81+
return formState?.send?.[protocol.id]
82+
} else {
83+
return formState?.receive?.[protocol.id]
84+
}
85+
}
86+
87+
export function useWalletProtocols () {
88+
const wallet = useWallet()
89+
const progress = useProgress()
90+
91+
const send = progress === Progress.SEND
92+
const protocolFilter = p => send ? p.send : !p.send
93+
94+
return isWallet(wallet)
95+
? wallet.template.protocols.filter(protocolFilter)
96+
: wallet.protocols.filter(protocolFilter)
97+
}
98+
99+
export function useProtocolForm (protocol) {
100+
const formState = useProtocolFormState(protocol)
101+
102+
const wallet = useWallet()
103+
const lud16Domain = walletLud16Domain(wallet.name)
104+
const fields = protocolFields(protocol)
105+
const initial = fields.reduce((acc, field) => {
106+
// wallet templates don't have a config
107+
let value = protocol.config?.[field.name] ?? formState?.[field.name]
108+
109+
if (field.name === 'address' && lud16Domain && value) {
110+
value = value.split('@')[0]
111+
}
112+
113+
return {
114+
...acc,
115+
[field.name]: value || ''
116+
}
117+
}, { enabled: protocol.enabled ?? formState?.enabled })
118+
119+
let schema = protocolClientSchema(protocol)
120+
if (lud16Domain) {
121+
schema = schema.transform(({ address, ...rest }) => {
122+
return {
123+
address: address ? `${address}@${lud16Domain}` : '',
124+
...rest
125+
}
126+
})
127+
}
128+
129+
return { fields, initial, schema }
130+
}

0 commit comments

Comments
 (0)