receive.go raw
1 package gui
2
3 import (
4 "fmt"
5 "strconv"
6
7 "github.com/p9c/p9/pkg/amt"
8
9 "github.com/atotto/clipboard"
10
11 l "github.com/p9c/p9/pkg/gel/gio/layout"
12 "github.com/p9c/p9/pkg/gel/gio/text"
13
14 "github.com/p9c/p9/pkg/gel"
15 )
16
17 const Break1 = 48
18
19 type ReceivePage struct {
20 wg *WalletGUI
21 inputWidth, break1 float32
22 sm, md, lg, xl l.Widget
23 urn string
24 }
25
26 func (wg *WalletGUI) GetReceivePage() (rp *ReceivePage) {
27 rp = &ReceivePage{
28 wg: wg,
29 inputWidth: 17,
30 break1: 48,
31 }
32 rp.sm = rp.SmallList
33 return
34 }
35
36 func (rp *ReceivePage) Fn(gtx l.Context) l.Dimensions {
37 wg := rp.wg
38 return wg.Responsive(
39 wg.Size.Load(), gel.Widgets{
40 {
41 Widget: rp.SmallList,
42 },
43 {
44 Size: rp.break1,
45 Widget: rp.MediumList,
46 },
47 },
48 ).Fn(gtx)
49 }
50
51 func (rp *ReceivePage) SmallList(gtx l.Context) l.Dimensions {
52 wg := rp.wg
53 smallWidgets := []l.Widget{
54 wg.Direction().Center().Embed(rp.QRButton()).Fn,
55 rp.InputMessage(),
56 rp.AmountInput(),
57 rp.MessageInput(),
58 rp.RegenerateButton(),
59 rp.AddressbookHeader(),
60 }
61 smallWidgets = append(smallWidgets, rp.GetAddressbookHistoryCards("DocBg")...)
62 le := func(gtx l.Context, index int) l.Dimensions {
63 return wg.Inset(0.25, smallWidgets[index]).Fn(gtx)
64 }
65 return wg.VFlex().AlignStart().
66 Flexed(
67 1,
68 wg.lists["receive"].
69 Vertical().Start().
70 Length(len(smallWidgets)).
71 ListElement(le).Fn,
72 ).Fn(gtx)
73 }
74
75 func (rp *ReceivePage) InputMessage() l.Widget {
76 return rp.wg.Body2("Input details to request a payment").Alignment(text.Middle).Fn
77 }
78
79 func (rp *ReceivePage) MediumList(gtx l.Context) l.Dimensions {
80 wg := rp.wg
81 qrWidget := []l.Widget{
82 wg.Direction().Center().Embed(rp.QRButton()).Fn,
83 rp.InputMessage(),
84 rp.AmountInput(),
85 rp.MessageInput(),
86 rp.RegenerateButton(),
87 // rp.AddressbookHeader(),
88 }
89 qrLE := func(gtx l.Context, index int) l.Dimensions {
90 return wg.Inset(0.25, qrWidget[index]).Fn(gtx)
91 }
92 var historyWidget []l.Widget
93
94 historyWidget = append(historyWidget, rp.GetAddressbookHistoryCards("DocBg")...)
95 historyLE := func(gtx l.Context, index int) l.Dimensions {
96 return wg.Inset(
97 0.25,
98 historyWidget[index],
99 ).Fn(gtx)
100 }
101 return wg.Flex().AlignStart().
102 Rigid(
103 func(gtx l.Context) l.Dimensions {
104 gtx.Constraints.Max.X, gtx.Constraints.Min.X = int(wg.TextSize.V*rp.inputWidth),
105 int(wg.TextSize.V*rp.inputWidth)
106 return wg.VFlex().
107 Rigid(
108 wg.lists["receiveMedium"].
109 Vertical().
110 Length(len(qrWidget)).
111 ListElement(qrLE).Fn,
112 ).Fn(gtx)
113 },
114 ).
115 Rigid(
116 wg.VFlex().AlignStart().
117 Rigid(
118 rp.AddressbookHeader(),
119 ).
120 Rigid(
121 wg.lists["receiveAddresses"].
122 Vertical().
123 Length(len(historyWidget)).
124 ListElement(historyLE).Fn,
125 ).
126 Fn,
127 ).Fn(gtx)
128 }
129
130 func (rp *ReceivePage) Spacer() l.Widget {
131 return rp.wg.Flex().AlignMiddle().Flexed(1, rp.wg.Inset(0.25, gel.EmptySpace(0, 0)).Fn).Fn
132 }
133
134 func (rp *ReceivePage) GetAddressbookHistoryCards(bg string) (widgets []l.Widget) {
135 wg := rp.wg
136 avail := len(wg.receiveAddressbookClickables)
137 req := len(wg.State.receiveAddresses)
138 if req > avail {
139 for i := 0; i < req-avail; i++ {
140 wg.receiveAddressbookClickables = append(wg.receiveAddressbookClickables, wg.WidgetPool.GetClickable())
141 }
142 }
143 for x := range wg.State.receiveAddresses {
144 j := x
145 i := len(wg.State.receiveAddresses) - 1 - x
146 widgets = append(
147 widgets, func(gtx l.Context) l.Dimensions {
148 return wg.ButtonLayout(
149 wg.receiveAddressbookClickables[i].SetClick(
150 func() {
151 msg := wg.State.receiveAddresses[i].Message
152 if len(msg) > 64 {
153 msg = msg[:64]
154 }
155 qrText := fmt.Sprintf(
156 "parallelcoin:%s?amount=%8.8f&message=%s",
157 wg.State.receiveAddresses[i].Address,
158 wg.State.receiveAddresses[i].Amount.ToDUO(),
159 msg,
160 )
161 D.Ln("clicked receive address list item", j)
162 if e := clipboard.WriteAll(qrText); E.Chk(e) {
163 }
164 wg.GetNewReceivingQRCode(qrText)
165 rp.urn = qrText
166 },
167 ),
168 ).
169 Background(bg).
170 Embed(
171 wg.Inset(
172 0.25,
173 wg.VFlex().AlignStart().
174 Rigid(
175 wg.Flex().AlignBaseline().
176 Rigid(
177 wg.Caption(wg.State.receiveAddresses[i].Address).
178 Font("go regular").Fn,
179 ).
180 Flexed(
181 1,
182 wg.Body1(wg.State.receiveAddresses[i].Amount.String()).
183 Alignment(text.End).Fn,
184 ).
185 Fn,
186 ).
187 Rigid(
188 wg.Caption(wg.State.receiveAddresses[i].Message).MaxLines(1).Fn,
189 ).
190 Fn,
191 ).
192 Fn,
193 ).Fn(gtx)
194 },
195 )
196 }
197 return
198 }
199
200 func (rp *ReceivePage) QRMessage() l.Widget {
201 return rp.wg.Body2("Scan to send or click to copy").Alignment(text.Middle).Fn
202 }
203
204 func (rp *ReceivePage) GetQRText() string {
205 wg := rp.wg
206 msg := wg.inputs["receiveMessage"].GetText()
207 if len(msg) > 64 {
208 msg = msg[:64]
209 }
210 return fmt.Sprintf(
211 "parallelcoin:%s?amount=%s&message=%s",
212 wg.State.currentReceivingAddress.Load().EncodeAddress(),
213 wg.inputs["receiveAmount"].GetText(),
214 msg,
215 )
216 }
217
218 func (rp *ReceivePage) QRButton() l.Widget {
219 wg := rp.wg
220 if !wg.WalletAndClientRunning() || wg.currentReceiveQRCode == nil {
221 return func(gtx l.Context) l.Dimensions {
222 return l.Dimensions{}
223 }
224 }
225 return wg.VFlex().
226 Rigid(
227 wg.ButtonLayout(
228 wg.currentReceiveCopyClickable.SetClick(
229 func() {
230 D.Ln("clicked qr code copy clicker")
231 if e := clipboard.WriteAll(rp.urn); E.Chk(e) {
232 }
233 },
234 ),
235 ).
236 Background("white").
237 Embed(
238 wg.Inset(
239 0.125,
240 wg.Image().Src(*wg.currentReceiveQRCode).Scale(1).Fn,
241 ).Fn,
242 ).Fn,
243 ).Rigid(
244 rp.QRMessage(),
245 ).Fn
246 }
247
248 func (rp *ReceivePage) AddressbookHeader() l.Widget {
249 wg := rp.wg
250 return wg.Flex().
251 Rigid(
252 wg.Inset(
253 0.25,
254 wg.H5("Receive Address History").Alignment(text.Middle).Fn,
255 ).Fn,
256 ).Fn
257 }
258
259 func (rp *ReceivePage) AmountInput() l.Widget {
260 return func(gtx l.Context) l.Dimensions {
261 wg := rp.wg
262 // gtx.Constraints.Max.X, gtx.Constraints.Min.X = int(wg.TextSize.True*rp.inputWidth), int(wg.TextSize.True*rp.inputWidth)
263 return wg.inputs["receiveAmount"].Fn(gtx)
264 }
265 }
266
267 func (rp *ReceivePage) MessageInput() l.Widget {
268 return func(gtx l.Context) l.Dimensions {
269 wg := rp.wg
270 // gtx.Constraints.Max.X, gtx.Constraints.Min.X = int(wg.TextSize.True*rp.inputWidth), int(wg.TextSize.True*rp.inputWidth)
271 return wg.inputs["receiveMessage"].Fn(gtx)
272 }
273 }
274
275 func (rp *ReceivePage) RegenerateButton() l.Widget {
276 return func(gtx l.Context) l.Dimensions {
277 wg := rp.wg
278 if wg.inputs["receiveAmount"].GetText() == "" || wg.inputs["receiveMessage"].GetText() == "" {
279 gtx.Queue = nil
280 }
281 // gtx.Constraints.Max.X, gtx.Constraints.Min.X = int(wg.TextSize.True*rp.inputWidth), int(wg.TextSize.True*rp.inputWidth)
282 return wg.ButtonLayout(
283 wg.currentReceiveRegenClickable.
284 SetClick(
285 func() {
286 D.Ln("clicked regenerate button")
287 var amount float64
288 var am amt.Amount
289 var e error
290 if amount, e = strconv.ParseFloat(
291 wg.inputs["receiveAmount"].GetText(),
292 64,
293 ); !E.Chk(e) {
294 if am, e = amt.NewAmount(amount); E.Chk(e) {
295 }
296 }
297 msg := wg.inputs["receiveMessage"].GetText()
298 if am == 0 || msg == "" {
299 // never store an entry without both fields filled
300 return
301 }
302 if len(wg.State.receiveAddresses) > 0 &&
303 (wg.State.receiveAddresses[len(wg.State.receiveAddresses)-1].Amount == 0 ||
304 wg.State.receiveAddresses[len(wg.State.receiveAddresses)-1].Message == "") {
305 // the first entry has neither of these, and newly generated items without them are assumed to
306 // not be intentional or used addresses so we don't generate a new entry for this case
307 wg.State.receiveAddresses[len(wg.State.receiveAddresses)-1].Amount = am
308 wg.State.receiveAddresses[len(wg.State.receiveAddresses)-1].Message = msg
309 } else {
310 // go func() {
311 wg.GetNewReceivingAddress()
312 msg := wg.inputs["receiveMessage"].GetText()
313 if len(msg) > 64 {
314 msg = msg[:64]
315 // enforce the field length limit
316 wg.inputs["receiveMessage"].SetText(msg)
317 }
318 qrText := fmt.Sprintf(
319 "parallelcoin:%s?amount=%f&message=%s",
320 wg.State.currentReceivingAddress.Load().EncodeAddress(),
321 am.ToDUO(),
322 msg,
323 )
324 rp.urn = qrText
325 wg.GetNewReceivingQRCode(rp.urn)
326 // }()
327 }
328 // force user to fill fields again after regenerate to stop duplicate entries especially from
329 // accidental double clicks/taps
330 wg.inputs["receiveAmount"].SetText("")
331 wg.inputs["receiveMessage"].SetText("")
332 wg.Invalidate()
333 },
334 ),
335 ).
336 Background("Primary").
337 Embed(
338 wg.Inset(
339 0.5,
340 wg.H6("regenerate").Color("Light").Fn,
341 ).
342 Fn,
343 ).
344 Fn(gtx)
345 }
346 }
347