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