Skip to content

Commit

Permalink
update logic get exchange rate
Browse files Browse the repository at this point in the history
  • Loading branch information
JustinBeBoy committed May 15, 2024
1 parent 650344a commit 46d028a
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 25 deletions.
17 changes: 16 additions & 1 deletion libwallet/assets_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/crypto-power/cryptopower/libwallet/instantswap"
"github.com/crypto-power/cryptopower/libwallet/internal/politeia"
"github.com/crypto-power/cryptopower/libwallet/utils"
"github.com/crypto-power/cryptopower/ui/notification"
"github.com/crypto-power/cryptopower/ui/values"
bolt "go.etcd.io/bbolt"

Expand Down Expand Up @@ -68,6 +69,9 @@ type AssetsManager struct {
dexcCtx context.Context
dexc DEXClient
startingDEX atomic.Bool

//TODO: some time need show message for user. Change it if has other solution
toast *notification.Toast
}

// initializeAssetsFields validate the network provided is valid for all assets before proceeding
Expand Down Expand Up @@ -202,6 +206,17 @@ func (mgr *AssetsManager) RootDir() string {
return mgr.params.RootDir
}

func (mgr *AssetsManager) SetToast(toast *notification.Toast) {
mgr.toast = toast
}

func (mgr *AssetsManager) disableConversionExchange() {
mgr.SetCurrencyConversionExchange("none")
if mgr.toast != nil {
mgr.toast.NotifyError(values.String(values.StrRateUnavailable))
}
}

// initRateSource initializes the user's rate source and starts a loop to
// refresh the rates.
func (mgr *AssetsManager) initRateSource() (err error) {
Expand All @@ -211,7 +226,7 @@ func (mgr *AssetsManager) initRateSource() (err error) {
rateSource := mgr.GetCurrencyConversionExchange()
disabled := mgr.IsPrivacyModeOn()

mgr.RateSource, err = ext.NewCommonRateSource(ctx, rateSource)
mgr.RateSource, err = ext.NewCommonRateSource(ctx, rateSource, mgr.disableConversionExchange)
if err != nil {
return fmt.Errorf("ext.NewCommonRateSource error: %w", err)
}
Expand Down
46 changes: 34 additions & 12 deletions libwallet/ext/rate_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ var (
ws: "wss://stream.binance.com:9443/stream?streams=%s",
}

binanceProhibitedCountries = "https://www.binance.com/en/legal/list-of-prohibited-countries"

// supportedMarkets is a map of markets supported by rate sources
// implemented (Binance, Bittrex).
supportedMarkets = map[string]*struct{}{
Expand Down Expand Up @@ -152,6 +150,8 @@ type CommonRateSource struct {

rateListenersMtx sync.RWMutex
rateListeners map[string]*RateListener

disableConversionExchange func()
}

// Name is the string associated with the rate source for display.
Expand Down Expand Up @@ -520,7 +520,7 @@ func (cs *CommonRateSource) Refresh(force bool) {
continue
}

ticker, err := cs.getTicker(market)
ticker, err := cs.retryGetTicker(market)
if err != nil {
cs.fail("Error fetching ticker", err)
continue
Expand Down Expand Up @@ -578,9 +578,30 @@ func (cs *CommonRateSource) Refresh(force bool) {
}
}

func (cs *CommonRateSource) retryGetTicker(market string) (*Ticker, error) {
var newTicker *Ticker
var err error
backoff := 1 * time.Second
for i := 0; i < 3; i++ {
newTicker, err = cs.getTicker(market)
if err == nil {
return newTicker, nil
}

fmt.Printf("fetching ticker %d failed: %v. Retrying in %v\n", i+1, err, backoff)

time.Sleep(backoff)
backoff *= 2 // Exponential backoff
}
if cs.disableConversionExchange != nil {
cs.disableConversionExchange()
}
return nil, err
}

// fetchRate retrieves new ticker information via the rate source's HTTP API.
func (cs *CommonRateSource) fetchRate(market string) *Ticker {
newTicker, err := cs.getTicker(market)
newTicker, err := cs.retryGetTicker(market)
if err != nil {
cs.fail("Error fetching ticker", err)
return nil
Expand Down Expand Up @@ -626,7 +647,7 @@ func (cs *CommonRateSource) GetTicker(market string, cacheOnly bool) *Ticker {
}

// Used to initialize a rate source.
func NewCommonRateSource(ctx context.Context, source string) (*CommonRateSource, error) {
func NewCommonRateSource(ctx context.Context, source string, disableConversionExchange func()) (*CommonRateSource, error) {
if source != binance && source != bittrex && source != none {
return nil, fmt.Errorf("New rate source %s is not supported", source)
}
Expand All @@ -643,13 +664,14 @@ func NewCommonRateSource(ctx context.Context, source string) (*CommonRateSource,
}

s := &CommonRateSource{
ctx: ctx,
source: source,
tickers: make(map[string]*Ticker),
getTicker: getTickerFunc,
wsProcessor: wsProcessor,
rateListeners: make(map[string]*RateListener),
sourceChanged: make(chan *struct{}),
ctx: ctx,
source: source,
tickers: make(map[string]*Ticker),
getTicker: getTickerFunc,
wsProcessor: wsProcessor,
rateListeners: make(map[string]*RateListener),
sourceChanged: make(chan *struct{}),
disableConversionExchange: disableConversionExchange,
}
s.cond = sync.NewCond(&s.mtx)

Expand Down
22 changes: 12 additions & 10 deletions ui/page/root/home_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,17 +176,19 @@ func (hp *HomePage) OnNavigatedTo() {
}
}

// Reload the window whenever there is an exchange rate update.
hp.AssetsManager.RateSource.RemoveRateListener(HomePageID)
rateListener := &ext.RateListener{
OnRateUpdated: hp.CalculateAssetsUSDBalance,
}
err := hp.AssetsManager.RateSource.AddRateListener(rateListener, HomePageID)
if err != nil {
log.Error("RateSource.AddRateListener error: %v", err)
}
if hp.AssetsManager.ExchangeRateFetchingEnabled() {
// Reload the window whenever there is an exchange rate update.
hp.AssetsManager.RateSource.RemoveRateListener(HomePageID)
rateListener := &ext.RateListener{
OnRateUpdated: hp.CalculateAssetsUSDBalance,
}
err := hp.AssetsManager.RateSource.AddRateListener(rateListener, HomePageID)
if err != nil {
log.Error("RateSource.AddRateListener error: %v", err)
}

go hp.CalculateAssetsUSDBalance()
go hp.CalculateAssetsUSDBalance()
}
hp.isBalanceHidden = hp.AssetsManager.IsTotalBalanceVisible()

if hp.isUpdateAPIAllowed() {
Expand Down
108 changes: 106 additions & 2 deletions ui/preference/list_preference.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@ package preference

import (
"gioui.org/font"
"gioui.org/io/clipboard"
"gioui.org/layout"
"gioui.org/widget"

sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet"
libutils "github.com/crypto-power/cryptopower/libwallet/utils"
"github.com/crypto-power/cryptopower/ui/cryptomaterial"
"github.com/crypto-power/cryptopower/ui/load"
"github.com/crypto-power/cryptopower/ui/modal"
"github.com/crypto-power/cryptopower/ui/values"
"github.com/crypto-power/cryptopower/ui/values/localizable"
)

const (
binanceProhibitedCountries = "https://www.binance.com/en/legal/list-of-prohibited-countries"
bittrexProhibitedCountries = "https://bittrex.zendesk.com/hc/en-us/articles/360034965072-Important-information-for-Bittrex-customers"
)

type (
C = layout.Context
D = layout.Dimensions
Expand All @@ -26,8 +33,8 @@ var (

// ExchOptions holds the configurable options for exchange servers.
ExchOptions = []ItemPreference{
{Key: values.BinanceExchange, Value: values.StrUsdBinance},
{Key: values.BittrexExchange, Value: values.StrUsdBittrex},
{Key: values.BinanceExchange, Value: values.StrUsdBinance, Warning: values.String(values.StrRateBinanceWarning), WarningLink: binanceProhibitedCountries},
{Key: values.BittrexExchange, Value: values.StrUsdBittrex, Warning: values.String(values.StrRateBittrexWarning), WarningLink: bittrexProhibitedCountries},
{Key: values.DefaultExchangeValue, Value: values.StrNone},
}

Expand Down Expand Up @@ -68,13 +75,22 @@ type ListPreferenceModal struct {
preferenceItems []ItemPreference

updateButtonClicked func(string)

// use for warning link
viewWarningAction *cryptomaterial.Clickable
copyRedirectURL *cryptomaterial.Clickable
redirectIcon *cryptomaterial.Image
}

// ItemPreference models the options shown by the list
// preference modal.
type ItemPreference struct {
Key string // option's key
Value string // option's value

// use when need to show warning for option
Warning string // option's value
WarningLink string // option's value
}

func NewListPreference(l *load.Load, preferenceKey, defaultValue string, items []ItemPreference) *ListPreferenceModal {
Expand All @@ -89,6 +105,9 @@ func NewListPreference(l *load.Load, preferenceKey, defaultValue string, items [
preferenceItems: items,
optionsRadioGroup: new(widget.Enum),
Modal: l.Theme.ModalFloatTitle("list_preference", l.IsMobileView()),
redirectIcon: l.Theme.Icons.RedirectIcon,
viewWarningAction: l.Theme.NewClickable(true),
copyRedirectURL: l.Theme.NewClickable(false),
}

lp.btnSave.Font.Weight = font.Medium
Expand Down Expand Up @@ -212,20 +231,105 @@ func (lp *ListPreferenceModal) Layout(gtx C) D {
w = append(w, items[i])
}

for lp.viewWarningAction.Clicked() {
host := ""
currentValue := lp.optionsRadioGroup.Value
for _, v := range lp.preferenceItems {
if currentValue == v.Key {
host = v.WarningLink
}
}
info := modal.NewCustomModal(lp.Load).
Title(values.String(values.StrRestrictedDetail)).
Body(values.String(values.StrCopyLink)).
SetCancelable(true).
UseCustomWidget(func(gtx C) D {
return layout.Stack{}.Layout(gtx,
layout.Stacked(func(gtx C) D {
border := widget.Border{Color: lp.Theme.Color.Gray4, CornerRadius: values.MarginPadding10, Width: values.MarginPadding2}
wrapper := lp.Theme.Card()
wrapper.Color = lp.Theme.Color.Gray4
return border.Layout(gtx, func(gtx C) D {
return wrapper.Layout(gtx, func(gtx C) D {
return layout.UniformInset(values.MarginPadding10).Layout(gtx, func(gtx C) D {
return layout.Flex{}.Layout(gtx,
layout.Flexed(0.9, lp.Theme.Body1(host).Layout),
layout.Flexed(0.1, func(gtx C) D {
return layout.E.Layout(gtx, func(gtx C) D {
if lp.copyRedirectURL.Clicked() {
clipboard.WriteOp{Text: host}.Add(gtx.Ops)
lp.Toast.Notify(values.String(values.StrCopied))
}
return lp.copyRedirectURL.Layout(gtx, lp.Theme.Icons.CopyIcon.Layout24dp)
})
}),
)
})
})
})
}),
layout.Stacked(func(gtx C) D {
return layout.Inset{
Top: values.MarginPaddingMinus10,
Left: values.MarginPadding10,
}.Layout(gtx, func(gtx C) D {
label := lp.Theme.Body2(values.String(values.StrWebURL))
label.Color = lp.Theme.Color.GrayText2
return label.Layout(gtx)
})
}),
)
}).
SetPositiveButtonText(values.String(values.StrGotIt))
lp.ParentWindow().ShowModal(info)
}

return lp.Modal.Layout(gtx, w)
}

func (lp *ListPreferenceModal) warningLayout(gtx C, text string) D {
return lp.viewWarningAction.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Flex{Alignment: layout.Middle, Spacing: layout.SpaceBetween}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
gtx.Constraints.Max.X = gtx.Constraints.Max.X - 60
lbl := lp.Theme.Body2(text)
lbl.Color = lp.Theme.Color.Warning
lbl.Font.Style = font.Italic
return lbl.Layout(gtx)
}),
layout.Rigid(func(gtx C) D {
return layout.Inset{
Right: values.MarginPadding10,
}.Layout(gtx, lp.redirectIcon.Layout24dp)
}),
)
})
}

func (lp *ListPreferenceModal) layoutItems() []layout.FlexChild {
items := make([]layout.FlexChild, 0)
warningText := ""
currentValue := lp.optionsRadioGroup.Value
for _, v := range lp.preferenceItems {
text := values.String(v.Value)
if lp.isWalletAccount {
text = v.Value
}

if currentValue == v.Key {
warningText = v.Warning
}

radioItem := layout.Rigid(lp.Theme.RadioButton(lp.optionsRadioGroup, v.Key, text, lp.Theme.Color.DeepBlue, lp.Theme.Color.Primary).Layout)
items = append(items, radioItem)
}
if warningText != "" {
warningChild := layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return lp.warningLayout(gtx, warningText)
})

items = append(items, warningChild)
}

return items
}
Expand Down
2 changes: 2 additions & 0 deletions ui/values/colors.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type Color struct {
Yellow color.NRGBA
OrangeYellow color.NRGBA
White color.NRGBA
Warning color.NRGBA
}

func (c *Color) DarkThemeColors() {
Expand Down Expand Up @@ -153,6 +154,7 @@ func (c *Color) DefaultThemeColors() *Color {
Yellow: rgb(0xFEE3AA),
OrangeYellow: rgb(0xd8a93e),
White: rgb(0xffffff),
Warning: rgb(0xff9966),
}

return &cl
Expand Down
4 changes: 4 additions & 0 deletions ui/values/localizable/en.go
Original file line number Diff line number Diff line change
Expand Up @@ -941,4 +941,8 @@ const EN = `
"33WordSeed" = "33-word seed"
"wordSeedType" = "Word Seed Type:"
"exportWalletSeed" = "Export wallet seed"
"rateBinanceWarning" = "*Some countries are restricted on Binance and may not be able to fetch rate."
"rateBittrexWarning" = "*Some countries are restricted on Bittrex and may not be able to fetch rate."
"restrictedDetail" = "Restricted Detail"
"rateUnavailable" = "The rate unavailable this time, please reset it later in settings."
`
4 changes: 4 additions & 0 deletions ui/values/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -1050,4 +1050,8 @@ const (
Str33WordSeed = "33WordSeed"
StrWordSeedType = "wordSeedType"
StrExportWalletSeed = "exportWalletSeed"
StrRateBinanceWarning = "rateBinanceWarning"
StrRateBittrexWarning = "rateBittrexWarning"
StrRestrictedDetail = "restrictedDetail"
StrRateUnavailable = "rateUnavailable"
)
2 changes: 2 additions & 0 deletions ui/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ func (win *Window) NewLoad(appInfo *load.AppInfo) (*load.Load, error) {
Printer: message.NewPrinter(language.English),
}

appInfo.AssetsManager.SetToast(l.Toast)

// DarkModeSettingChanged checks if any page or any
// modal implements the AppSettingsChangeHandler
l.DarkModeSettingChanged = func(isDarkModeOn bool) {
Expand Down

0 comments on commit 46d028a

Please sign in to comment.