diff --git a/go.mod b/go.mod index 0e8f4a525..3d0dfb88e 100644 --- a/go.mod +++ b/go.mod @@ -27,5 +27,5 @@ require ( replace ( github.com/decred/dcrdata/txhelpers/v4 => github.com/decred/dcrdata/txhelpers/v4 v4.0.0-20200108145420-f82113e7e212 - github.com/planetdecred/dcrlibwallet => github.com/C-ollins/mobilewallet v1.0.0-rc1.0.20210910121332-e44a98c834c2 + github.com/planetdecred/dcrlibwallet => github.com/C-ollins/mobilewallet v1.0.0-rc1.0.20210912175524-041481a23c8b ) diff --git a/go.sum b/go.sum index 83df901be..cb8bf6501 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,8 @@ github.com/C-ollins/mobilewallet v1.0.0-rc1.0.20210825093648-ec344982588e h1:x6A github.com/C-ollins/mobilewallet v1.0.0-rc1.0.20210825093648-ec344982588e/go.mod h1:sRwfsPrOEnpGBNL54KS83Dpxx2kp2AzZ0Je5vKRRE4o= github.com/C-ollins/mobilewallet v1.0.0-rc1.0.20210910121332-e44a98c834c2 h1:EHZLz82ivykxAkm6BI15FBrPL/PdUsUD2gtrb6pnFpY= github.com/C-ollins/mobilewallet v1.0.0-rc1.0.20210910121332-e44a98c834c2/go.mod h1:sRwfsPrOEnpGBNL54KS83Dpxx2kp2AzZ0Je5vKRRE4o= +github.com/C-ollins/mobilewallet v1.0.0-rc1.0.20210912175524-041481a23c8b h1:JvcNx/P2fSfgXpySx/HVbnRyYo6ZPgnpyVmnar6SuE4= +github.com/C-ollins/mobilewallet v1.0.0-rc1.0.20210912175524-041481a23c8b/go.mod h1:sRwfsPrOEnpGBNL54KS83Dpxx2kp2AzZ0Je5vKRRE4o= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/zstd v1.4.8 h1:Rpmta4xZ/MgZnriKNd24iZMhGpP5dvUcs/uqfBapKZY= github.com/DataDog/zstd v1.4.8/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= diff --git a/ui/load/vsp.go b/ui/load/vsp.go index e872d37a5..ac2099d79 100644 --- a/ui/load/vsp.go +++ b/ui/load/vsp.go @@ -1,7 +1,6 @@ package load import ( - "context" "errors" "fmt" "strings" @@ -95,12 +94,12 @@ func (wl *WalletLoad) GetVSPList() { } wl.MultiWallet.ReadUserConfigValue(dcrlibwallet.VSPHostConfigKey, &valueOut) - var loadedVSP []wallet.VSPInfo + var loadedVSP []*wallet.VSPInfo for _, host := range valueOut.List { v, err := getVSPInfo(host) if err == nil { - loadedVSP = append(loadedVSP, wallet.VSPInfo{ + loadedVSP = append(loadedVSP, &wallet.VSPInfo{ Host: host, Info: v, }) @@ -110,14 +109,14 @@ func (wl *WalletLoad) GetVSPList() { l, _ := getInitVSPInfo("https://api.decred.org/?c=vsp") for h, v := range l { if strings.Contains(wl.Wallet.Net, v.Network) { - loadedVSP = append(loadedVSP, wallet.VSPInfo{ + loadedVSP = append(loadedVSP, &wallet.VSPInfo{ Host: fmt.Sprintf("https://%s", h), Info: v, }) } } - (*wl.VspInfo).List = loadedVSP + wl.VspInfo.List = loadedVSP } // TicketPrice get ticket price @@ -130,40 +129,6 @@ func (wl *WalletLoad) TicketPrice() int64 { return pr.TicketPrice } -func (wl *WalletLoad) NewVSPD(host string, walletID int, accountID int32) (*dcrlibwallet.VSP, error) { - if host == "" { - return nil, fmt.Errorf("Host is required") - } - wall := wl.MultiWallet.WalletWithID(walletID) - if wall == nil { - return nil, ErrIDNotExist - } - vspd, err := wl.MultiWallet.NewVSPClient(host, walletID, uint32(accountID)) - if err != nil { - return nil, fmt.Errorf("Something wrong when creating new VSPD: %v", err) - } - return vspd, nil -} - -func (wl *WalletLoad) PurchaseTicket(walletID int, tickets uint32, passphrase []byte, vspd *dcrlibwallet.VSP) (err error) { - wall := wl.MultiWallet.WalletWithID(walletID) - if wall == nil { - return fmt.Errorf("wallet ID does not exist") - } - - _, err = vspd.GetInfo(context.Background()) - if err != nil { - return err - } - - err = vspd.PurchaseTickets(int32(tickets), wl.MultiWallet.GetBestBlock().Height+256, passphrase) - if err != nil { - return - } - - return -} - func (wl *WalletLoad) AddVSP(host string) (err error) { var valueOut struct { Remember string @@ -190,7 +155,7 @@ func (wl *WalletLoad) AddVSP(host string) (err error) { valueOut.List = append(valueOut.List, host) wl.MultiWallet.SaveUserConfigValue(dcrlibwallet.VSPHostConfigKey, valueOut) - (*wl.VspInfo).List = append((*wl.VspInfo).List, wallet.VSPInfo{ + (*wl.VspInfo).List = append((*wl.VspInfo).List, &wallet.VSPInfo{ Host: host, Info: info, }) diff --git a/ui/page/overview_page.go b/ui/page/overview_page.go index 7fba978d8..bdb6ec282 100644 --- a/ui/page/overview_page.go +++ b/ui/page/overview_page.go @@ -143,13 +143,6 @@ func (pg *OverviewPage) loadTransactions() { // Layout lays out the entire content for overview pg. func (pg *OverviewPage) Layout(gtx layout.Context) layout.Dimensions { pg.queue = gtx - if pg.WL.Info.LoadedWallets == 0 { - return components.UniformPadding(gtx, func(gtx C) D { - return layout.Center.Layout(gtx, func(gtx C) D { - return pg.Theme.H3(values.String(values.StrNoWalletLoaded)).Layout(gtx) - }) - }) - } pageContent := []func(gtx C) D{ func(gtx C) D { diff --git a/ui/page/tickets/purchase_modal.go b/ui/page/tickets/purchase_modal.go index 173bfde0d..19f69db2a 100644 --- a/ui/page/tickets/purchase_modal.go +++ b/ui/page/tickets/purchase_modal.go @@ -5,11 +5,10 @@ import ( "image/color" "strconv" - "gioui.org/gesture" "gioui.org/layout" "gioui.org/widget" - "github.com/decred/dcrd/dcrutil" + "github.com/decred/dcrd/dcrutil/v3" "github.com/planetdecred/dcrlibwallet" "github.com/planetdecred/godcr/ui/decredmaterial" "github.com/planetdecred/godcr/ui/load" @@ -22,23 +21,19 @@ const purchaseModalID = "ticket_purchase_modal" type ticketPurchaseModal struct { *load.Load - ticketPrice string - totalCost int64 - balanceLessCost int64 - vspIsFetched bool - isPurchaseLoading bool + ticketPrice dcrutil.Amount + totalCost int64 + balanceLessCost int64 + vspIsFetched bool modal decredmaterial.Modal tickets decredmaterial.Editor rememberVSP decredmaterial.CheckBoxStyle - selectVSP []*gesture.Click cancelPurchase decredmaterial.Button reviewPurchase decredmaterial.Button accountSelector *components.AccountSelector vspSelector *vspSelector - - vsp *dcrlibwallet.VSP } func newTicketPurchaseModal(l *load.Load) *ticketPurchaseModal { @@ -60,6 +55,22 @@ func newTicketPurchaseModal(l *load.Load) *ticketPurchaseModal { return tp } +func (tp *ticketPurchaseModal) OnResume() { + tp.initializeAccountSelector() + err := tp.accountSelector.SelectFirstWalletValidAccount() + if err != nil { + tp.Toast.NotifyError(err.Error()) + } + + tp.vspSelector = newVSPSelector(tp.Load).title("Select a vsp") + tp.ticketPrice = dcrutil.Amount(tp.WL.TicketPrice()) + + if tp.vspIsFetched && tp.WL.GetRememberVSP() != "" { + tp.vspSelector.selectVSP(tp.WL.GetRememberVSP()) + tp.rememberVSP.CheckBox.Value = true + } +} + func (tp *ticketPurchaseModal) Layout(gtx layout.Context) layout.Dimensions { l := []layout.Widget{ func(gtx C) D { @@ -73,28 +84,30 @@ func (tp *ticketPurchaseModal) Layout(gtx layout.Context) layout.Dimensions { }), layout.Rigid(func(gtx C) D { return layout.Inset{Top: values.MarginPadding8}.Layout(gtx, func(gtx C) D { - return components.LayoutBalance(gtx, tp.Load, tp.ticketPrice) + return components.LayoutBalanceSize(gtx, tp.Load, tp.ticketPrice.String(), values.Size28) }) }), ) }) }), layout.Rigid(func(gtx C) D { - return layout.Flex{}.Layout(gtx, - layout.Flexed(.5, func(gtx C) D { - return layout.Flex{Axis: layout.Vertical}.Layout(gtx, - layout.Rigid(func(gtx C) D { - tit := tp.Theme.Label(values.TextSize14, "Total") - tit.Color = tp.Theme.Color.Gray2 - return tit.Layout(gtx) - }), - layout.Rigid(func(gtx C) D { - return tp.Theme.Label(values.TextSize16, tp.ticketPrice).Layout(gtx) - }), - ) - }), - layout.Flexed(.5, tp.tickets.Layout), - ) + return layout.Inset{Top: values.MarginPadding8}.Layout(gtx, func(gtx C) D { + return layout.Flex{}.Layout(gtx, + layout.Flexed(.5, func(gtx C) D { + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + layout.Rigid(func(gtx C) D { + tit := tp.Theme.Label(values.TextSize14, "Total") + tit.Color = tp.Theme.Color.Gray3 + return tit.Layout(gtx) + }), + layout.Rigid(func(gtx C) D { + return tp.Theme.Label(values.TextSize16, dcrutil.Amount(int64(tp.ticketPrice)*tp.ticketCount()).String()).Layout(gtx) + }), + ) + }), + layout.Flexed(.5, tp.tickets.Layout), + ) + }) }), ) }, @@ -145,7 +158,7 @@ func (tp *ticketPurchaseModal) ticketCount() int64 { } func (tp *ticketPurchaseModal) canPurchase() bool { - if tp.vspSelector.selectedVSP.Info == nil { + if tp.vspSelector.selectedVSP == nil { return false } @@ -155,10 +168,6 @@ func (tp *ticketPurchaseModal) canPurchase() bool { return false } - if tp.vspSelector.selectedVSP.Host == "" { - return false - } - if tp.ticketCount() < 1 { return false } @@ -178,22 +187,6 @@ func (tp *ticketPurchaseModal) Dismiss() { tp.DismissModal(tp) } -func (tp *ticketPurchaseModal) OnResume() { - tp.initializeAccountSelector() - err := tp.accountSelector.SelectFirstWalletValidAccount() - if err != nil { - tp.Toast.NotifyError(err.Error()) - } - - tp.vspSelector = newVSPSelector(tp.Load).title("Select a vsp") - tp.ticketPrice = dcrutil.Amount(tp.WL.TicketPrice()).String() - - if tp.vspIsFetched && tp.WL.GetRememberVSP() != "" { - tp.vspSelector.selectVSP(tp.WL.GetRememberVSP()) - tp.rememberVSP.CheckBox.Value = true - } -} - func (tp *ticketPurchaseModal) initializeAccountSelector() { tp.accountSelector = components.NewAccountSelector(tp.Load). Title("Purchasing account"). @@ -216,35 +209,44 @@ func (tp *ticketPurchaseModal) initializeAccountSelector() { func (tp *ticketPurchaseModal) OnDismiss() {} func (tp *ticketPurchaseModal) calculateTotals() { - accountBalance := tp.accountSelector.SelectedAccount().Balance.Spendable - feePercentage := tp.vspSelector.selectedVSP.Info.FeePercentage - total := tp.WL.TicketPrice() * tp.ticketCount() - fee := int64((float64(total) / 100) * feePercentage) - tp.totalCost = total + fee - tp.balanceLessCost = accountBalance - tp.totalCost -} + account := tp.accountSelector.SelectedAccount() + wal := tp.WL.MultiWallet.WalletWithID(account.WalletID) -func (tp *ticketPurchaseModal) createNewVSPD() { - selectedAccount := tp.accountSelector.SelectedAccount() - selectedVSP := tp.vspSelector.SelectedVSP() - vspd, err := tp.WL.NewVSPD(selectedVSP.Host, selectedAccount.WalletID, selectedAccount.Number) + ticketPrice, err := wal.TicketPrice() if err != nil { tp.Toast.NotifyError(err.Error()) + return } - tp.vsp = vspd + + feePercentage := tp.vspSelector.selectedVSP.Info.FeePercentage + total := ticketPrice.TicketPrice * tp.ticketCount() + fee := int64((float64(total) / 100) * feePercentage) + + tp.totalCost = total + fee + tp.balanceLessCost = account.Balance.Spendable - tp.totalCost } func (tp *ticketPurchaseModal) purchaseTickets(password []byte) { tp.Dismiss() - tp.Toast.Notify(fmt.Sprintf("attempting to purchase %v ticket(s)", tp.ticketCount())) + tp.Toast.Notify(fmt.Sprintf("Attempting to purchase %v ticket(s)", tp.ticketCount())) go func() { + selectedVSP := tp.vspSelector.SelectedVSP() account := tp.accountSelector.SelectedAccount() - err := tp.WL.PurchaseTicket(account.WalletID, uint32(tp.ticketCount()), password, tp.vsp) + wal := tp.WL.MultiWallet.WalletWithID(account.WalletID) + + vsp, err := tp.WL.MultiWallet.NewVSPClient(selectedVSP.Host, account.WalletID, uint32(account.Number)) if err != nil { tp.Toast.NotifyError(err.Error()) return } + + err = vsp.PurchaseTickets(int32(tp.ticketCount()), wal.GetBestBlock()+256, password) + if err != nil { + tp.Toast.NotifyError(err.Error()) + return + } + tp.Toast.Notify(fmt.Sprintf("%v ticket(s) purchased successfully", tp.ticketCount())) }() } @@ -263,7 +265,6 @@ func (tp *ticketPurchaseModal) Handle() { } if tp.reviewPurchase.Button.Clicked() && tp.canPurchase() { - go tp.createNewVSPD() if tp.vspSelector.Changed() && tp.rememberVSP.CheckBox.Value { tp.WL.RememberVSP(tp.vspSelector.selectedVSP.Host) diff --git a/ui/page/tickets/utils.go b/ui/page/tickets/utils.go index 5ad7815d9..c1a224cc2 100644 --- a/ui/page/tickets/utils.go +++ b/ui/page/tickets/utils.go @@ -341,7 +341,7 @@ func ticketCard(gtx layout.Context, l *load.Load, tx *transactionItem, showWalle durationLayout := layout.Flex{Alignment: layout.Middle}.Layout(gtx, layout.Rigid(func(gtx C) D { - return layout.Inset{Right: values.MarginPadding4}.Layout(gtx, l.Icons.TimerIcon.Layout) + return layout.Inset{Right: values.MarginPadding4}.Layout(gtx, l.Icons.TimerIcon.Layout12dp) }), layout.Rigid(txt.Layout), ) diff --git a/ui/page/tickets/vsp_selector.go b/ui/page/tickets/vsp_selector.go index d63209b2f..aaacc84df 100644 --- a/ui/page/tickets/vsp_selector.go +++ b/ui/page/tickets/vsp_selector.go @@ -23,14 +23,12 @@ type vspSelector struct { changed bool showVSPModal *widget.Clickable - vspInfo *wallet.VSP - selectedVSP wallet.VSPInfo + selectedVSP *wallet.VSPInfo } func newVSPSelector(l *load.Load) *vspSelector { v := &vspSelector{ Load: l, - vspInfo: l.WL.VspInfo, showVSPModal: new(widget.Clickable), } return v @@ -48,7 +46,7 @@ func (v *vspSelector) Changed() bool { } func (v *vspSelector) selectVSP(vspHost string) { - for _, vsp := range (*v.vspInfo).List { + for _, vsp := range v.WL.VspInfo.List { if vsp.Host == vspHost { v.changed = true v.selectedVSP = vsp @@ -57,7 +55,7 @@ func (v *vspSelector) selectVSP(vspHost string) { } } -func (v *vspSelector) SelectedVSP() wallet.VSPInfo { +func (v *vspSelector) SelectedVSP() *wallet.VSPInfo { return v.selectedVSP } @@ -65,7 +63,7 @@ func (v *vspSelector) handle() { if v.showVSPModal.Clicked() { newVSPSelectorModal(v.Load). title("Voting service provider"). - vspSelected(func(info wallet.VSPInfo) { + vspSelected(func(info *wallet.VSPInfo) { v.selectVSP(info.Host) }). Show() @@ -86,7 +84,7 @@ func (v *vspSelector) Layout(gtx layout.Context) layout.Dimensions { return decredmaterial.Clickable(gtx, v.showVSPModal, func(gtx C) D { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, layout.Rigid(func(gtx C) D { - if v.selectedVSP.Host == "" { + if v.selectedVSP == nil { txt := v.Theme.Label(values.TextSize16, "Select VSP...") txt.Color = v.Theme.Color.Gray2 return txt.Layout(gtx) @@ -97,7 +95,7 @@ func (v *vspSelector) Layout(gtx layout.Context) layout.Dimensions { return layout.E.Layout(gtx, func(gtx C) D { return layout.Flex{}.Layout(gtx, layout.Rigid(func(gtx C) D { - if v.selectedVSP.Info == nil { + if v.selectedVSP == nil { return layout.Dimensions{} } txt := v.Theme.Label(values.TextSize16, fmt.Sprintf("%v%%", v.selectedVSP.Info.FeePercentage)) @@ -132,19 +130,17 @@ type vspSelectorModal struct { inputVSP decredmaterial.Editor addVSP decredmaterial.Button - vspInfo *wallet.VSP vspHosts *layout.List selectVSP []*gesture.Click - selectedVSP wallet.VSPInfo + selectedVSP *wallet.VSPInfo - vspSelectedCallback func(wallet.VSPInfo) + vspSelectedCallback func(*wallet.VSPInfo) } func newVSPSelectorModal(l *load.Load) *vspSelectorModal { v := &vspSelectorModal{ Load: l, - vspInfo: l.WL.VspInfo, inputVSP: l.Theme.Editor(new(widget.Editor), "Add a new VSP..."), addVSP: l.Theme.Button(new(widget.Clickable), "Save"), vspHosts: &layout.List{Axis: layout.Vertical}, @@ -182,7 +178,7 @@ func (v *vspSelectorModal) Handle() { }() } - vspList := (*v.vspInfo).List + vspList := v.WL.VspInfo.List if len(vspList) != len(v.selectVSP) { v.selectVSP = createClickGestures(len(vspList)) } @@ -193,7 +189,7 @@ func (v *vspSelectorModal) title(title string) *vspSelectorModal { return v } -func (v *vspSelectorModal) vspSelected(callback func(wallet.VSPInfo)) *vspSelectorModal { +func (v *vspSelectorModal) vspSelected(callback func(*wallet.VSPInfo)) *vspSelectorModal { v.vspSelectedCallback = callback v.Dismiss() return v @@ -218,8 +214,8 @@ func (v *vspSelectorModal) Layout(gtx layout.Context) layout.Dimensions { }) }), layout.Rigid(func(gtx C) D { - listVSP := (*v.vspInfo).List - return v.vspHosts.Layout(gtx, len(v.selectVSP), func(gtx C, i int) D { + listVSP := v.WL.VspInfo.List + return v.vspHosts.Layout(gtx, len(listVSP), func(gtx C, i int) D { click := v.selectVSP[i] pointer.Rect(image.Rectangle{Max: gtx.Constraints.Max}).Add(gtx.Ops) click.Add(gtx.Ops) @@ -234,7 +230,7 @@ func (v *vspSelectorModal) Layout(gtx layout.Context) layout.Dimensions { }) }), layout.Rigid(func(gtx C) D { - if v.selectedVSP.Host != listVSP[i].Host { + if v.selectedVSP != nil || v.selectedVSP != listVSP[i] { return layout.Inset{Right: values.MarginPadding40}.Layout(gtx, func(gtx C) D { return layout.Dimensions{} }) @@ -257,7 +253,7 @@ func (v *vspSelectorModal) Layout(gtx layout.Context) layout.Dimensions { }, 900) } -func (v *vspSelectorModal) handlerSelectVSP(events []gesture.ClickEvent, info wallet.VSPInfo) { +func (v *vspSelectorModal) handlerSelectVSP(events []gesture.ClickEvent, info *wallet.VSPInfo) { for _, e := range events { if e.Type == gesture.TypeClick { v.selectedVSP = info diff --git a/ui/state.go b/ui/state.go index 465abcc12..6c2803ff8 100644 --- a/ui/state.go +++ b/ui/state.go @@ -51,7 +51,7 @@ func (win *Window) updateStates(update interface{}) { win.walletUnspentOutputs = e case *wallet.VSPInfo: win.states.loading = false - win.vspInfo.List = append(win.vspInfo.List, *e) + // win.vspInfo.List = append(win.vspInfo.List, *e) return case *wallet.VSP: win.vspInfo = e diff --git a/ui/values/dimensions.go b/ui/values/dimensions.go index 40a0ee64c..d02c4cbf1 100644 --- a/ui/values/dimensions.go +++ b/ui/values/dimensions.go @@ -4,6 +4,7 @@ import "gioui.org/unit" var ( Size0_5 = unit.Dp(0.5) + Size28 = unit.Dp(28) MarginPadding0 = unit.Dp(0) MarginPadding1 = unit.Dp(1) diff --git a/ui/window.go b/ui/window.go index 15eebe1a9..5b33f557a 100644 --- a/ui/window.go +++ b/ui/window.go @@ -288,7 +288,7 @@ func (win *Window) Loop(w *app.Window, shutdown chan int) { break } - win.updateStates(e.Resp) + // win.updateStates(e.Resp) case update := <-win.wallet.Sync: switch update.Stage { diff --git a/wallet/responses.go b/wallet/responses.go index 42565655f..ed78a3d97 100644 --- a/wallet/responses.go +++ b/wallet/responses.go @@ -205,7 +205,7 @@ type VSPInfo struct { // VSP is sent when the Wallet is done getting all VSP info type VSP struct { - List []VSPInfo + List []*VSPInfo } // Proposals is sent when all proposals has been fetched