Skip to content

Commit

Permalink
dex bug fixes (#678)
Browse files Browse the repository at this point in the history
- Add info icon for lots
- Fix add server page when visited from markets page so users can return to the
  markets page.
- Disable switch to amount input on the order form.
- Fix server dropdown bug
- Ensure btn on post bond page spins on click.

Signed-off-by: Philemon Ukane <ukanephilemon@gmail.com>
  • Loading branch information
ukane-philemon authored Oct 2, 2024
1 parent 0647530 commit aae288c
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 49 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/crypto-power/cryptopower
go 1.21

require (
decred.org/dcrdex v0.6.3
decred.org/dcrdex v1.0.0
decred.org/dcrwallet/v4 v4.1.3
gioui.org v0.7.1
git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0
Expand Down Expand Up @@ -225,4 +225,4 @@ require (
replace github.com/lib/pq => github.com/lib/pq v1.10.4

// https://github.com/ukane-philemon/dcrdex/tree/btc-node
replace decred.org/dcrdex v0.6.3 => github.com/ukane-philemon/dcrdex v0.0.0-20240906090529-912997266ecf
replace decred.org/dcrdex v1.0.0 => github.com/ukane-philemon/dcrdex v0.0.0-20240906090529-912997266ecf
4 changes: 4 additions & 0 deletions ui/cryptomaterial/dropdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@ func (d *DropDown) collapsedAndExpandedLayout(gtx C) D {
func (d *DropDown) expandedLayout(gtx C) D {
m := op.Record(gtx.Ops)
gtx.Constraints.Min.Y = gtx.Constraints.Max.Y
// This allows the dropdown to over lap other elements on the screen and not
// limit the dropdown items to the height of its parent
// (gtx.Constraints.Max.Y).
gtx.Constraints.Max.Y = inf
d.updateDropdownWidth(gtx, true)
d.updateDropdownBackground(true)
d.ExpandedLayoutInset.Layout(gtx, func(gtx C) D {
Expand Down
2 changes: 1 addition & 1 deletion ui/page/dcrdex/dcrdex_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (pg *DEXPage) prepareInitialPage() {
}

if showOnBoardingPage {
pg.Display(NewDEXOnboarding(pg.Load, ""))
pg.Display(NewDEXOnboarding(pg.Load, "", nil))
} else {
pg.Display(NewDEXMarketPage(pg.Load, ""))
}
Expand Down
74 changes: 43 additions & 31 deletions ui/page/dcrdex/dex_onboarding_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,13 @@ type DEXOnboarding struct {
bondServer *bondServerInfo

// Sub Step Add Server
wantCustomServer bool
serverURLEditor cryptomaterial.Editor
serverCertEditor cryptomaterial.Editor
goBackToChooseServer *cryptomaterial.Clickable
wantCustomServer bool
serverURLEditor cryptomaterial.Editor
serverCertEditor cryptomaterial.Editor
// goBackToChooseServerOrMarketPage redirects a user back to the choose
// server screen if dex has not initialized and no previously registered dex
// server is found, else the user is redirect to the markets page.
goBackToChooseServerOrMarketPage *cryptomaterial.Clickable
// TODO: add a file selector to choose server cert.

// Step Post Bond
Expand All @@ -140,37 +143,39 @@ type DEXOnboarding struct {
goBackBtn cryptomaterial.Button
nextBtn cryptomaterial.Button

materialLoader material.LoaderStyle
isLoading bool
existingDEXServer string
materialLoader material.LoaderStyle
isLoading bool
existingDEXServer string
redirectToMarketPageFn func()

bondFeeCache map[uint32]uint64
}

// NewDEXOnboarding creates a new DEX onboarding pages. Specify
// existingDEXServer to use the DEX onboarding flow to allow user post bonds for
// a particular server.
func NewDEXOnboarding(l *load.Load, existingDEXServer string) *DEXOnboarding {
func NewDEXOnboarding(l *load.Load, existingDEXServer string, redirectToMarketPageFn func()) *DEXOnboarding {
th := l.Theme
pg := &DEXOnboarding{
Load: l,
GenericPageModal: app.NewGenericPageModal(DEXOnboardingPageID),
scrollContainer: &widget.List{List: layout.List{Axis: layout.Vertical, Alignment: layout.Middle}},
passwordEditor: newPasswordEditor(th, values.String(values.StrNewPassword)),
confirmPasswordEditor: newPasswordEditor(th, values.String(values.StrConfirmPassword)),
seedEditor: newTextEditor(l.Theme, values.String(values.StrOptionalRestorationSeed), values.String(values.StrOptionalRestorationSeed), true),
addServerBtn: th.NewClickable(false),
bondServer: &bondServerInfo{},
serverURLEditor: newTextEditor(th, values.String(values.StrServerURL), values.String(values.StrInputURL), false),
serverCertEditor: newTextEditor(th, values.String(values.StrCertificateOPtional), values.String(values.StrInputCertificate), true),
goBackToChooseServer: th.NewClickable(false),
bondStrengthEditor: newTextEditor(th, values.String(values.StrBondStrength), "1", false),
bondStrengthMoreInfo: th.NewClickable(false),
goBackBtn: th.Button(values.String(values.StrBack)),
nextBtn: th.Button(values.String(values.StrNext)),
materialLoader: material.Loader(th.Base),
existingDEXServer: existingDEXServer,
bondFeeCache: make(map[uint32]uint64),
Load: l,
GenericPageModal: app.NewGenericPageModal(DEXOnboardingPageID),
scrollContainer: &widget.List{List: layout.List{Axis: layout.Vertical, Alignment: layout.Middle}},
passwordEditor: newPasswordEditor(th, values.String(values.StrNewPassword)),
confirmPasswordEditor: newPasswordEditor(th, values.String(values.StrConfirmPassword)),
seedEditor: newTextEditor(l.Theme, values.String(values.StrOptionalRestorationSeed), values.String(values.StrOptionalRestorationSeed), true),
addServerBtn: th.NewClickable(false),
bondServer: &bondServerInfo{},
serverURLEditor: newTextEditor(th, values.String(values.StrServerURL), values.String(values.StrInputURL), false),
serverCertEditor: newTextEditor(th, values.String(values.StrCertificateOPtional), values.String(values.StrInputCertificate), true),
goBackToChooseServerOrMarketPage: th.NewClickable(false),
bondStrengthEditor: newTextEditor(th, values.String(values.StrBondStrength), "1", false),
bondStrengthMoreInfo: th.NewClickable(false),
goBackBtn: th.Button(values.String(values.StrBack)),
nextBtn: th.Button(values.String(values.StrNext)),
materialLoader: material.Loader(th.Base),
existingDEXServer: existingDEXServer,
bondFeeCache: make(map[uint32]uint64),
redirectToMarketPageFn: redirectToMarketPageFn,
}

pg.onBoardingSteps = map[onboardingStep]dexOnboardingStep{
Expand Down Expand Up @@ -260,7 +265,6 @@ func (pg *DEXOnboarding) OnNavigatedFrom() {
// to be eventually drawn on screen.
// Part of the load.Page interface.
func (pg *DEXOnboarding) Layout(gtx C) D {
pg.handleEditorEvents(gtx)
if !pg.AssetsManager.DEXCInitialized() {
pg.ParentNavigator().CloseCurrentPage()
return D{}
Expand Down Expand Up @@ -463,15 +467,15 @@ func (pg *DEXOnboarding) subStepAddServer(gtx C) D {
Alignment: layout.Middle,
}.Layout(gtx,
layout.Rigid(func(gtx C) D {
if !pg.wantCustomServer {
if !pg.wantCustomServer && pg.redirectToMarketPageFn == nil {
return D{}
}

return cryptomaterial.LinearLayout{
Width: cryptomaterial.WrapContent,
Height: cryptomaterial.WrapContent,
Orientation: layout.Horizontal,
Clickable: pg.goBackToChooseServer,
Clickable: pg.goBackToChooseServerOrMarketPage,
}.Layout(gtx,
layout.Rigid(func(gtx C) D {
return pg.Theme.Icons.NavigationArrowBack.Layout(gtx, pg.Theme.Color.Gray1)
Expand Down Expand Up @@ -1047,6 +1051,8 @@ func (pg *DEXOnboarding) handleEditorEvents(gtx C) {
pg.isLoading = false
}()
}

pg.ParentWindow().Reload()
}
}

Expand All @@ -1064,9 +1070,13 @@ func (pg *DEXOnboarding) HandleUserInteractions(gtx C) {
pg.serverCertEditor.Editor.SetText("")
}

if pg.goBackToChooseServer.Clicked(gtx) {
if pg.goBackToChooseServerOrMarketPage.Clicked(gtx) {
pg.wantCustomServer = false
pg.currentStep = onboardingChooseServer
if pg.redirectToMarketPageFn != nil {
pg.redirectToMarketPageFn()
} else {
pg.currentStep = onboardingChooseServer
}
pg.serverURLEditor.SetError("")
pg.serverCertEditor.SetError("")
}
Expand Down Expand Up @@ -1105,6 +1115,8 @@ func (pg *DEXOnboarding) HandleUserInteractions(gtx C) {
if pg.bondSourceAccountSelector != nil {
pg.bondSourceAccountSelector.Handle(gtx)
}

pg.handleEditorEvents(gtx)
}

func (pg *DEXOnboarding) setAddServerStep() {
Expand Down
72 changes: 58 additions & 14 deletions ui/page/dcrdex/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,16 @@ type DEXMarketPage struct {
toggleBuyAndSellBtn *cryptomaterial.SegmentedControl
orderTypesDropdown *cryptomaterial.DropDown

priceEditor cryptomaterial.Editor
priceEditor cryptomaterial.Editor
// TODO: Remove switchLotsOrAmount and related checks for amounts input on
// lotsOrAmountEditor. It seems we prefer users learning how to trade with
// lots since it's more straight forward. If we intend to allow users
// provide an amount and convert to lots for them before this todo is done,
// we can just just display this switch instead of the lot size.
switchLotsOrAmount *cryptomaterial.Switch
lotsOrAmountEditor cryptomaterial.Editor
totalEditor cryptomaterial.Editor
lotsInfoBtn *cryptomaterial.Clickable

maxBuyOrSellStr string
orderFeeEstimateStr string
Expand Down Expand Up @@ -155,6 +161,7 @@ func NewDEXMarketPage(l *load.Load, selectServer string) *DEXMarketPage {
priceEditor: newTextEditor(l.Theme, values.String(values.StrPrice), "", false),
switchLotsOrAmount: l.Theme.Switch(),
lotsOrAmountEditor: newTextEditor(l.Theme, values.String(values.StrLots), "", false),
lotsInfoBtn: th.NewClickable(false),
totalEditor: newTextEditor(th, values.String(values.StrTotal), "", false),
maxBuyOrSellStr: "---",
orderFeeEstimateStr: "------",
Expand Down Expand Up @@ -779,10 +786,10 @@ func (pg *DEXMarketPage) orderForm(gtx C) D {
} else {
if sell { // Show base asset available balance.
tradeDirection = values.String(values.StrSell)
availableAssetBal, baseOrQuoteAssetSym = pg.availableWalletAccountBalanceString(false)
availableAssetBal, baseOrQuoteAssetSym = pg.availableWalletAccountBalance(false)
} else {
tradeDirection = values.String(values.StrBuy)
availableAssetBal, baseOrQuoteAssetSym = pg.availableWalletAccountBalanceString(true)
availableAssetBal, baseOrQuoteAssetSym = pg.availableWalletAccountBalance(true)
}
}

Expand Down Expand Up @@ -820,15 +827,28 @@ func (pg *DEXMarketPage) orderForm(gtx C) D {
layout.Rigid(func(gtx C) D {
return layout.Inset{Bottom: dp5}.Layout(gtx, func(gtx C) D {
var labelText string
var lotSize string
if pg.orderWithLots() {
labelText = fmt.Sprintf("%s (%s)", values.String(values.StrLots), lotsOrAmountSubtext)
if mkt := pg.selectedMarketInfo(); mkt != nil {
lotSize = values.StringF(values.StrLotSizeFmt, fmt.Sprintf("%s %s", trimmedConventionalAmtString(mkt.MsgRateToConventional(mkt.LotSize)), convertAssetIDToAssetType(pg.selectedMarketOrderBook.base)))
}
} else {
labelText = fmt.Sprintf("%s (%s)", values.String(values.StrAmount), lotsOrAmountSubtext)
}
return layout.Flex{Axis: horizontal}.Layout(gtx,
layout.Rigid(pg.semiBoldLabelText(labelText).Layout),
layout.Rigid(func(gtx C) D {
return layout.Inset{Top: dp5, Left: dp2}.Layout(gtx, func(gtx C) D {
return pg.lotsInfoBtn.Layout(gtx, pg.Theme.Icons.InfoAction.Layout16dp)
})
}),
layout.Flexed(1, func(gtx C) D {
return layout.E.Layout(gtx, pg.switchLotsOrAmount.Layout)
if lotSize == "" {
return D{}
}

return layout.E.Layout(gtx, pg.Theme.Label(values.TextSize14, lotSize).Layout)
}),
)
})
Expand Down Expand Up @@ -998,10 +1018,9 @@ func trimZeros(s string) string {
return strings.TrimSuffix(strings.TrimRight(s, "0"), ".")
}

// availableWalletAccountBalanceString returns the balance of the DEX wallet
// account for the quote or base asset of the selected market. Returns the
// wallet's spendable balance as string.
func (pg *DEXMarketPage) availableWalletAccountBalanceString(forQuoteAsset bool) (bal float64, assetSym string) {
// availableWalletAccountBalance returns the balance of the DEX wallet account
// for the quote or base asset of the selected market.
func (pg *DEXMarketPage) availableWalletAccountBalance(forQuoteAsset bool) (bal float64, assetSym string) {
if pg.noMarketOrServerDisconnected.Load() {
return 0, ""
}
Expand Down Expand Up @@ -1348,6 +1367,8 @@ func (pg *DEXMarketPage) setBuyOrSell() {
pg.lotsOrAmountEditor.UpdateFocus(!pg.lotsOrAmountEditor.Editor.ReadOnly)
pg.totalEditor.Editor.ReadOnly = isSell
pg.totalEditor.UpdateFocus(!pg.totalEditor.Editor.ReadOnly)
pg.lotsOrAmountEditor.Editor.SetText("")
pg.totalEditor.Editor.SetText("")

if !isSell { // Buy
pg.createOrderBtn.Text = values.String(values.StrBuy)
Expand Down Expand Up @@ -1474,15 +1495,17 @@ func (pg *DEXMarketPage) HandleUserInteractions(gtx C) {
selectedServer := pg.serverSelector.Selected()
xc, err := dexc.Exchange(selectedServer)
if err != nil && xc.Auth.EffectiveTier == 0 /* need to post bond now */ {
pg.ParentNavigator().ClearStackAndDisplay(NewDEXOnboarding(pg.Load, selectedServer))
pg.ParentNavigator().ClearStackAndDisplay(NewDEXOnboarding(pg.Load, selectedServer, nil))
} else {
pg.lastSelectedDEXServer = selectedServer
pg.setServerMarkets()
}
}

if pg.addServerBtn.Clicked(gtx) {
pg.ParentNavigator().ClearStackAndDisplay(NewDEXOnboarding(pg.Load, ""))
pg.ParentNavigator().ClearStackAndDisplay(NewDEXOnboarding(pg.Load, "", func() {
pg.ParentNavigator().ClearStackAndDisplay(NewDEXMarketPage(pg.Load, ""))
}))
}

if pg.openOrdersBtn.Clicked(gtx) {
Expand All @@ -1506,6 +1529,18 @@ func (pg *DEXMarketPage) HandleUserInteractions(gtx C) {
log.Info("button click listener for full order book view is not implemented")
}

if pg.lotsInfoBtn.Clicked(gtx) {
infoModal := modal.NewCustomModal(pg.Load).
Title(values.String(values.StrLots)).
UseCustomWidget(func(gtx layout.Context) layout.Dimensions {
return pg.Theme.Body2(values.String(values.StrLotsExplanation)).Layout(gtx)
}).
SetCancelable(true).
SetContentAlignment(layout.W, layout.W, layout.Center).
SetPositiveButtonText(values.String(values.StrOk))
pg.ParentWindow().ShowModal(infoModal)
}

if pg.immediateOrderInfoBtn.Clicked(gtx) {
infoModal := modal.NewCustomModal(pg.Load).
Title(values.String(values.StrImmediateOrder)).
Expand All @@ -1520,7 +1555,7 @@ func (pg *DEXMarketPage) HandleUserInteractions(gtx C) {

// TODO: postBondBtn should open a separate page when its design is ready.
if pg.postBondBtn.Clicked(gtx) {
pg.ParentNavigator().ClearStackAndDisplay(NewDEXOnboarding(pg.Load, pg.serverSelector.Selected()))
pg.ParentNavigator().ClearStackAndDisplay(NewDEXOnboarding(pg.Load, pg.serverSelector.Selected(), nil))
}

if pg.loginBtn.Clicked(gtx) {
Expand Down Expand Up @@ -1827,9 +1862,16 @@ func anyMatchActive(matches []*core.Match) bool {
func (pg *DEXMarketPage) hasValidOrderInfo() bool {
mkt := pg.selectedMarketInfo()
_, lotsOrAmtOk := pg.orderLotsOrAmt()
_, totalOk := pg.totalOrderAmt()
orderAmt, totalOk := pg.totalOrderAmt()
// TODO: Check that their tier limit has not been exceeded by this trade.
return pg.orderPrice(mkt) > 0 && lotsOrAmtOk && totalOk
orderPriceIsOk := pg.orderPrice(mkt) > 0 && lotsOrAmtOk && totalOk
if !orderPriceIsOk {
return false
}

// Fetch wallet balance from dex and ensure wallet can fund dex order.
walletBalance, _ := pg.availableWalletAccountBalance(!pg.isSellOrder())
return orderPriceIsOk && orderAmt < walletBalance
}

func (pg *DEXMarketPage) orderLotsOrAmt() (float64, bool) {
Expand Down Expand Up @@ -1867,13 +1909,15 @@ func (pg *DEXMarketPage) calculateOrderAmount(mkt *core.Market, isSwitchLotsOrAm
if !pg.isSellOrder() {
amtStr := pg.totalEditor.Editor.Text()
if amtStr == "" {
pg.lotsOrAmountEditor.Editor.SetText("")
return false
}

// It's a buy order, user supplies how much total they want to buy and
// we calculate based on that.
totalAmt, err := strconv.ParseFloat(amtStr, 64)
if err != nil || totalAmt <= 0 {
pg.lotsOrAmountEditor.Editor.SetText("")
pg.totalEditor.SetError(values.String(values.StrInvalidAmount))
return false
}
Expand All @@ -1898,7 +1942,7 @@ func (pg *DEXMarketPage) calculateOrderAmount(mkt *core.Market, isSwitchLotsOrAm

pg.totalEditor.Editor.SetText("")
if pg.orderWithLots() {
if lots, err := strconv.ParseFloat(lotsOrAmtStr, 64); err != nil || lots <= 0 {
if lots, err := strconv.ParseFloat(lotsOrAmtStr, 64); err != nil || lots <= 0 || float64(int64(lots)) != lots {
pg.lotsOrAmountEditor.SetError(values.String(values.StrInvalidLot))
} else {
if isSwitchLotsOrAmtChanged {
Expand Down
4 changes: 3 additions & 1 deletion ui/values/localizable/en.go
Original file line number Diff line number Diff line change
Expand Up @@ -847,8 +847,10 @@ const EN = `
"24hVolume" = "24h Volume (%s)"
"24hHigh" = "24h High"
"lots" = "Lots"
"lotSizeFmt" = "1 Lot = %v"
"lotsExplanation" = "The Lot size is the minimum amount you can buy or sell in one trade. Lot sizes are chosen to minimize the on chain fees to below ~1% of each trade value. If you want to buy or sell 10 (or any number) lots, total order quantity will be -> (number of lots * lots size) denominated in the base currency."
"invalidPrice" = "Invalid price"
"invalidLot" = "Invalid lot"
"invalidLot" = "Invalid lot: Lot must be a valid non-decimal number"
"minMaxLot" = "Min Lots: %d, Max Lots: %d"
"buy" = "Buy"
"sell" = "Sell"
Expand Down
2 changes: 2 additions & 0 deletions ui/values/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,8 @@ const (
Str24hVolume = "24hVolume"
Str24hHigh = "24hHigh"
StrLots = "lots"
StrLotSizeFmt = "lotSizeFmt"
StrLotsExplanation = "lotsExplanation"
StrInvalidLot = "invalidLot"
StrInvalidPrice = "invalidPrice"
StrBuy = "buy"
Expand Down

0 comments on commit aae288c

Please sign in to comment.