From 4ef4f7aa54e48fdfabb58e606e66e6d8f2091cf4 Mon Sep 17 00:00:00 2001 From: Miguel Mota Date: Sun, 30 Jun 2019 18:50:48 -0700 Subject: [PATCH] Clean up --- README.md | 5 +- cointop/actions.go | 2 +- cointop/chart.go | 104 +++--- cointop/coin.go | 61 ++++ cointop/cointop.go | 299 ++++++++++-------- cointop/colorscheme.go | 2 + cointop/config.go | 52 +-- cointop/conversion.go | 78 ++--- .../{defaultcolors.go => default_colors.go} | 0 cointop/favorites.go | 12 +- cointop/help.go | 54 ++-- cointop/keybindings.go | 24 +- cointop/layout.go | 191 +++++------ cointop/list.go | 40 +-- cointop/marketbar.go | 24 +- cointop/navigation.go | 156 ++++----- cointop/portfolio.go | 86 ++--- cointop/quit.go | 10 +- cointop/refresh.go | 21 +- cointop/search.go | 22 +- cointop/selection.go | 19 ++ cointop/sort.go | 78 ++--- cointop/statusbar.go | 16 +- cointop/table.go | 113 +++---- cointop/{headers.go => table_header.go} | 12 +- cointop/types.go | 24 -- cointop/view.go | 39 +++ 27 files changed, 794 insertions(+), 750 deletions(-) create mode 100644 cointop/coin.go rename cointop/{defaultcolors.go => default_colors.go} (100%) create mode 100644 cointop/selection.go rename cointop/{headers.go => table_header.go} (90%) delete mode 100644 cointop/types.go create mode 100644 cointop/view.go diff --git a/README.md b/README.md index 7840b8d..9b5fd01 100644 --- a/README.md +++ b/README.md @@ -644,6 +644,10 @@ Frequently asked questions: cointop --coinmarketcap-api-key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ``` +- Q: I can I add my own API to cointop? + + - A: Fork cointop and add the API that implements the [interface](https://github.com/miguelmota/cointop/blob/master/cointop/common/api/interface.go) to [`cointop/cointop/common/api/impl/`](https://github.com/miguelmota/cointop/tree/master/cointop/common/api/impl). You can use the [CoinGecko implementatoin](https://github.com/miguelmota/cointop/blob/master/cointop/common/api/impl/coingecko/coingecko.go) as reference. + - Q: I installed cointop without errors but the command is not found. - A: Make sure your `GOPATH` and `PATH` is set correctly. @@ -886,7 +890,6 @@ Frequently asked questions: -A: Cointop uses ~15MB of memory so you can run it on a Raspberry Pi Zero if you wanted to (one reason why cointop was built using Go instead of Node.js or Python). - - Q: How does cointop differ from [rate.sx](https://rate.sx/)? - A: *rate.sx* is great for one-off queries or fetching data for bash scripts because it doesn't require installing anything. Cointop differs in that it is interactive and also supports more currencies. diff --git a/cointop/actions.go b/cointop/actions.go index a3e585b..c830f1e 100644 --- a/cointop/actions.go +++ b/cointop/actions.go @@ -55,5 +55,5 @@ func actionsMap() map[string]bool { } func (ct *Cointop) actionExists(action string) bool { - return ct.actionsmap[action] + return ct.actionsMap[action] } diff --git a/cointop/chart.go b/cointop/chart.go index 09ca1e4..7c5ef2f 100644 --- a/cointop/chart.go +++ b/cointop/chart.go @@ -11,38 +11,38 @@ import ( "github.com/miguelmota/cointop/cointop/common/timeutil" ) -var chartlock sync.Mutex -var chartpointslock sync.Mutex +var chartLock sync.Mutex +var chartPointsLock sync.Mutex func (ct *Cointop) updateChart() error { - if ct.chartview == nil { + if ct.Views.Chart.Backing == nil { return nil } - chartlock.Lock() - defer chartlock.Unlock() + chartLock.Lock() + defer chartLock.Unlock() - if ct.portfoliovisible { + if ct.State.portfolioVisible { if err := ct.portfolioChart(); err != nil { return err } } else { symbol := ct.selectedCoinSymbol() name := ct.selectedCoinName() - ct.chartPoints(symbol, name) + ct.calcChartPoints(symbol, name) } - if len(ct.chartpoints) != 0 { - ct.chartview.Clear() + if len(ct.State.chartPoints) != 0 { + ct.Views.Chart.Backing.Clear() } var body string - if len(ct.chartpoints) == 0 { + if len(ct.State.chartPoints) == 0 { body = "\n\n\n\n\nnot enough data for chart" } else { - for i := range ct.chartpoints { + for i := range ct.State.chartPoints { var s string - for j := range ct.chartpoints[i] { - p := ct.chartpoints[i][j] + for j := range ct.State.chartPoints[i] { + p := ct.State.chartPoints[i][j] s = fmt.Sprintf("%s%c", s, p.Ch) } body = fmt.Sprintf("%s%s\n", body, s) @@ -50,20 +50,20 @@ func (ct *Cointop) updateChart() error { } } ct.update(func() { - if ct.chartview == nil { + if ct.Views.Chart.Backing == nil { return } - fmt.Fprint(ct.chartview, ct.colorscheme.Chart(body)) + fmt.Fprint(ct.Views.Chart.Backing, ct.colorscheme.Chart(body)) }) return nil } -func (ct *Cointop) chartPoints(symbol string, name string) error { - maxX := ct.maxtablewidth - 3 - chartpointslock.Lock() - defer chartpointslock.Unlock() +func (ct *Cointop) calcChartPoints(symbol string, name string) error { + maxX := ct.maxTableWidth - 3 + chartPointsLock.Lock() + defer chartPointsLock.Unlock() // TODO: not do this (SoC) go ct.updateMarketbar() @@ -74,8 +74,8 @@ func (ct *Cointop) chartPoints(symbol string, name string) error { // NOTE: empty list means don't show x-axis labels chart.DataLabels = []string{""} - rangeseconds := ct.chartrangesmap[ct.selectedchartrange] - if ct.selectedchartrange == "YTD" { + rangeseconds := ct.chartRangesMap[ct.State.selectedChartRange] + if ct.State.selectedChartRange == "YTD" { ytd := time.Now().Unix() - int64(timeutil.BeginningOfYear().Unix()) rangeseconds = time.Duration(ytd) * time.Second } @@ -91,7 +91,7 @@ func (ct *Cointop) chartPoints(symbol string, name string) error { if keyname == "" { keyname = "globaldata" } - cachekey := ct.cacheKey(fmt.Sprintf("%s_%s", keyname, strings.Replace(ct.selectedchartrange, " ", "", -1))) + cachekey := ct.cacheKey(fmt.Sprintf("%s_%s", keyname, strings.Replace(ct.State.selectedChartRange, " ", "", -1))) cached, found := ct.cache.Get(cachekey) if found { @@ -155,15 +155,15 @@ func (ct *Cointop) chartPoints(symbol string, name string) error { points = append(points, rowpoints) } - ct.chartpoints = points + ct.State.chartPoints = points return nil } func (ct *Cointop) portfolioChart() error { - maxX := ct.maxtablewidth - 3 - chartpointslock.Lock() - defer chartpointslock.Unlock() + maxX := ct.maxTableWidth - 3 + chartPointsLock.Lock() + defer chartPointsLock.Unlock() // TODO: not do this (SoC) go ct.updateMarketbar() @@ -174,8 +174,8 @@ func (ct *Cointop) portfolioChart() error { // NOTE: empty list means don't show x-axis labels chart.DataLabels = []string{""} - rangeseconds := ct.chartrangesmap[ct.selectedchartrange] - if ct.selectedchartrange == "YTD" { + rangeseconds := ct.chartRangesMap[ct.State.selectedChartRange] + if ct.State.selectedChartRange == "YTD" { ytd := time.Now().Unix() - int64(timeutil.BeginningOfYear().Unix()) rangeseconds = time.Duration(ytd) * time.Second } @@ -201,7 +201,7 @@ func (ct *Cointop) portfolioChart() error { } var graphData []float64 - cachekey := strings.ToLower(fmt.Sprintf("%s_%s", p.Symbol, strings.Replace(ct.selectedchartrange, " ", "", -1))) + cachekey := strings.ToLower(fmt.Sprintf("%s_%s", p.Symbol, strings.Replace(ct.State.selectedChartRange, " ", "", -1))) cached, found := ct.cache.Get(cachekey) if found { // cache hit @@ -263,16 +263,16 @@ func (ct *Cointop) portfolioChart() error { points = append(points, rowpoints) } - ct.chartpoints = points + ct.State.chartPoints = points return nil } func (ct *Cointop) nextChartRange() error { sel := 0 - max := len(ct.chartranges) - for i, k := range ct.chartranges { - if k == ct.selectedchartrange { + max := len(ct.chartRanges) + for i, k := range ct.chartRanges { + if k == ct.State.selectedChartRange { sel = i + 1 break } @@ -281,7 +281,7 @@ func (ct *Cointop) nextChartRange() error { sel = 0 } - ct.selectedchartrange = ct.chartranges[sel] + ct.State.selectedChartRange = ct.chartRanges[sel] go ct.updateChart() return nil @@ -289,57 +289,39 @@ func (ct *Cointop) nextChartRange() error { func (ct *Cointop) prevChartRange() error { sel := 0 - for i, k := range ct.chartranges { - if k == ct.selectedchartrange { + for i, k := range ct.chartRanges { + if k == ct.State.selectedChartRange { sel = i - 1 break } } if sel < 0 { - sel = len(ct.chartranges) - 1 + sel = len(ct.chartRanges) - 1 } - ct.selectedchartrange = ct.chartranges[sel] + ct.State.selectedChartRange = ct.chartRanges[sel] go ct.updateChart() return nil } func (ct *Cointop) firstChartRange() error { - ct.selectedchartrange = ct.chartranges[0] + ct.State.selectedChartRange = ct.chartRanges[0] go ct.updateChart() return nil } func (ct *Cointop) lastChartRange() error { - ct.selectedchartrange = ct.chartranges[len(ct.chartranges)-1] + ct.State.selectedChartRange = ct.chartRanges[len(ct.chartRanges)-1] go ct.updateChart() return nil } -func (ct *Cointop) selectedCoinName() string { - coin := ct.selectedcoin - if coin != nil { - return coin.Name - } - - return "" -} - -func (ct *Cointop) selectedCoinSymbol() string { - coin := ct.selectedcoin - if coin != nil { - return coin.Symbol - } - - return "" -} - func (ct *Cointop) toggleCoinChart() error { highlightedcoin := ct.highlightedRowCoin() - if ct.selectedcoin == highlightedcoin { - ct.selectedcoin = nil + if ct.State.selectedCoin == highlightedcoin { + ct.State.selectedCoin = nil } else { - ct.selectedcoin = highlightedcoin + ct.State.selectedCoin = highlightedcoin } go ct.updateChart() diff --git a/cointop/coin.go b/cointop/coin.go new file mode 100644 index 0000000..55eb807 --- /dev/null +++ b/cointop/coin.go @@ -0,0 +1,61 @@ +package cointop + +// Coin is the row structure +type Coin struct { + ID string + Name string + Slug string + Symbol string + Rank int + Price float64 + Volume24H float64 + MarketCap float64 + AvailableSupply float64 + TotalSupply float64 + PercentChange1H float64 + PercentChange24H float64 + PercentChange7D float64 + LastUpdated string + // for favorites + Favorite bool + // for portfolio + Holdings float64 + Balance float64 +} + +func (ct *Cointop) allCoins() []*Coin { + if ct.State.filterByFavorites { + var list []*Coin + for i := range ct.State.allCoins { + coin := ct.State.allCoins[i] + if coin.Favorite { + list = append(list, coin) + } + } + return list + } + + if ct.State.portfolioVisible { + var list []*Coin + for i := range ct.State.allCoins { + coin := ct.State.allCoins[i] + if ct.PortfolioEntryExists(coin) { + list = append(list, coin) + } + } + return list + } + + return ct.State.allCoins +} + +func (ct *Cointop) coinBySymbol(symbol string) *Coin { + for i := range ct.State.allCoins { + coin := ct.State.allCoins[i] + if coin.Symbol == symbol { + return coin + } + } + + return nil +} diff --git a/cointop/cointop.go b/cointop/cointop.go index 68a4b25..55548e4 100644 --- a/cointop/cointop.go +++ b/cointop/cointop.go @@ -24,80 +24,79 @@ import ( // ErrInvalidAPIChoice is error for invalid API choice var ErrInvalidAPIChoice = errors.New("Invalid API choice") -// Cointop cointop -type Cointop struct { - g *gocui.Gui - apiChoice string - colorschemename string - colorscheme *Colorscheme - marketbarviewname string - marketbarview *gocui.View - chartview *gocui.View - chartviewname string - chartpoints [][]termui.Cell - chartranges []string - chartrangesmap map[string]time.Duration - selectedchartrange string - headersview *gocui.View - headerviewname string - tableview *gocui.View - tableviewname string - tablecolumnorder []string - table *table.Table - maxtablewidth int - portfoliovisible bool - visible bool - statusbarview *gocui.View - statusbarviewname string - sortdesc bool - sortby string - api api.Interface - allcoins []*Coin - coins []*Coin - allcoinsslugmap map[string]*Coin - page int - perpage int - refreshmux sync.Mutex - refreshRate time.Duration - refreshTicker *time.Ticker - forcerefresh chan bool - selectedcoin *Coin - actionsmap map[string]bool - shortcutkeys map[string]string - config config // toml config - configFilepath string - searchfield *gocui.View - searchfieldviewname string - searchfieldvisible bool +// Views are all views in cointop +type Views struct { + Chart *View + Header *View + Table *View + Marketbar *View + SearchField *View + Statusbar *View + Help *View + ConvertMenu *View + Input *View + PortfolioUpdateMenu *View +} + +// State is the state preferences of cointop +type State struct { + allCoins []*Coin + allCoinsSlugMap map[string]*Coin + coins []*Coin + chartPoints [][]termui.Cell + currencyConversion string + convertMenuVisible bool + defaultView string // DEPRECATED: favorites by 'symbol' is deprecated because of collisions. favoritesbysymbol map[string]bool - favorites map[string]bool - filterByFavorites bool - savemux sync.Mutex - cache *cache.Cache - debug bool - helpview *gocui.View - helpviewname string - helpvisible bool - currencyconversion string - convertmenuview *gocui.View - convertmenuviewname string - convertmenuvisible bool - portfolio *portfolio - portfolioupdatemenuview *gocui.View - portfolioupdatemenuviewname string - portfolioupdatemenuvisible bool - inputview *gocui.View - inputviewname string - defaultView string - apiKeys *apiKeys - limiter <-chan time.Time - hideMarketbar bool - hideChart bool - hideStatusbar bool - onlyTable bool + favorites map[string]bool + filterByFavorites bool + helpVisible bool + hideMarketbar bool + hideChart bool + hideStatusbar bool + page int + perPage int + portfolio *Portfolio + portfolioVisible bool + portfolioUpdateMenuVisible bool + refreshRate time.Duration + searchFieldVisible bool + selectedCoin *Coin + selectedChartRange string + shortcutKeys map[string]string + sortDesc bool + sortBy string + onlyTable bool +} + +// Cointop cointop +type Cointop struct { + g *gocui.Gui + actionsMap map[string]bool + apiKeys *APIKeys + cache *cache.Cache + config config // toml config + configFilepath string + api api.Interface + apiChoice string + chartRanges []string + chartRangesMap map[string]time.Duration + colorschemeName string + colorscheme *Colorscheme + debug bool + forceRefresh chan bool + limiter <-chan time.Time + maxTableWidth int + refreshMux sync.Mutex + refreshTicker *time.Ticker + saveMux sync.Mutex + State *State + table *table.Table + tableColumnOrder []string + Views *Views } // CoinMarketCap is API choice @@ -107,14 +106,14 @@ var CoinMarketCap = "coinmarketcap" var CoinGecko = "coingecko" // PortfolioEntry is portfolio entry -type portfolioEntry struct { +type PortfolioEntry struct { Coin string Holdings float64 } // Portfolio is portfolio structure -type portfolio struct { - Entries map[string]*portfolioEntry +type Portfolio struct { + Entries map[string]*PortfolioEntry } // Config config options @@ -131,8 +130,8 @@ type Config struct { RefreshRate *uint } -// apiKeys is api keys structure -type apiKeys struct { +// APIKeys is api keys structure +type APIKeys struct { cmc string } @@ -154,25 +153,14 @@ func NewCointop(config *Config) *Cointop { } ct := &Cointop{ - apiChoice: CoinGecko, - allcoinsslugmap: make(map[string]*Coin), - allcoins: []*Coin{}, - sortby: "rank", - page: 0, - perpage: 100, - forcerefresh: make(chan bool), - maxtablewidth: 175, - actionsmap: actionsMap(), - shortcutkeys: defaultShortcuts(), - // DEPRECATED: favorites by 'symbol' is deprecated because of collisions. Kept for backward compatibility. - favoritesbysymbol: make(map[string]bool), - favorites: make(map[string]bool), - cache: cache.New(1*time.Minute, 2*time.Minute), - debug: debug, - configFilepath: configFilepath, - marketbarviewname: "market", - chartviewname: "chart", - chartranges: []string{ + apiChoice: CoinGecko, + apiKeys: new(APIKeys), + forceRefresh: make(chan bool), + maxTableWidth: 175, + actionsMap: actionsMap(), + cache: cache.New(1*time.Minute, 2*time.Minute), + configFilepath: configFilepath, + chartRanges: []string{ "1H", "6H", "24H", @@ -185,7 +173,8 @@ func NewCointop(config *Config) *Cointop { "YTD", "All Time", }, - chartrangesmap: map[string]time.Duration{ + debug: debug, + chartRangesMap: map[string]time.Duration{ "All Time": time.Duration(24 * 7 * 4 * 12 * 5 * time.Hour), "YTD": time.Duration(1 * time.Second), // this will be calculated "1Y": time.Duration(24 * 7 * 4 * 12 * time.Hour), @@ -198,10 +187,29 @@ func NewCointop(config *Config) *Cointop { "6H": time.Duration(6 * time.Hour), "1H": time.Duration(1 * time.Hour), }, - selectedchartrange: "7D", - headerviewname: "header", - tableviewname: "table", - tablecolumnorder: []string{ + limiter: time.Tick(2 * time.Second), + State: &State{ + allCoinsSlugMap: make(map[string]*Coin), + allCoins: []*Coin{}, + currencyConversion: "USD", + // DEPRECATED: favorites by 'symbol' is deprecated because of collisions. Kept for backward compatibility. + favoritesbysymbol: make(map[string]bool), + favorites: make(map[string]bool), + hideMarketbar: config.HideMarketbar, + hideChart: config.HideChart, + hideStatusbar: config.HideStatusbar, + onlyTable: config.OnlyTable, + refreshRate: 60 * time.Second, + selectedChartRange: "7D", + shortcutKeys: defaultShortcuts(), + sortBy: "rank", + page: 0, + perPage: 100, + portfolio: &Portfolio{ + Entries: make(map[string]*PortfolioEntry, 0), + }, + }, + tableColumnOrder: []string{ "rank", "name", "symbol", @@ -217,23 +225,38 @@ func NewCointop(config *Config) *Cointop { "percentholdings", "lastupdated", }, - statusbarviewname: "statusbar", - searchfieldviewname: "searchfield", - helpviewname: "help", - convertmenuviewname: "convertmenu", - currencyconversion: "USD", - portfolio: &portfolio{ - Entries: make(map[string]*portfolioEntry, 0), + Views: &Views{ + Chart: &View{ + Name: "chart", + }, + Header: &View{ + Name: "header", + }, + Table: &View{ + Name: "table", + }, + Marketbar: &View{ + Name: "marketbar", + }, + SearchField: &View{ + Name: "searchfield", + }, + Statusbar: &View{ + Name: "statusbar", + }, + Help: &View{ + Name: "help", + }, + ConvertMenu: &View{ + Name: "convert", + }, + Input: &View{ + Name: "input", + }, + PortfolioUpdateMenu: &View{ + Name: "portfolioupdatemenu", + }, }, - portfolioupdatemenuviewname: "portfolioupdatemenu", - inputviewname: "input", - apiKeys: new(apiKeys), - limiter: time.Tick(2 * time.Second), - hideMarketbar: config.HideMarketbar, - hideChart: config.HideChart, - hideStatusbar: config.HideStatusbar, - onlyTable: config.OnlyTable, - refreshRate: 60 * time.Second, } err := ct.setupConfig() @@ -241,20 +264,20 @@ func NewCointop(config *Config) *Cointop { log.Fatal(err) } - ct.cache.Set("onlyTable", ct.onlyTable, cache.NoExpiration) - ct.cache.Set("hideMarketbar", ct.hideMarketbar, cache.NoExpiration) - ct.cache.Set("hideChart", ct.hideChart, cache.NoExpiration) - ct.cache.Set("hideStatusbar", ct.hideStatusbar, cache.NoExpiration) + ct.cache.Set("onlyTable", ct.State.onlyTable, cache.NoExpiration) + ct.cache.Set("hideMarketbar", ct.State.hideMarketbar, cache.NoExpiration) + ct.cache.Set("hideChart", ct.State.hideChart, cache.NoExpiration) + ct.cache.Set("hideStatusbar", ct.State.hideStatusbar, cache.NoExpiration) if config.RefreshRate != nil { - ct.refreshRate = time.Duration(*config.RefreshRate) * time.Second + ct.State.refreshRate = time.Duration(*config.RefreshRate) * time.Second } - if ct.refreshRate == 0 { + if ct.State.refreshRate == 0 { ct.refreshTicker = time.NewTicker(time.Duration(1)) ct.refreshTicker.Stop() } else { - ct.refreshTicker = time.NewTicker(ct.refreshRate) + ct.refreshTicker = time.NewTicker(ct.State.refreshRate) } // prompt for CoinMarketCap api key if not found @@ -266,7 +289,7 @@ func NewCointop(config *Config) *Cointop { } if config.Colorscheme != "" { - ct.colorschemename = config.Colorscheme + ct.colorschemeName = config.Colorscheme } colors, err := ct.getColorschemeColors() @@ -297,7 +320,7 @@ func NewCointop(config *Config) *Cointop { } if ct.apiChoice == CoinGecko { - ct.selectedchartrange = "1Y" + ct.State.selectedChartRange = "1Y" } if ct.apiChoice == CoinMarketCap { @@ -308,35 +331,35 @@ func NewCointop(config *Config) *Cointop { log.Fatal(ErrInvalidAPIChoice) } - coinscachekey := ct.cacheKey("allcoinsslugmap") - filecache.Get(coinscachekey, &ct.allcoinsslugmap) + coinscachekey := ct.cacheKey("allCoinsSlugMap") + filecache.Get(coinscachekey, &ct.State.allCoinsSlugMap) - for k := range ct.allcoinsslugmap { - ct.allcoins = append(ct.allcoins, ct.allcoinsslugmap[k]) + for k := range ct.State.allCoinsSlugMap { + ct.State.allCoins = append(ct.State.allCoins, ct.State.allCoinsSlugMap[k]) } - if len(ct.allcoins) > 1 { - max := len(ct.allcoins) + if len(ct.State.allCoins) > 1 { + max := len(ct.State.allCoins) if max > 100 { max = 100 } - ct.sort(ct.sortby, ct.sortdesc, ct.allcoins, false) - ct.coins = ct.allcoins[0:max] + ct.sort(ct.State.sortBy, ct.State.sortDesc, ct.State.allCoins, false) + ct.State.coins = ct.State.allCoins[0:max] } // DEPRECATED: favorites by 'symbol' is deprecated because of collisions. Kept for backward compatibility. // Here we're doing a lookup based on symbol and setting the favorite to the coin name instead of coin symbol. - for i := range ct.allcoinsslugmap { - coin := ct.allcoinsslugmap[i] - for k := range ct.favoritesbysymbol { + for i := range ct.State.allCoinsSlugMap { + coin := ct.State.allCoinsSlugMap[i] + for k := range ct.State.favoritesbysymbol { if coin.Symbol == k { - ct.favorites[coin.Name] = true - delete(ct.favoritesbysymbol, k) + ct.State.favorites[coin.Name] = true + delete(ct.State.favoritesbysymbol, k) } } } var globaldata []float64 - chartcachekey := ct.cacheKey(fmt.Sprintf("%s_%s", "globaldata", strings.Replace(ct.selectedchartrange, " ", "", -1))) + chartcachekey := ct.cacheKey(fmt.Sprintf("%s_%s", "globaldata", strings.Replace(ct.State.selectedChartRange, " ", "", -1))) filecache.Get(chartcachekey, &globaldata) ct.cache.Set(chartcachekey, globaldata, 10*time.Second) diff --git a/cointop/colorscheme.go b/cointop/colorscheme.go index 2987ba9..b2d3457 100644 --- a/cointop/colorscheme.go +++ b/cointop/colorscheme.go @@ -8,6 +8,8 @@ import ( xtermcolor "github.com/tomnomnom/xtermcolor" ) +// TODO: fix hex color support + // colorschemeColors .. type colorschemeColors map[string]interface{} diff --git a/cointop/config.go b/cointop/config.go index 1a154e0..d548e51 100644 --- a/cointop/config.go +++ b/cointop/config.go @@ -125,8 +125,8 @@ func (ct *Cointop) makeConfigFile() error { } func (ct *Cointop) saveConfig() error { - ct.savemux.Lock() - defer ct.savemux.Unlock() + ct.saveMux.Lock() + defer ct.saveMux.Unlock() path := ct.configPath() if _, err := os.Stat(path); err == nil { b, err := ct.configToToml() @@ -154,13 +154,13 @@ func (ct *Cointop) parseConfig() error { func (ct *Cointop) configToToml() ([]byte, error) { shortcutsIfcs := map[string]interface{}{} - for k, v := range ct.shortcutkeys { + for k, v := range ct.State.shortcutKeys { var i interface{} = v shortcutsIfcs[k] = i } var favorites []interface{} - for k, ok := range ct.favorites { + for k, ok := range ct.State.favorites { if ok { var i interface{} = k favorites = append(favorites, i) @@ -174,8 +174,8 @@ func (ct *Cointop) configToToml() ([]byte, error) { } portfolioIfc := map[string]interface{}{} - for name := range ct.portfolio.Entries { - entry, ok := ct.portfolio.Entries[name] + for name := range ct.State.portfolio.Entries { + entry, ok := ct.State.portfolio.Entries[name] if !ok || entry.Coin == "" { continue } @@ -183,10 +183,10 @@ func (ct *Cointop) configToToml() ([]byte, error) { portfolioIfc[entry.Coin] = i } - var currencyIfc interface{} = ct.currencyconversion - var defaultViewIfc interface{} = ct.defaultView - var colorschemeIfc interface{} = ct.colorschemename - var refreshRateIfc interface{} = uint(ct.refreshRate.Seconds()) + var currencyIfc interface{} = ct.State.currencyConversion + var defaultViewIfc interface{} = ct.State.defaultView + var colorschemeIfc interface{} = ct.colorschemeName + var refreshRateIfc interface{} = uint(ct.State.refreshRate.Seconds()) cmcIfc := map[string]interface{}{ "pro_api_key": ct.apiKeys.cmc, @@ -221,10 +221,10 @@ func (ct *Cointop) loadShortcutsFromConfig() error { if !ct.actionExists(v) { continue } - if ct.shortcutkeys[k] == "" { + if ct.State.shortcutKeys[k] == "" { continue } - ct.shortcutkeys[k] = v + ct.State.shortcutKeys[k] = v } } return nil @@ -232,7 +232,7 @@ func (ct *Cointop) loadShortcutsFromConfig() error { func (ct *Cointop) loadCurrencyFromConfig() error { if currency, ok := ct.config.Currency.(string); ok { - ct.currencyconversion = strings.ToUpper(currency) + ct.State.currencyConversion = strings.ToUpper(currency) } return nil } @@ -242,17 +242,17 @@ func (ct *Cointop) loadDefaultViewFromConfig() error { defaultView = strings.ToLower(defaultView) switch defaultView { case "portfolio": - ct.portfoliovisible = true + ct.State.portfolioVisible = true case "favorites": - ct.filterByFavorites = true + ct.State.filterByFavorites = true case "default": fallthrough default: - ct.portfoliovisible = false - ct.filterByFavorites = false + ct.State.portfolioVisible = false + ct.State.filterByFavorites = false defaultView = "default" } - ct.defaultView = defaultView + ct.State.defaultView = defaultView } return nil } @@ -269,7 +269,7 @@ func (ct *Cointop) loadAPIKeysFromConfig() error { func (ct *Cointop) loadColorschemeFromConfig() error { if colorscheme, ok := ct.config.Colorscheme.(string); ok { - ct.colorschemename = colorscheme + ct.colorschemeName = colorscheme } return nil @@ -277,7 +277,7 @@ func (ct *Cointop) loadColorschemeFromConfig() error { func (ct *Cointop) loadRefreshRateFromConfig() error { if refreshRate, ok := ct.config.RefreshRate.(int64); ok { - ct.refreshRate = time.Duration(uint(refreshRate)) * time.Second + ct.State.refreshRate = time.Duration(uint(refreshRate)) * time.Second } return nil @@ -285,16 +285,16 @@ func (ct *Cointop) loadRefreshRateFromConfig() error { func (ct *Cointop) getColorschemeColors() (map[string]interface{}, error) { var colors map[string]interface{} - if ct.colorschemename == "" { - ct.colorschemename = defaultColorscheme + if ct.colorschemeName == "" { + ct.colorschemeName = defaultColorscheme if _, err := toml.Decode(DefaultColors, &colors); err != nil { return nil, err } } else { - path := normalizePath(fmt.Sprintf("~/.cointop/colors/%s.toml", ct.colorschemename)) + path := normalizePath(fmt.Sprintf("~/.cointop/colors/%s.toml", ct.colorschemeName)) if _, err := os.Stat(path); os.IsNotExist(err) { // NOTE: case for when cointop is set as the theme but the colorscheme file doesn't exist - if ct.colorschemename == "cointop" { + if ct.colorschemeName == "cointop" { if _, err := toml.Decode(DefaultColors, &colors); err != nil { return nil, err } @@ -328,13 +328,13 @@ func (ct *Cointop) loadFavoritesFromConfig() error { if k == "symbols" { for _, ifc := range arr { if v, ok := ifc.(string); ok { - ct.favoritesbysymbol[strings.ToUpper(v)] = true + ct.State.favoritesbysymbol[strings.ToUpper(v)] = true } } } else if k == "names" { for _, ifc := range arr { if v, ok := ifc.(string); ok { - ct.favorites[v] = true + ct.State.favorites[v] = true } } } diff --git a/cointop/conversion.go b/cointop/conversion.go index 3c90a19..d0bb0d1 100644 --- a/cointop/conversion.go +++ b/cointop/conversion.go @@ -119,19 +119,11 @@ func (ct *Cointop) sortedSupportedCurrencyConversions() []string { return keys } -func (ct *Cointop) toggleConvertMenu() error { - ct.convertmenuvisible = !ct.convertmenuvisible - if ct.convertmenuvisible { - return ct.showConvertMenu() - } - return ct.hideConvertMenu() -} - func (ct *Cointop) updateConvertMenu() { - header := ct.colorscheme.MenuHeader(fmt.Sprintf(" Currency Conversion %s\n\n", pad.Left("[q] close menu ", ct.maxtablewidth-20, " "))) + header := ct.colorscheme.MenuHeader(fmt.Sprintf(" Currency Conversion %s\n\n", pad.Left("[q] close menu ", ct.maxTableWidth-20, " "))) helpline := " Press the corresponding key to select currency for conversion\n\n" cnt := 0 - h := ct.viewHeight(ct.convertmenuviewname) + h := ct.viewHeight(ct.Views.ConvertMenu.Name) percol := h - 5 cols := make([][]string, percol) for i := range cols { @@ -146,7 +138,7 @@ func (ct *Cointop) updateConvertMenu() { cnt = 0 } shortcut := string(alphanumericcharacters[i]) - if key == ct.currencyconversion { + if key == ct.State.currencyConversion { shortcut = ct.colorscheme.MenuLabelActive(color.Bold("*")) key = ct.colorscheme.Menu(color.Bold(key)) currency = ct.colorscheme.MenuLabelActive(color.Bold(currency)) @@ -171,53 +163,61 @@ func (ct *Cointop) updateConvertMenu() { content := fmt.Sprintf("%s%s%s", header, helpline, body) ct.update(func() { - if ct.convertmenuview == nil { + if ct.Views.ConvertMenu.Backing == nil { return } - ct.convertmenuview.Clear() - ct.convertmenuview.Frame = true - fmt.Fprintln(ct.convertmenuview, content) + ct.Views.ConvertMenu.Backing.Clear() + ct.Views.ConvertMenu.Backing.Frame = true + fmt.Fprintln(ct.Views.ConvertMenu.Backing, content) }) } +func (ct *Cointop) setCurrencyConverstion(convert string) func() error { + return func() error { + ct.State.currencyConversion = convert + ct.hideConvertMenu() + go ct.refreshAll() + return nil + } +} + +func (ct *Cointop) currencySymbol() string { + symbol, ok := currencySymbol[ct.State.currencyConversion] + if ok { + return symbol + } + + return "$" +} + func (ct *Cointop) showConvertMenu() error { - ct.convertmenuvisible = true + ct.State.convertMenuVisible = true ct.updateConvertMenu() - ct.setActiveView(ct.convertmenuviewname) + ct.setActiveView(ct.Views.ConvertMenu.Name) return nil } func (ct *Cointop) hideConvertMenu() error { - ct.convertmenuvisible = false - ct.setViewOnBottom(ct.convertmenuviewname) - ct.setActiveView(ct.tableviewname) + ct.State.convertMenuVisible = false + ct.setViewOnBottom(ct.Views.ConvertMenu.Name) + ct.setActiveView(ct.Views.Table.Name) ct.update(func() { - if ct.convertmenuview == nil { + if ct.Views.ConvertMenu.Backing == nil { return } - ct.convertmenuview.Clear() - ct.convertmenuview.Frame = false - fmt.Fprintln(ct.convertmenuview, "") + ct.Views.ConvertMenu.Backing.Clear() + ct.Views.ConvertMenu.Backing.Frame = false + fmt.Fprintln(ct.Views.ConvertMenu.Backing, "") }) return nil } -func (ct *Cointop) setCurrencyConverstion(convert string) func() error { - return func() error { - ct.currencyconversion = convert - ct.hideConvertMenu() - go ct.refreshAll() - return nil - } -} - -func (ct *Cointop) currencySymbol() string { - symbol, ok := currencySymbol[ct.currencyconversion] - if ok { - return symbol +func (ct *Cointop) toggleConvertMenu() error { + ct.State.convertMenuVisible = !ct.State.convertMenuVisible + if ct.State.convertMenuVisible { + return ct.showConvertMenu() } - - return "$" + return ct.hideConvertMenu() } diff --git a/cointop/defaultcolors.go b/cointop/default_colors.go similarity index 100% rename from cointop/defaultcolors.go rename to cointop/default_colors.go diff --git a/cointop/favorites.go b/cointop/favorites.go index aea336f..2531214 100644 --- a/cointop/favorites.go +++ b/cointop/favorites.go @@ -1,17 +1,17 @@ package cointop func (ct *Cointop) toggleFavorite() error { - ct.portfoliovisible = false + ct.State.portfolioVisible = false coin := ct.highlightedRowCoin() if coin == nil { return nil } - _, ok := ct.favorites[coin.Name] + _, ok := ct.State.favorites[coin.Name] if ok { - delete(ct.favorites, coin.Name) + delete(ct.State.favorites, coin.Name) coin.Favorite = false } else { - ct.favorites[coin.Name] = true + ct.State.favorites[coin.Name] = true coin.Favorite = true } go ct.updateTable() @@ -19,8 +19,8 @@ func (ct *Cointop) toggleFavorite() error { } func (ct *Cointop) toggleShowFavorites() error { - ct.portfoliovisible = false - ct.filterByFavorites = !ct.filterByFavorites + ct.State.portfolioVisible = false + ct.State.filterByFavorites = !ct.State.filterByFavorites go ct.updateTable() return nil } diff --git a/cointop/help.go b/cointop/help.go index f12c442..0e8f2b6 100644 --- a/cointop/help.go +++ b/cointop/help.go @@ -7,31 +7,23 @@ import ( "github.com/miguelmota/cointop/cointop/common/pad" ) -func (ct *Cointop) toggleHelp() error { - ct.helpvisible = !ct.helpvisible - if ct.helpvisible { - return ct.showHelp() - } - return ct.hideHelp() -} - func (ct *Cointop) updateHelp() { - keys := make([]string, 0, len(ct.shortcutkeys)) - for k := range ct.shortcutkeys { + keys := make([]string, 0, len(ct.State.shortcutKeys)) + for k := range ct.State.shortcutKeys { keys = append(keys, k) } sort.Strings(keys) - header := ct.colorscheme.MenuHeader(fmt.Sprintf(" Help %s\n\n", pad.Left("[q] close ", ct.maxtablewidth-10, " "))) + header := ct.colorscheme.MenuHeader(fmt.Sprintf(" Help %s\n\n", pad.Left("[q] close ", ct.maxTableWidth-10, " "))) cnt := 0 - h := ct.viewHeight(ct.helpviewname) + h := ct.viewHeight(ct.Views.Help.Name) percol := h - 6 cols := make([][]string, percol) for i := range cols { cols[i] = make([]string, 20) } for _, k := range keys { - v := ct.shortcutkeys[k] + v := ct.State.shortcutKeys[k] if cnt%percol == 0 { cnt = 0 } @@ -52,39 +44,47 @@ func (ct *Cointop) updateHelp() { body = fmt.Sprintf("%s\n", body) infoline := " List of keyboard shortcuts\n\n" - versionline := pad.Left(fmt.Sprintf("v%s", ct.version()), ct.maxtablewidth-5, " ") + versionline := pad.Left(fmt.Sprintf("v%s", ct.version()), ct.maxTableWidth-5, " ") content := header + infoline + body + versionline ct.update(func() { - if ct.helpview == nil { + if ct.Views.Help.Backing == nil { return } - ct.helpview.Clear() - ct.helpview.Frame = true - fmt.Fprintln(ct.helpview, content) + ct.Views.Help.Backing.Clear() + ct.Views.Help.Backing.Frame = true + fmt.Fprintln(ct.Views.Help.Backing, content) }) } func (ct *Cointop) showHelp() error { - ct.helpvisible = true + ct.State.helpVisible = true ct.updateHelp() - ct.setActiveView(ct.helpviewname) + ct.setActiveView(ct.Views.Help.Name) return nil } func (ct *Cointop) hideHelp() error { - ct.helpvisible = false - ct.setViewOnBottom(ct.helpviewname) - ct.setActiveView(ct.tableviewname) + ct.State.helpVisible = false + ct.setViewOnBottom(ct.Views.Help.Name) + ct.setActiveView(ct.Views.Table.Name) ct.update(func() { - if ct.helpview == nil { + if ct.Views.Help.Backing == nil { return } - ct.helpview.Clear() - ct.helpview.Frame = false - fmt.Fprintln(ct.helpview, "") + ct.Views.Help.Backing.Clear() + ct.Views.Help.Backing.Frame = false + fmt.Fprintln(ct.Views.Help.Backing, "") }) return nil } + +func (ct *Cointop) toggleHelp() error { + ct.State.helpVisible = !ct.State.helpVisible + if ct.State.helpVisible { + return ct.showHelp() + } + return ct.hideHelp() +} diff --git a/cointop/keybindings.go b/cointop/keybindings.go index f0a923b..1f7191a 100644 --- a/cointop/keybindings.go +++ b/cointop/keybindings.go @@ -207,7 +207,7 @@ func (ct *Cointop) parseKeys(s string) (interface{}, gocui.Modifier) { } func (ct *Cointop) keybindings(g *gocui.Gui) error { - for k, v := range ct.shortcutkeys { + for k, v := range ct.State.shortcutKeys { if k == "" { continue } @@ -345,29 +345,29 @@ func (ct *Cointop) keybindings(g *gocui.Gui) error { ct.setKeybindingMod(gocui.KeyCtrlZ, gocui.ModNone, ct.keyfn(ct.quit), "") // searchfield keys - ct.setKeybindingMod(gocui.KeyEnter, gocui.ModNone, ct.keyfn(ct.doSearch), ct.searchfieldviewname) - ct.setKeybindingMod(gocui.KeyEsc, gocui.ModNone, ct.keyfn(ct.cancelSearch), ct.searchfieldviewname) + ct.setKeybindingMod(gocui.KeyEnter, gocui.ModNone, ct.keyfn(ct.doSearch), ct.Views.SearchField.Name) + ct.setKeybindingMod(gocui.KeyEsc, gocui.ModNone, ct.keyfn(ct.cancelSearch), ct.Views.SearchField.Name) // keys to quit help when open - ct.setKeybindingMod(gocui.KeyEsc, gocui.ModNone, ct.keyfn(ct.hideHelp), ct.helpviewname) - ct.setKeybindingMod('q', gocui.ModNone, ct.keyfn(ct.hideHelp), ct.helpviewname) + ct.setKeybindingMod(gocui.KeyEsc, gocui.ModNone, ct.keyfn(ct.hideHelp), ct.Views.Help.Name) + ct.setKeybindingMod('q', gocui.ModNone, ct.keyfn(ct.hideHelp), ct.Views.Help.Name) // keys to quit portfolio update menu when open - ct.setKeybindingMod(gocui.KeyEsc, gocui.ModNone, ct.keyfn(ct.hidePortfolioUpdateMenu), ct.inputviewname) - ct.setKeybindingMod('q', gocui.ModNone, ct.keyfn(ct.hidePortfolioUpdateMenu), ct.inputviewname) + ct.setKeybindingMod(gocui.KeyEsc, gocui.ModNone, ct.keyfn(ct.hidePortfolioUpdateMenu), ct.Views.Input.Name) + ct.setKeybindingMod('q', gocui.ModNone, ct.keyfn(ct.hidePortfolioUpdateMenu), ct.Views.Input.Name) // keys to update portfolio holdings - ct.setKeybindingMod(gocui.KeyEnter, gocui.ModNone, ct.keyfn(ct.setPortfolioHoldings), ct.inputviewname) + ct.setKeybindingMod(gocui.KeyEnter, gocui.ModNone, ct.keyfn(ct.setPortfolioHoldings), ct.Views.Input.Name) // keys to quit convert menu when open - ct.setKeybindingMod(gocui.KeyEsc, gocui.ModNone, ct.keyfn(ct.hideConvertMenu), ct.convertmenuviewname) - ct.setKeybindingMod('q', gocui.ModNone, ct.keyfn(ct.hideConvertMenu), ct.convertmenuviewname) + ct.setKeybindingMod(gocui.KeyEsc, gocui.ModNone, ct.keyfn(ct.hideConvertMenu), ct.Views.ConvertMenu.Name) + ct.setKeybindingMod('q', gocui.ModNone, ct.keyfn(ct.hideConvertMenu), ct.Views.ConvertMenu.Name) // character key press to select option // TODO: use scrolling table keys := ct.sortedSupportedCurrencyConversions() for i, k := range keys { - ct.setKeybindingMod(rune(alphanumericcharacters[i]), gocui.ModNone, ct.keyfn(ct.setCurrencyConverstion(k)), ct.convertmenuviewname) + ct.setKeybindingMod(rune(alphanumericcharacters[i]), gocui.ModNone, ct.keyfn(ct.setCurrencyConverstion(k)), ct.Views.ConvertMenu.Name) } return nil @@ -392,7 +392,7 @@ func (ct *Cointop) keyfn(fn func() error) func(g *gocui.Gui, v *gocui.View) erro func (ct *Cointop) handleHkey(key interface{}) func(g *gocui.Gui, v *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error { - if k, ok := key.(rune); ok && k == 'h' && ct.portfoliovisible { + if k, ok := key.(rune); ok && k == 'h' && ct.State.portfolioVisible { ct.sortToggle("holdings", true) } else { ct.prevPage() diff --git a/cointop/layout.go b/cointop/layout.go index 6f6e28c..1c2a586 100644 --- a/cointop/layout.go +++ b/cointop/layout.go @@ -7,6 +7,8 @@ import ( "github.com/jroimartin/gocui" ) +// TODO: break up into small functions + // layout sets initial layout func (ct *Cointop) layout(g *gocui.Gui) error { maxX, maxY := ct.size() @@ -17,63 +19,63 @@ func (ct *Cointop) layout(g *gocui.Gui) error { chartHeight := 10 statusbarHeight := 1 - if ct.onlyTable { - ct.hideMarketbar = true - ct.hideChart = true - ct.hideStatusbar = true + if ct.State.onlyTable { + ct.State.hideMarketbar = true + ct.State.hideChart = true + ct.State.hideStatusbar = true } - if ct.hideMarketbar { + if ct.State.hideMarketbar { marketbarHeight = 0 } - if ct.hideChart { + if ct.State.hideChart { chartHeight = 0 } - if ct.hideStatusbar { + if ct.State.hideStatusbar { statusbarHeight = 0 } - if !ct.hideMarketbar { - if v, err := g.SetView(ct.marketbarviewname, 0, topOffset, maxX, 2); err != nil { + if !ct.State.hideMarketbar { + if v, err := g.SetView(ct.Views.Marketbar.Name, 0, topOffset, maxX, 2); err != nil { if err != gocui.ErrUnknownView { return err } - ct.marketbarview = v - ct.marketbarview.Frame = false - ct.colorscheme.SetViewColor(ct.marketbarview, "marketbar") + ct.Views.Marketbar.Backing = v + ct.Views.Marketbar.Backing.Frame = false + ct.colorscheme.SetViewColor(ct.Views.Marketbar.Backing, "marketbar") go func() { ct.updateMarketbar() - _, found := ct.cache.Get(ct.marketbarviewname) + _, found := ct.cache.Get(ct.Views.Marketbar.Name) if found { - ct.cache.Delete(ct.marketbarviewname) + ct.cache.Delete(ct.Views.Marketbar.Name) ct.updateMarketbar() } }() } } else { - if ct.marketbarview != nil { - if err := g.DeleteView(ct.marketbarviewname); err != nil { + if ct.Views.Marketbar.Backing != nil { + if err := g.DeleteView(ct.Views.Marketbar.Name); err != nil { return err } - ct.marketbarview = nil + ct.Views.Marketbar.Backing = nil } } topOffset = topOffset + marketbarHeight - if !ct.hideChart { - if v, err := g.SetView(ct.chartviewname, 0, topOffset, maxX, topOffset+chartHeight+marketbarHeight); err != nil { + if !ct.State.hideChart { + if v, err := g.SetView(ct.Views.Chart.Name, 0, topOffset, maxX, topOffset+chartHeight+marketbarHeight); err != nil { if err != gocui.ErrUnknownView { return err } - ct.chartview = v - ct.chartview.Frame = false - ct.colorscheme.SetViewColor(ct.chartview, "chart") + ct.Views.Chart.Backing = v + ct.Views.Chart.Backing.Frame = false + ct.colorscheme.SetViewColor(ct.Views.Chart.Backing, "chart") go func() { ct.updateChart() - cachekey := strings.ToLower(fmt.Sprintf("%s_%s", "globaldata", strings.Replace(ct.selectedchartrange, " ", "", -1))) + cachekey := strings.ToLower(fmt.Sprintf("%s_%s", "globaldata", strings.Replace(ct.State.selectedChartRange, " ", "", -1))) _, found := ct.cache.Get(cachekey) if found { ct.cache.Delete(cachekey) @@ -82,37 +84,37 @@ func (ct *Cointop) layout(g *gocui.Gui) error { }() } } else { - if ct.chartview != nil { - if err := g.DeleteView(ct.chartviewname); err != nil { + if ct.Views.Chart.Backing != nil { + if err := g.DeleteView(ct.Views.Chart.Name); err != nil { return err } - ct.chartview = nil + ct.Views.Chart.Backing = nil } } topOffset = topOffset + chartHeight - if v, err := g.SetView(ct.headerviewname, 0, topOffset, ct.maxtablewidth, topOffset+2); err != nil { + if v, err := g.SetView(ct.Views.Header.Name, 0, topOffset, ct.maxTableWidth, topOffset+2); err != nil { if err != gocui.ErrUnknownView { return err } - ct.headersview = v - ct.headersview.Frame = false - ct.colorscheme.SetViewColor(ct.headersview, "table_header") + ct.Views.Header.Backing = v + ct.Views.Header.Backing.Frame = false + ct.colorscheme.SetViewColor(ct.Views.Header.Backing, "table_header") go ct.updateHeaders() } topOffset = topOffset + headerHeight - if v, err := g.SetView(ct.tableviewname, 0, topOffset, ct.maxtablewidth, maxY-statusbarHeight); err != nil { + if v, err := g.SetView(ct.Views.Table.Name, 0, topOffset, ct.maxTableWidth, maxY-statusbarHeight); err != nil { if err != gocui.ErrUnknownView { return err } - ct.tableview = v - ct.tableview.Frame = false - ct.tableview.Highlight = true - ct.colorscheme.SetViewActiveColor(ct.tableview, "table_row_active") - _, found := ct.cache.Get("allcoinsslugmap") + ct.Views.Table.Backing = v + ct.Views.Table.Backing.Frame = false + ct.Views.Table.Backing.Highlight = true + ct.colorscheme.SetViewActiveColor(ct.Views.Table.Backing, "table_row_active") + _, found := ct.cache.Get("allCoinsSlugMap") if found { - ct.cache.Delete("allcoinsslugmap") + ct.cache.Delete("allCoinsSlugMap") } go func() { ct.updateCoins() @@ -120,123 +122,84 @@ func (ct *Cointop) layout(g *gocui.Gui) error { }() } - if !ct.hideStatusbar { - if v, err := g.SetView(ct.statusbarviewname, 0, maxY-statusbarHeight-1, ct.maxtablewidth, maxY); err != nil { + if !ct.State.hideStatusbar { + if v, err := g.SetView(ct.Views.Statusbar.Name, 0, maxY-statusbarHeight-1, ct.maxTableWidth, maxY); err != nil { if err != gocui.ErrUnknownView { return err } - ct.statusbarview = v - ct.statusbarview.Frame = false - ct.colorscheme.SetViewColor(ct.statusbarview, "statusbar") + ct.Views.Statusbar.Backing = v + ct.Views.Statusbar.Backing.Frame = false + ct.colorscheme.SetViewColor(ct.Views.Statusbar.Backing, "statusbar") go ct.updateStatusbar("") } } else { - if ct.statusbarview != nil { - if err := g.DeleteView(ct.statusbarviewname); err != nil { + if ct.Views.Statusbar.Backing != nil { + if err := g.DeleteView(ct.Views.Statusbar.Name); err != nil { return err } - ct.statusbarview = nil + ct.Views.Statusbar.Backing = nil } } - if v, err := g.SetView(ct.searchfieldviewname, 0, maxY-2, ct.maxtablewidth, maxY); err != nil { + if v, err := g.SetView(ct.Views.SearchField.Name, 0, maxY-2, ct.maxTableWidth, maxY); err != nil { if err != gocui.ErrUnknownView { return err } - ct.searchfield = v - ct.searchfield.Editable = true - ct.searchfield.Wrap = true - ct.searchfield.Frame = false - ct.colorscheme.SetViewColor(ct.searchfield, "searchbar") + ct.Views.SearchField.Backing = v + ct.Views.SearchField.Backing.Editable = true + ct.Views.SearchField.Backing.Wrap = true + ct.Views.SearchField.Backing.Frame = false + ct.colorscheme.SetViewColor(ct.Views.SearchField.Backing, "searchbar") } - if v, err := g.SetView(ct.helpviewname, 1, 1, ct.maxtablewidth-1, maxY-1); err != nil { + if v, err := g.SetView(ct.Views.Help.Name, 1, 1, ct.maxTableWidth-1, maxY-1); err != nil { if err != gocui.ErrUnknownView { return err } - ct.helpview = v - ct.helpview.Frame = false - ct.colorscheme.SetViewColor(ct.helpview, "menu") + ct.Views.Help.Backing = v + ct.Views.Help.Backing.Frame = false + ct.colorscheme.SetViewColor(ct.Views.Help.Backing, "menu") } - if v, err := g.SetView(ct.portfolioupdatemenuviewname, 1, 1, ct.maxtablewidth-1, maxY-1); err != nil { + if v, err := g.SetView(ct.Views.PortfolioUpdateMenu.Name, 1, 1, ct.maxTableWidth-1, maxY-1); err != nil { if err != gocui.ErrUnknownView { return err } - ct.portfolioupdatemenuview = v - ct.portfolioupdatemenuview.Frame = false - ct.colorscheme.SetViewColor(ct.portfolioupdatemenuview, "menu") + ct.Views.PortfolioUpdateMenu.Backing = v + ct.Views.PortfolioUpdateMenu.Backing.Frame = false + ct.colorscheme.SetViewColor(ct.Views.PortfolioUpdateMenu.Backing, "menu") } - if v, err := g.SetView(ct.inputviewname, 3, 6, 30, 8); err != nil { + if v, err := g.SetView(ct.Views.Input.Name, 3, 6, 30, 8); err != nil { if err != gocui.ErrUnknownView { return err } - ct.inputview = v - ct.inputview.Frame = true - ct.inputview.Editable = true - ct.inputview.Wrap = true - ct.colorscheme.SetViewColor(ct.inputview, "menu") + ct.Views.Input.Backing = v + ct.Views.Input.Backing.Frame = true + ct.Views.Input.Backing.Editable = true + ct.Views.Input.Backing.Wrap = true + ct.colorscheme.SetViewColor(ct.Views.Input.Backing, "menu") } - if v, err := g.SetView(ct.convertmenuviewname, 1, 1, ct.maxtablewidth-1, maxY-1); err != nil { + if v, err := g.SetView(ct.Views.ConvertMenu.Name, 1, 1, ct.maxTableWidth-1, maxY-1); err != nil { if err != gocui.ErrUnknownView { return err } - ct.convertmenuview = v - ct.convertmenuview.Frame = false - ct.colorscheme.SetViewColor(ct.convertmenuview, "menu") + ct.Views.ConvertMenu.Backing = v + ct.Views.ConvertMenu.Backing.Frame = false + ct.colorscheme.SetViewColor(ct.Views.ConvertMenu.Backing, "menu") // run only once on init. // this bit of code should be at the bottom ct.g = g - g.SetViewOnBottom(ct.searchfieldviewname) // hide - g.SetViewOnBottom(ct.helpviewname) // hide - g.SetViewOnBottom(ct.convertmenuviewname) // hide - g.SetViewOnBottom(ct.portfolioupdatemenuviewname) // hide - g.SetViewOnBottom(ct.inputviewname) // hide - ct.setActiveView(ct.tableviewname) + g.SetViewOnBottom(ct.Views.SearchField.Name) // hide + g.SetViewOnBottom(ct.Views.Help.Name) // hide + g.SetViewOnBottom(ct.Views.ConvertMenu.Name) // hide + g.SetViewOnBottom(ct.Views.PortfolioUpdateMenu.Name) // hide + g.SetViewOnBottom(ct.Views.Input.Name) // hide + ct.setActiveView(ct.Views.Table.Name) ct.intervalFetchData() } return nil } - -func (ct *Cointop) setActiveView(v string) error { - ct.g.SetViewOnTop(v) - ct.g.SetCurrentView(v) - if v == ct.searchfieldviewname { - ct.searchfield.Clear() - ct.searchfield.SetCursor(1, 0) - fmt.Fprintf(ct.searchfield, "%s", "/") - } else if v == ct.tableviewname { - ct.g.SetViewOnTop(ct.statusbarviewname) - } - if v == ct.portfolioupdatemenuviewname { - ct.g.SetViewOnTop(ct.inputviewname) - ct.g.SetCurrentView(ct.inputviewname) - } - return nil -} - -func (ct *Cointop) activeViewName() string { - return ct.g.CurrentView().Name() -} - -func (ct *Cointop) setViewOnBottom(v string) error { - _, err := ct.g.SetViewOnBottom(v) - return err -} - -func (ct *Cointop) intervalFetchData() { - go func() { - for { - select { - case <-ct.forcerefresh: - ct.refreshAll() - case <-ct.refreshTicker.C: - ct.refreshAll() - } - } - }() -} diff --git a/cointop/list.go b/cointop/list.go index f79dbf0..2649c38 100644 --- a/cointop/list.go +++ b/cointop/list.go @@ -14,23 +14,23 @@ var updatecoinsmux sync.Mutex func (ct *Cointop) updateCoins() error { coinslock.Lock() defer coinslock.Unlock() - cachekey := ct.cacheKey("allcoinsslugmap") + cachekey := ct.cacheKey("allCoinsSlugMap") var err error - var allcoinsslugmap map[string]types.Coin + var allCoinsSlugMap map[string]types.Coin cached, found := ct.cache.Get(cachekey) _ = cached if found { // cache hit - allcoinsslugmap, _ = cached.(map[string]types.Coin) + allCoinsSlugMap, _ = cached.(map[string]types.Coin) ct.debuglog("soft cache hit") } // cache miss - if allcoinsslugmap == nil { + if allCoinsSlugMap == nil { ct.debuglog("cache miss") ch := make(chan []types.Coin) - err = ct.api.GetAllCoinData(ct.currencyconversion, ch) + err = ct.api.GetAllCoinData(ct.State.currencyConversion, ch) if err != nil { return err } @@ -39,7 +39,7 @@ func (ct *Cointop) updateCoins() error { go ct.processCoins(coins) } } else { - ct.processCoinsMap(allcoinsslugmap) + ct.processCoinsMap(allCoinsSlugMap) } return nil @@ -58,14 +58,14 @@ func (ct *Cointop) processCoins(coins []types.Coin) { updatecoinsmux.Lock() defer updatecoinsmux.Unlock() - cachekey := ct.cacheKey("allcoinsslugmap") - ct.cache.Set(cachekey, ct.allcoinsslugmap, 10*time.Second) - filecache.Set(cachekey, ct.allcoinsslugmap, 24*time.Hour) + cachekey := ct.cacheKey("allCoinsSlugMap") + ct.cache.Set(cachekey, ct.State.allCoinsSlugMap, 10*time.Second) + filecache.Set(cachekey, ct.State.allCoinsSlugMap, 24*time.Hour) for _, v := range coins { k := v.Name - last := ct.allcoinsslugmap[k] - ct.allcoinsslugmap[k] = &Coin{ + last := ct.State.allCoinsSlugMap[k] + ct.State.allCoinsSlugMap[k] = &Coin{ ID: v.ID, Name: v.Name, Symbol: v.Symbol, @@ -81,23 +81,23 @@ func (ct *Cointop) processCoins(coins []types.Coin) { LastUpdated: v.LastUpdated, } if last != nil { - ct.allcoinsslugmap[k].Favorite = last.Favorite + ct.State.allCoinsSlugMap[k].Favorite = last.Favorite } } - if len(ct.allcoins) < len(ct.allcoinsslugmap) { + if len(ct.State.allCoins) < len(ct.State.allCoinsSlugMap) { list := []*Coin{} for _, v := range coins { k := v.Name - coin := ct.allcoinsslugmap[k] + coin := ct.State.allCoinsSlugMap[k] list = append(list, coin) } - ct.allcoins = append(ct.allcoins, list...) + ct.State.allCoins = append(ct.State.allCoins, list...) } else { // update list in place without changing order - for i := range ct.allcoinsslugmap { - cm := ct.allcoinsslugmap[i] - for k := range ct.allcoins { - c := ct.allcoins[k] + for i := range ct.State.allCoinsSlugMap { + cm := ct.State.allCoinsSlugMap[i] + for k := range ct.State.allCoins { + c := ct.State.allCoins[k] if c.ID == cm.ID { // TODO: improve this c.ID = cm.ID @@ -120,7 +120,7 @@ func (ct *Cointop) processCoins(coins []types.Coin) { } time.AfterFunc(10*time.Millisecond, func() { - ct.sort(ct.sortby, ct.sortdesc, ct.coins, true) + ct.sort(ct.State.sortBy, ct.State.sortDesc, ct.State.coins, true) ct.updateTable() }) } diff --git a/cointop/marketbar.go b/cointop/marketbar.go index ec109d2..4051a3b 100644 --- a/cointop/marketbar.go +++ b/cointop/marketbar.go @@ -13,26 +13,26 @@ import ( ) func (ct *Cointop) updateMarketbar() error { - if ct.marketbarview == nil { + if ct.Views.Marketbar.Backing == nil { return nil } maxX := ct.width() logo := "❯❯❯cointop" - if ct.colorschemename == "cointop" { + if ct.colorschemeName == "cointop" { logo = fmt.Sprintf("%s%s%s%s", color.Green("❯"), color.Cyan("❯"), color.Green("❯"), color.Cyan("cointop")) } var content string - if ct.portfoliovisible { + if ct.State.portfolioVisible { total := ct.getPortfolioTotal() totalstr := humanize.Commaf(total) - if !(ct.currencyconversion == "BTC" || ct.currencyconversion == "ETH" || total < 1) { + if !(ct.State.currencyConversion == "BTC" || ct.State.currencyConversion == "ETH" || total < 1) { total = math.Round(total*1e2) / 1e2 totalstr = humanize.Commaf2(total) } - timeframe := ct.selectedchartrange + timeframe := ct.State.selectedChartRange chartname := ct.selectedCoinName() var charttitle string if chartname == "" { @@ -63,7 +63,7 @@ func (ct *Cointop) updateMarketbar() error { } chartInfo := "" - if !ct.hideChart { + if !ct.State.hideChart { chartInfo = fmt.Sprintf( "[ Chart: %s %s ] ", charttitle, @@ -93,7 +93,7 @@ func (ct *Cointop) updateMarketbar() error { } if market.TotalMarketCapUSD == 0 { - market, err = ct.api.GetGlobalMarketData(ct.currencyconversion) + market, err = ct.api.GetGlobalMarketData(ct.State.currencyConversion) if err != nil { filecache.Get(cachekey, &market) } @@ -104,14 +104,14 @@ func (ct *Cointop) updateMarketbar() error { }() } - timeframe := ct.selectedchartrange + timeframe := ct.State.selectedChartRange chartname := ct.selectedCoinName() if chartname == "" { chartname = "Global" } chartInfo := "" - if !ct.hideChart { + if !ct.State.hideChart { chartInfo = fmt.Sprintf( "[ Chart: %s %s ] ", ct.colorscheme.MarketBarLabelActive(chartname), @@ -133,12 +133,12 @@ func (ct *Cointop) updateMarketbar() error { content = ct.colorscheme.Marketbar(content) ct.update(func() { - if ct.marketbarview == nil { + if ct.Views.Marketbar.Backing == nil { return } - ct.marketbarview.Clear() - fmt.Fprintln(ct.marketbarview, content) + ct.Views.Marketbar.Backing.Clear() + fmt.Fprintln(ct.Views.Marketbar.Backing, content) }) return nil diff --git a/cointop/navigation.go b/cointop/navigation.go index 1c517bf..4b83105 100644 --- a/cointop/navigation.go +++ b/cointop/navigation.go @@ -1,15 +1,15 @@ package cointop func (ct *Cointop) currentPage() int { - return ct.page + 1 + return ct.State.page + 1 } func (ct *Cointop) currentDisplayPage() int { - return ct.page + 1 + return ct.State.page + 1 } func (ct *Cointop) totalPages() int { - return ct.getListCount() / ct.perpage + return ct.getListCount() / ct.State.perPage } func (ct *Cointop) totalPagesDisplay() int { @@ -17,48 +17,30 @@ func (ct *Cointop) totalPagesDisplay() int { } func (ct *Cointop) totalPerPage() int { - return ct.perpage + return ct.State.perPage } func (ct *Cointop) setPage(page int) int { - if (page*ct.perpage) < ct.getListCount() && page >= 0 { - ct.page = page + if (page*ct.State.perPage) < ct.getListCount() && page >= 0 { + ct.State.page = page } - return ct.page -} - -func (ct *Cointop) highlightRow(idx int) error { - ct.tableview.SetOrigin(0, 0) - ct.tableview.SetCursor(0, 0) - ox, _ := ct.tableview.Origin() - cx, _ := ct.tableview.Cursor() - _, sy := ct.tableview.Size() - perpage := ct.totalPerPage() - p := idx % perpage - oy := (p / sy) * sy - cy := p % sy - if oy > 0 { - ct.tableview.SetOrigin(ox, oy) - } - ct.tableview.SetCursor(cx, cy) - - return nil + return ct.State.page } func (ct *Cointop) cursorDown() error { - if ct.tableview == nil { + if ct.Views.Table.Backing == nil { return nil } - _, y := ct.tableview.Origin() - cx, cy := ct.tableview.Cursor() - numRows := len(ct.coins) - 1 + _, y := ct.Views.Table.Backing.Origin() + cx, cy := ct.Views.Table.Backing.Cursor() + numRows := len(ct.State.coins) - 1 if (cy + y + 1) > numRows { return nil } - if err := ct.tableview.SetCursor(cx, cy+1); err != nil { - ox, oy := ct.tableview.Origin() + if err := ct.Views.Table.Backing.SetCursor(cx, cy+1); err != nil { + ox, oy := ct.Views.Table.Backing.Origin() // set origin scrolls - if err := ct.tableview.SetOrigin(ox, oy+1); err != nil { + if err := ct.Views.Table.Backing.SetOrigin(ox, oy+1); err != nil { return err } } @@ -67,14 +49,14 @@ func (ct *Cointop) cursorDown() error { } func (ct *Cointop) cursorUp() error { - if ct.tableview == nil { + if ct.Views.Table.Backing == nil { return nil } - ox, oy := ct.tableview.Origin() - cx, cy := ct.tableview.Cursor() - if err := ct.tableview.SetCursor(cx, cy-1); err != nil && oy > 0 { + ox, oy := ct.Views.Table.Backing.Origin() + cx, cy := ct.Views.Table.Backing.Cursor() + if err := ct.Views.Table.Backing.SetCursor(cx, cy-1); err != nil && oy > 0 { // set origin scrolls - if err := ct.tableview.SetOrigin(ox, oy-1); err != nil { + if err := ct.Views.Table.Backing.SetOrigin(ox, oy-1); err != nil { return err } } @@ -83,14 +65,14 @@ func (ct *Cointop) cursorUp() error { } func (ct *Cointop) pageDown() error { - if ct.tableview == nil { + if ct.Views.Table.Backing == nil { return nil } - ox, oy := ct.tableview.Origin() // this is prev origin position - cx, _ := ct.tableview.Cursor() // relative cursor position - _, sy := ct.tableview.Size() // rows in visible view + ox, oy := ct.Views.Table.Backing.Origin() // this is prev origin position + cx, _ := ct.Views.Table.Backing.Cursor() // relative cursor position + _, sy := ct.Views.Table.Backing.Size() // rows in visible view k := oy + sy - l := len(ct.coins) + l := len(ct.State.coins) // end of table if (oy + sy + sy) > l { k = l - sy @@ -101,12 +83,12 @@ func (ct *Cointop) pageDown() error { sy = l } - if err := ct.tableview.SetOrigin(ox, k); err != nil { + if err := ct.Views.Table.Backing.SetOrigin(ox, k); err != nil { return err } // move cursor to last line if can't scroll further if k == oy { - if err := ct.tableview.SetCursor(cx, sy-1); err != nil { + if err := ct.Views.Table.Backing.SetCursor(cx, sy-1); err != nil { return err } } @@ -115,22 +97,22 @@ func (ct *Cointop) pageDown() error { } func (ct *Cointop) pageUp() error { - if ct.tableview == nil { + if ct.Views.Table.Backing == nil { return nil } - ox, oy := ct.tableview.Origin() - cx, _ := ct.tableview.Cursor() // relative cursor position - _, sy := ct.tableview.Size() // rows in visible view + ox, oy := ct.Views.Table.Backing.Origin() + cx, _ := ct.Views.Table.Backing.Cursor() // relative cursor position + _, sy := ct.Views.Table.Backing.Size() // rows in visible view k := oy - sy if k < 0 { k = 0 } - if err := ct.tableview.SetOrigin(ox, k); err != nil { + if err := ct.Views.Table.Backing.SetOrigin(ox, k); err != nil { return err } // move cursor to first line if can't scroll further if k == oy { - if err := ct.tableview.SetCursor(cx, 0); err != nil { + if err := ct.Views.Table.Backing.SetCursor(cx, 0); err != nil { return err } } @@ -139,15 +121,15 @@ func (ct *Cointop) pageUp() error { } func (ct *Cointop) navigateFirstLine() error { - if ct.tableview == nil { + if ct.Views.Table.Backing == nil { return nil } - ox, _ := ct.tableview.Origin() - cx, _ := ct.tableview.Cursor() - if err := ct.tableview.SetOrigin(ox, 0); err != nil { + ox, _ := ct.Views.Table.Backing.Origin() + cx, _ := ct.Views.Table.Backing.Cursor() + if err := ct.Views.Table.Backing.SetOrigin(ox, 0); err != nil { return err } - if err := ct.tableview.SetCursor(cx, 0); err != nil { + if err := ct.Views.Table.Backing.SetCursor(cx, 0); err != nil { return err } ct.rowChanged() @@ -155,18 +137,18 @@ func (ct *Cointop) navigateFirstLine() error { } func (ct *Cointop) navigateLastLine() error { - if ct.tableview == nil { + if ct.Views.Table.Backing == nil { return nil } - ox, _ := ct.tableview.Origin() - cx, _ := ct.tableview.Cursor() - _, sy := ct.tableview.Size() - l := len(ct.coins) + ox, _ := ct.Views.Table.Backing.Origin() + cx, _ := ct.Views.Table.Backing.Cursor() + _, sy := ct.Views.Table.Backing.Size() + l := len(ct.State.coins) k := l - sy - if err := ct.tableview.SetOrigin(ox, k); err != nil { + if err := ct.Views.Table.Backing.SetOrigin(ox, k); err != nil { return err } - if err := ct.tableview.SetCursor(cx, sy-1); err != nil { + if err := ct.Views.Table.Backing.SetCursor(cx, sy-1); err != nil { return err } ct.rowChanged() @@ -174,11 +156,11 @@ func (ct *Cointop) navigateLastLine() error { } func (ct *Cointop) navigatePageFirstLine() error { - if ct.tableview == nil { + if ct.Views.Table.Backing == nil { return nil } - cx, _ := ct.tableview.Cursor() - if err := ct.tableview.SetCursor(cx, 0); err != nil { + cx, _ := ct.Views.Table.Backing.Cursor() + if err := ct.Views.Table.Backing.SetCursor(cx, 0); err != nil { return err } ct.rowChanged() @@ -186,12 +168,12 @@ func (ct *Cointop) navigatePageFirstLine() error { } func (ct *Cointop) navigatePageMiddleLine() error { - if ct.tableview == nil { + if ct.Views.Table.Backing == nil { return nil } - cx, _ := ct.tableview.Cursor() - _, sy := ct.tableview.Size() - if err := ct.tableview.SetCursor(cx, (sy/2)-1); err != nil { + cx, _ := ct.Views.Table.Backing.Cursor() + _, sy := ct.Views.Table.Backing.Size() + if err := ct.Views.Table.Backing.SetCursor(cx, (sy/2)-1); err != nil { return err } ct.rowChanged() @@ -199,12 +181,12 @@ func (ct *Cointop) navigatePageMiddleLine() error { } func (ct *Cointop) navigatePageLastLine() error { - if ct.tableview == nil { + if ct.Views.Table.Backing == nil { return nil } - cx, _ := ct.tableview.Cursor() - _, sy := ct.tableview.Size() - if err := ct.tableview.SetCursor(cx, sy-1); err != nil { + cx, _ := ct.Views.Table.Backing.Cursor() + _, sy := ct.Views.Table.Backing.Size() + if err := ct.Views.Table.Backing.SetCursor(cx, sy-1); err != nil { return err } ct.rowChanged() @@ -215,32 +197,32 @@ func (ct *Cointop) prevPage() error { if ct.isFirstPage() { return nil } - ct.setPage(ct.page - 1) + ct.setPage(ct.State.page - 1) ct.updateTable() ct.rowChanged() return nil } func (ct *Cointop) nextPage() error { - ct.setPage(ct.page + 1) + ct.setPage(ct.State.page + 1) ct.updateTable() ct.rowChanged() return nil } func (ct *Cointop) firstPage() error { - ct.page = 0 + ct.State.page = 0 ct.updateTable() ct.rowChanged() return nil } func (ct *Cointop) isFirstPage() bool { - return ct.page == 0 + return ct.State.page == 0 } func (ct *Cointop) lastPage() error { - ct.page = ct.getListCount() / ct.perpage + ct.State.page = ct.getListCount() / ct.State.perPage ct.updateTable() ct.rowChanged() return nil @@ -255,3 +237,21 @@ func (ct *Cointop) goToGlobalIndex(idx int) error { ct.updateTable() return nil } + +func (ct *Cointop) highlightRow(idx int) error { + ct.Views.Table.Backing.SetOrigin(0, 0) + ct.Views.Table.Backing.SetCursor(0, 0) + ox, _ := ct.Views.Table.Backing.Origin() + cx, _ := ct.Views.Table.Backing.Cursor() + _, sy := ct.Views.Table.Backing.Size() + perpage := ct.totalPerPage() + p := idx % perpage + oy := (p / sy) * sy + cy := p % sy + if oy > 0 { + ct.Views.Table.Backing.SetOrigin(ox, oy) + } + ct.Views.Table.Backing.SetCursor(cx, cy) + + return nil +} diff --git a/cointop/portfolio.go b/cointop/portfolio.go index 7440792..62734d1 100644 --- a/cointop/portfolio.go +++ b/cointop/portfolio.go @@ -11,22 +11,22 @@ import ( ) func (ct *Cointop) togglePortfolio() error { - ct.filterByFavorites = false - ct.portfoliovisible = !ct.portfoliovisible + ct.State.filterByFavorites = false + ct.State.portfolioVisible = !ct.State.portfolioVisible go ct.updateTable() return nil } func (ct *Cointop) toggleShowPortfolio() error { - ct.filterByFavorites = false - ct.portfoliovisible = true + ct.State.filterByFavorites = false + ct.State.portfolioVisible = true go ct.updateTable() return nil } func (ct *Cointop) togglePortfolioUpdateMenu() error { - ct.portfolioupdatemenuvisible = !ct.portfolioupdatemenuvisible - if ct.portfolioupdatemenuvisible { + ct.State.portfolioUpdateMenuVisible = !ct.State.portfolioUpdateMenuVisible + if ct.State.portfolioUpdateMenuVisible { return ct.showPortfolioUpdateMenu() } return ct.hidePortfolioUpdateMenu() @@ -34,7 +34,7 @@ func (ct *Cointop) togglePortfolioUpdateMenu() error { func (ct *Cointop) updatePortfolioUpdateMenu() { coin := ct.highlightedRowCoin() - exists := ct.portfolioEntryExists(coin) + exists := ct.PortfolioEntryExists(coin) value := strconv.FormatFloat(coin.Holdings, 'f', -1, 64) var mode string var current string @@ -47,16 +47,16 @@ func (ct *Cointop) updatePortfolioUpdateMenu() { mode = "Add" submitText = "Add" } - header := ct.colorscheme.MenuHeader(fmt.Sprintf(" %s Portfolio Entry %s\n\n", mode, pad.Left("[q] close ", ct.maxtablewidth-26, " "))) + header := ct.colorscheme.MenuHeader(fmt.Sprintf(" %s Portfolio Entry %s\n\n", mode, pad.Left("[q] close ", ct.maxTableWidth-26, " "))) label := fmt.Sprintf(" Enter holdings for %s %s", ct.colorscheme.MenuLabel(coin.Name), current) content := fmt.Sprintf("%s\n%s\n\n%s%s\n\n\n [Enter] %s [ESC] Cancel", header, label, strings.Repeat(" ", 29), coin.Symbol, submitText) ct.update(func() { - ct.portfolioupdatemenuview.Clear() - ct.portfolioupdatemenuview.Frame = true - fmt.Fprintln(ct.portfolioupdatemenuview, content) - fmt.Fprintln(ct.inputview, value) - ct.inputview.SetCursor(len(value), 0) + ct.Views.PortfolioUpdateMenu.Backing.Clear() + ct.Views.PortfolioUpdateMenu.Backing.Frame = true + fmt.Fprintln(ct.Views.PortfolioUpdateMenu.Backing, content) + fmt.Fprintln(ct.Views.Input.Backing, value) + ct.Views.Input.Backing.SetCursor(len(value), 0) }) } @@ -67,28 +67,28 @@ func (ct *Cointop) showPortfolioUpdateMenu() error { return nil } - ct.portfolioupdatemenuvisible = true + ct.State.portfolioUpdateMenuVisible = true ct.updatePortfolioUpdateMenu() - ct.setActiveView(ct.portfolioupdatemenuviewname) + ct.setActiveView(ct.Views.PortfolioUpdateMenu.Name) return nil } func (ct *Cointop) hidePortfolioUpdateMenu() error { - ct.portfolioupdatemenuvisible = false - ct.setViewOnBottom(ct.portfolioupdatemenuviewname) - ct.setViewOnBottom(ct.inputviewname) - ct.setActiveView(ct.tableviewname) + ct.State.portfolioUpdateMenuVisible = false + ct.setViewOnBottom(ct.Views.PortfolioUpdateMenu.Name) + ct.setViewOnBottom(ct.Views.Input.Name) + ct.setActiveView(ct.Views.Table.Name) ct.update(func() { - if ct.portfolioupdatemenuview == nil { + if ct.Views.PortfolioUpdateMenu.Backing == nil { return } - ct.portfolioupdatemenuview.Clear() - ct.portfolioupdatemenuview.Frame = false - fmt.Fprintln(ct.portfolioupdatemenuview, "") + ct.Views.PortfolioUpdateMenu.Backing.Clear() + ct.Views.PortfolioUpdateMenu.Backing.Frame = false + fmt.Fprintln(ct.Views.PortfolioUpdateMenu.Backing, "") - ct.inputview.Clear() - fmt.Fprintln(ct.inputview, "") + ct.Views.Input.Backing.Clear() + fmt.Fprintln(ct.Views.Input.Backing, "") }) return nil } @@ -99,7 +99,7 @@ func (ct *Cointop) setPortfolioHoldings() error { coin := ct.highlightedRowCoin() b := make([]byte, 100) - n, err := ct.inputview.Read(b) + n, err := ct.Views.Input.Backing.Read(b) if n == 0 { return nil } @@ -129,20 +129,20 @@ func (ct *Cointop) setPortfolioHoldings() error { return nil } -func (ct *Cointop) portfolioEntry(c *Coin) (*portfolioEntry, bool) { +func (ct *Cointop) PortfolioEntry(c *Coin) (*PortfolioEntry, bool) { if c == nil { - return &portfolioEntry{}, true + return &PortfolioEntry{}, true } - var p *portfolioEntry + var p *PortfolioEntry var isNew bool var ok bool key := strings.ToLower(c.Name) - if p, ok = ct.portfolio.Entries[key]; !ok { + if p, ok = ct.State.portfolio.Entries[key]; !ok { // NOTE: if not found then try the symbol key := strings.ToLower(c.Symbol) - if p, ok = ct.portfolio.Entries[key]; !ok { - p = &portfolioEntry{ + if p, ok = ct.State.portfolio.Entries[key]; !ok { + p = &PortfolioEntry{ Coin: c.Name, Holdings: 0, } @@ -154,11 +154,11 @@ func (ct *Cointop) portfolioEntry(c *Coin) (*portfolioEntry, bool) { } func (ct *Cointop) setPortfolioEntry(coin string, holdings float64) { - c, _ := ct.allcoinsslugmap[strings.ToLower(coin)] - p, isNew := ct.portfolioEntry(c) + c, _ := ct.State.allCoinsSlugMap[strings.ToLower(coin)] + p, isNew := ct.PortfolioEntry(c) if isNew { key := strings.ToLower(coin) - ct.portfolio.Entries[key] = &portfolioEntry{ + ct.State.portfolio.Entries[key] = &PortfolioEntry{ Coin: coin, Holdings: holdings, } @@ -168,33 +168,33 @@ func (ct *Cointop) setPortfolioEntry(coin string, holdings float64) { } func (ct *Cointop) removePortfolioEntry(coin string) { - delete(ct.portfolio.Entries, strings.ToLower(coin)) + delete(ct.State.portfolio.Entries, strings.ToLower(coin)) } -func (ct *Cointop) portfolioEntryExists(c *Coin) bool { - _, isNew := ct.portfolioEntry(c) +func (ct *Cointop) PortfolioEntryExists(c *Coin) bool { + _, isNew := ct.PortfolioEntry(c) return !isNew } func (ct *Cointop) portfolioEntriesCount() int { - return len(ct.portfolio.Entries) + return len(ct.State.portfolio.Entries) } func (ct *Cointop) getPortfolioSlice() []*Coin { sliced := []*Coin{} - for i := range ct.allcoins { + for i := range ct.State.allCoins { if ct.portfolioEntriesCount() == 0 { break } - coin := ct.allcoins[i] - p, isNew := ct.portfolioEntry(coin) + coin := ct.State.allCoins[i] + p, isNew := ct.PortfolioEntry(coin) if isNew { continue } coin.Holdings = p.Holdings balance := coin.Price * p.Holdings balancestr := fmt.Sprintf("%.2f", balance) - if ct.currencyconversion == "ETH" || ct.currencyconversion == "BTC" { + if ct.State.currencyConversion == "ETH" || ct.State.currencyConversion == "BTC" { balancestr = fmt.Sprintf("%.5f", balance) } balance, _ = strconv.ParseFloat(balancestr, 64) diff --git a/cointop/quit.go b/cointop/quit.go index c5d742d..a940fc4 100644 --- a/cointop/quit.go +++ b/cointop/quit.go @@ -11,15 +11,15 @@ func (ct *Cointop) quit() error { } func (ct *Cointop) quitView() error { - if ct.portfoliovisible { - ct.portfoliovisible = false + if ct.State.portfolioVisible { + ct.State.portfolioVisible = false return ct.updateTable() } - if ct.filterByFavorites { - ct.filterByFavorites = false + if ct.State.filterByFavorites { + ct.State.filterByFavorites = false return ct.updateTable() } - if ct.activeViewName() == ct.tableviewname { + if ct.activeViewName() == ct.Views.Table.Name { return ct.quit() } diff --git a/cointop/refresh.go b/cointop/refresh.go index f7a1616..6aa2283 100644 --- a/cointop/refresh.go +++ b/cointop/refresh.go @@ -8,16 +8,16 @@ import ( func (ct *Cointop) refresh() error { go func() { <-ct.limiter - ct.forcerefresh <- true + ct.forceRefresh <- true }() return nil } func (ct *Cointop) refreshAll() error { - ct.refreshmux.Lock() - defer ct.refreshmux.Unlock() + ct.refreshMux.Lock() + defer ct.refreshMux.Unlock() ct.setRefreshStatus() - ct.cache.Delete("allcoinsslugmap") + ct.cache.Delete("allCoinsSlugMap") ct.cache.Delete("market") go func() { ct.updateCoins() @@ -46,3 +46,16 @@ func (ct *Cointop) loadingTicks(s string, t int) { } } } + +func (ct *Cointop) intervalFetchData() { + go func() { + for { + select { + case <-ct.forceRefresh: + ct.refreshAll() + case <-ct.refreshTicker.C: + ct.refreshAll() + } + } + }() +} diff --git a/cointop/search.go b/cointop/search.go index dbd3364..7b80852 100644 --- a/cointop/search.go +++ b/cointop/search.go @@ -8,27 +8,27 @@ import ( ) func (ct *Cointop) openSearch() error { - ct.searchfieldvisible = true - ct.setActiveView(ct.searchfieldviewname) + ct.State.searchFieldVisible = true + ct.setActiveView(ct.Views.SearchField.Name) return nil } func (ct *Cointop) cancelSearch() error { - ct.searchfieldvisible = false - ct.setActiveView(ct.tableviewname) + ct.State.searchFieldVisible = false + ct.setActiveView(ct.Views.Table.Name) return nil } func (ct *Cointop) doSearch() error { - ct.searchfield.Rewind() + ct.Views.SearchField.Backing.Rewind() b := make([]byte, 100) - n, err := ct.searchfield.Read(b) + n, err := ct.Views.SearchField.Backing.Read(b) // TODO: do this a better way (SoC) - ct.filterByFavorites = false - ct.portfoliovisible = false + ct.State.filterByFavorites = false + ct.State.portfolioVisible = false - defer ct.setActiveView(ct.tableviewname) + defer ct.setActiveView(ct.Views.Table.Name) if err != nil { return nil } @@ -51,8 +51,8 @@ func (ct *Cointop) search(q string) error { min := -1 var hasprefixidx []int var hasprefixdist []int - for i := range ct.allcoins { - coin := ct.allcoins[i] + for i := range ct.State.allCoins { + coin := ct.State.allCoins[i] name := strings.ToLower(coin.Name) symbol := strings.ToLower(coin.Symbol) // if query matches symbol, return immediately diff --git a/cointop/selection.go b/cointop/selection.go new file mode 100644 index 0000000..f70d448 --- /dev/null +++ b/cointop/selection.go @@ -0,0 +1,19 @@ +package cointop + +func (ct *Cointop) selectedCoinName() string { + coin := ct.State.selectedCoin + if coin != nil { + return coin.Name + } + + return "" +} + +func (ct *Cointop) selectedCoinSymbol() string { + coin := ct.State.selectedCoin + if coin != nil { + return coin.Symbol + } + + return "" +} diff --git a/cointop/sort.go b/cointop/sort.go index ceee7a3..c495387 100644 --- a/cointop/sort.go +++ b/cointop/sort.go @@ -9,7 +9,7 @@ import ( var sortlock sync.Mutex -func (ct *Cointop) sort(sortby string, desc bool, list []*Coin, renderHeaders bool) { +func (ct *Cointop) sort(sortBy string, desc bool, list []*Coin, renderHeaders bool) { sortlock.Lock() defer sortlock.Unlock() if list == nil { @@ -18,10 +18,10 @@ func (ct *Cointop) sort(sortby string, desc bool, list []*Coin, renderHeaders bo if len(list) < 2 { return } - ct.sortby = sortby - ct.sortdesc = desc + ct.State.sortBy = sortBy + ct.State.sortDesc = desc sort.Slice(list[:], func(i, j int) bool { - if ct.sortdesc { + if ct.State.sortDesc { i, j = j, i } a := list[i] @@ -32,7 +32,7 @@ func (ct *Cointop) sort(sortby string, desc bool, list []*Coin, renderHeaders bo if b == nil { return false } - switch sortby { + switch sortBy { case "rank": return a.Rank < b.Rank case "name": @@ -71,66 +71,66 @@ func (ct *Cointop) sort(sortby string, desc bool, list []*Coin, renderHeaders bo } } -func (ct *Cointop) sortToggle(sortby string, desc bool) error { - if ct.sortby == sortby { - desc = !ct.sortdesc - } - - ct.sort(sortby, desc, ct.coins, true) - ct.updateTable() - return nil -} - -func (ct *Cointop) sortfn(sortby string, desc bool) func(g *gocui.Gui, v *gocui.View) error { - return func(g *gocui.Gui, v *gocui.View) error { - return ct.sortToggle(sortby, desc) - } -} - -func (ct *Cointop) getSortColIndex() int { - for i, col := range ct.tablecolumnorder { - if ct.sortby == col { - return i - } - } - return 0 -} - func (ct *Cointop) sortAsc() error { - ct.sortdesc = false + ct.State.sortDesc = false ct.updateTable() return nil } func (ct *Cointop) sortDesc() error { - ct.sortdesc = true + ct.State.sortDesc = true ct.updateTable() return nil } func (ct *Cointop) sortPrevCol() error { - nextsortby := ct.tablecolumnorder[0] + nextsortBy := ct.tableColumnOrder[0] i := ct.getSortColIndex() k := i - 1 if k < 0 { k = 0 } - nextsortby = ct.tablecolumnorder[k] - ct.sort(nextsortby, ct.sortdesc, ct.coins, true) + nextsortBy = ct.tableColumnOrder[k] + ct.sort(nextsortBy, ct.State.sortDesc, ct.State.coins, true) ct.updateTable() return nil } func (ct *Cointop) sortNextCol() error { - nextsortby := ct.tablecolumnorder[0] - l := len(ct.tablecolumnorder) + nextsortBy := ct.tableColumnOrder[0] + l := len(ct.tableColumnOrder) i := ct.getSortColIndex() k := i + 1 if k > l-1 { k = l - 1 } - nextsortby = ct.tablecolumnorder[k] - ct.sort(nextsortby, ct.sortdesc, ct.coins, true) + nextsortBy = ct.tableColumnOrder[k] + ct.sort(nextsortBy, ct.State.sortDesc, ct.State.coins, true) ct.updateTable() return nil } + +func (ct *Cointop) sortToggle(sortBy string, desc bool) error { + if ct.State.sortBy == sortBy { + desc = !ct.State.sortDesc + } + + ct.sort(sortBy, desc, ct.State.coins, true) + ct.updateTable() + return nil +} + +func (ct *Cointop) sortfn(sortBy string, desc bool) func(g *gocui.Gui, v *gocui.View) error { + return func(g *gocui.Gui, v *gocui.View) error { + return ct.sortToggle(sortBy, desc) + } +} + +func (ct *Cointop) getSortColIndex() int { + for i, col := range ct.tableColumnOrder { + if ct.State.sortBy == col { + return i + } + } + return 0 +} diff --git a/cointop/statusbar.go b/cointop/statusbar.go index 9011769..cdaf414 100644 --- a/cointop/statusbar.go +++ b/cointop/statusbar.go @@ -8,7 +8,7 @@ import ( ) func (ct *Cointop) updateStatusbar(s string) error { - if ct.statusbarview == nil { + if ct.Views.Statusbar.Backing == nil { return nil } @@ -17,33 +17,33 @@ func (ct *Cointop) updateStatusbar(s string) error { var quitText string var favoritesText string var portfolioText string - if ct.portfoliovisible || ct.filterByFavorites { + if ct.State.portfolioVisible || ct.State.filterByFavorites { quitText = "Return" } else { quitText = "Quit" } - if ct.portfoliovisible { + if ct.State.portfolioVisible { portfolioText = "[E]Edit" } else { portfolioText = "[P]Portfolio" } - if ct.filterByFavorites { + if ct.State.filterByFavorites { favoritesText = "[Space]Unfavorite" } else { favoritesText = "[F]Favorites" } ct.update(func() { - if ct.statusbarview == nil { + if ct.Views.Statusbar.Backing == nil { return } - ct.statusbarview.Clear() + ct.Views.Statusbar.Backing.Clear() base := fmt.Sprintf("%s%s %sHelp %sChart %sRange %sSearch %sConvert %s %s %sSave", "[Q]", quitText, "[?]", "[Enter]", "[[ ]]", "[/]", "[C]", favoritesText, portfolioText, "[CTRL-S]") - str := pad.Right(fmt.Sprintf("%v %sPage %v/%v %s", base, "[← →]", currpage, totalpages, s), ct.maxtablewidth, " ") + str := pad.Right(fmt.Sprintf("%v %sPage %v/%v %s", base, "[← →]", currpage, totalpages, s), ct.maxTableWidth, " ") v := fmt.Sprintf("v%s", ct.version()) str = str[:len(str)-len(v)+2] + v - fmt.Fprintln(ct.statusbarview, str) + fmt.Fprintln(ct.Views.Statusbar.Backing, str) }) return nil diff --git a/cointop/table.go b/cointop/table.go index 173ddb6..9373fd9 100644 --- a/cointop/table.go +++ b/cointop/table.go @@ -18,7 +18,7 @@ func (ct *Cointop) refreshTable() error { ct.table = table.New().SetWidth(maxX) ct.table.HideColumHeaders = true - if ct.portfoliovisible { + if ct.State.portfolioVisible { ct.table.AddCol("") ct.table.AddCol("") ct.table.AddCol("") @@ -31,7 +31,7 @@ func (ct *Cointop) refreshTable() error { total := ct.getPortfolioTotal() - for _, coin := range ct.coins { + for _, coin := range ct.State.coins { unix, _ := strconv.ParseInt(coin.LastUpdated, 10, 64) lastUpdated := time.Unix(unix, 0).Format("15:04:05 Jan 02") colorbalance := ct.colorscheme.TableColumnPrice @@ -80,7 +80,7 @@ func (ct *Cointop) refreshTable() error { ct.table.AddCol("") ct.table.AddCol("") ct.table.AddCol("") - for _, coin := range ct.coins { + for _, coin := range ct.State.coins { if coin == nil { continue } @@ -125,7 +125,7 @@ func (ct *Cointop) refreshTable() error { symbolpadding := 5 // NOTE: this is to adjust padding by 1 because when all name rows are // yellow it messes the spacing (need to debug) - if ct.filterByFavorites { + if ct.State.filterByFavorites { symbolpadding = 6 } ct.table.AddRow( @@ -148,17 +148,17 @@ func (ct *Cointop) refreshTable() error { // highlight last row if current row is out of bounds (can happen when switching views) currentrow := ct.highlightedRowIndex() - if len(ct.coins) > currentrow { + if len(ct.State.coins) > currentrow { ct.highlightRow(currentrow) } ct.update(func() { - if ct.tableview == nil { + if ct.Views.Table.Backing == nil { return } - ct.tableview.Clear() - ct.table.Format().Fprint(ct.tableview) + ct.Views.Table.Backing.Clear() + ct.table.Format().Fprint(ct.Views.Table.Backing) go ct.rowChanged() go ct.updateHeaders() go ct.updateMarketbar() @@ -171,36 +171,36 @@ func (ct *Cointop) refreshTable() error { func (ct *Cointop) updateTable() error { sliced := []*Coin{} - for i := range ct.allcoinsslugmap { - v := ct.allcoinsslugmap[i] - if ct.favorites[v.Name] { + for i := range ct.State.allCoinsSlugMap { + v := ct.State.allCoinsSlugMap[i] + if ct.State.favorites[v.Name] { v.Favorite = true } } - if ct.filterByFavorites { - for i := range ct.allcoins { - coin := ct.allcoins[i] + if ct.State.filterByFavorites { + for i := range ct.State.allCoins { + coin := ct.State.allCoins[i] if coin.Favorite { sliced = append(sliced, coin) } } - ct.coins = sliced + ct.State.coins = sliced go ct.refreshTable() return nil } - if ct.portfoliovisible { + if ct.State.portfolioVisible { sliced = ct.getPortfolioSlice() - ct.coins = sliced + ct.State.coins = sliced go ct.refreshTable() return nil } - start := ct.page * ct.perpage - end := start + ct.perpage - allcoins := ct.allCoins() - size := len(allcoins) + start := ct.State.page * ct.State.perPage + end := start + ct.State.perPage + allCoins := ct.allCoins() + size := len(allCoins) if start < 0 { start = 0 } @@ -221,34 +221,34 @@ func (ct *Cointop) updateTable() error { return nil } if end > 0 { - sliced = allcoins[start:end] + sliced = allCoins[start:end] } - ct.coins = sliced + ct.State.coins = sliced - ct.sort(ct.sortby, ct.sortdesc, ct.coins, true) + ct.sort(ct.State.sortBy, ct.State.sortDesc, ct.State.coins, true) go ct.refreshTable() return nil } func (ct *Cointop) highlightedRowIndex() int { - _, y := ct.tableview.Origin() - _, cy := ct.tableview.Cursor() + _, y := ct.Views.Table.Backing.Origin() + _, cy := ct.Views.Table.Backing.Cursor() idx := y + cy if idx < 0 { idx = 0 } - if idx >= len(ct.coins) { - idx = len(ct.coins) - 1 + if idx >= len(ct.State.coins) { + idx = len(ct.State.coins) - 1 } return idx } func (ct *Cointop) highlightedRowCoin() *Coin { idx := ct.highlightedRowIndex() - if len(ct.coins) == 0 { + if len(ct.State.coins) == 0 { return nil } - return ct.coins[idx] + return ct.State.coins[idx] } func (ct *Cointop) rowLink() string { @@ -282,46 +282,9 @@ func (ct *Cointop) rowLinkShort() string { return "" } -func (ct *Cointop) allCoins() []*Coin { - if ct.filterByFavorites { - var list []*Coin - for i := range ct.allcoins { - coin := ct.allcoins[i] - if coin.Favorite { - list = append(list, coin) - } - } - return list - } - - if ct.portfoliovisible { - var list []*Coin - for i := range ct.allcoins { - coin := ct.allcoins[i] - if ct.portfolioEntryExists(coin) { - list = append(list, coin) - } - } - return list - } - - return ct.allcoins -} - -func (ct *Cointop) coinBySymbol(symbol string) *Coin { - for i := range ct.allcoins { - coin := ct.allcoins[i] - if coin.Symbol == symbol { - return coin - } - } - - return nil -} - func (ct *Cointop) toggleTableFullscreen() error { - ct.onlyTable = !ct.onlyTable - if ct.onlyTable { + ct.State.onlyTable = !ct.State.onlyTable + if ct.State.onlyTable { } else { // NOTE: cached values are initial config settings. // If the only-table config was set then toggle @@ -329,17 +292,17 @@ func (ct *Cointop) toggleTableFullscreen() error { onlyTable, _ := ct.cache.Get("onlyTable") if onlyTable.(bool) { - ct.hideMarketbar = false - ct.hideChart = false - ct.hideStatusbar = false + ct.State.hideMarketbar = false + ct.State.hideChart = false + ct.State.hideStatusbar = false } else { // NOTE: cached values store initial hidden views preferences. hideMarketbar, _ := ct.cache.Get("hideMarketbar") - ct.hideMarketbar = hideMarketbar.(bool) + ct.State.hideMarketbar = hideMarketbar.(bool) hideChart, _ := ct.cache.Get("hideChart") - ct.hideChart = hideChart.(bool) + ct.State.hideChart = hideChart.(bool) hideStatusbar, _ := ct.cache.Get("hideStatusbar") - ct.hideStatusbar = hideStatusbar.(bool) + ct.State.hideStatusbar = hideStatusbar.(bool) } } diff --git a/cointop/headers.go b/cointop/table_header.go similarity index 90% rename from cointop/headers.go rename to cointop/table_header.go index be3a796..ee2a28b 100644 --- a/cointop/headers.go +++ b/cointop/table_header.go @@ -37,9 +37,9 @@ func (ct *Cointop) updateHeaders() { for k := range cm { cm[k].arrow = " " - if ct.sortby == k { + if ct.State.sortBy == k { cm[k].colorfn = ct.colorscheme.TableHeaderColumnActiveSprintf() - if ct.sortdesc { + if ct.State.sortDesc { cm[k].arrow = "▼" } else { cm[k].arrow = "▲" @@ -47,7 +47,7 @@ func (ct *Cointop) updateHeaders() { } } - if ct.portfoliovisible { + if ct.State.portfolioVisible { cols = []string{"rank", "name", "symbol", "price", "holdings", "balance", "24hchange", "percentholdings", "lastupdated"} } else { @@ -78,11 +78,11 @@ func (ct *Cointop) updateHeaders() { } ct.update(func() { - if ct.headersview == nil { + if ct.Views.Header.Backing == nil { return } - ct.headersview.Clear() - fmt.Fprintln(ct.headersview, strings.Join(headers, "")) + ct.Views.Header.Backing.Clear() + fmt.Fprintln(ct.Views.Header.Backing, strings.Join(headers, "")) }) } diff --git a/cointop/types.go b/cointop/types.go deleted file mode 100644 index 4a511a5..0000000 --- a/cointop/types.go +++ /dev/null @@ -1,24 +0,0 @@ -package cointop - -// Coin is the row structure -type Coin struct { - ID string - Name string - Slug string - Symbol string - Rank int - Price float64 - Volume24H float64 - MarketCap float64 - AvailableSupply float64 - TotalSupply float64 - PercentChange1H float64 - PercentChange24H float64 - PercentChange7D float64 - LastUpdated string - // for favorites - Favorite bool - // for portfolio - Holdings float64 - Balance float64 -} diff --git a/cointop/view.go b/cointop/view.go new file mode 100644 index 0000000..7972997 --- /dev/null +++ b/cointop/view.go @@ -0,0 +1,39 @@ +package cointop + +import ( + "fmt" + + "github.com/jroimartin/gocui" +) + +// View is a cointop view +type View struct { + Backing *gocui.View + Name string +} + +func (ct *Cointop) setActiveView(v string) error { + ct.g.SetViewOnTop(v) + ct.g.SetCurrentView(v) + if v == ct.Views.SearchField.Name { + ct.Views.SearchField.Backing.Clear() + ct.Views.SearchField.Backing.SetCursor(1, 0) + fmt.Fprintf(ct.Views.SearchField.Backing, "%s", "/") + } else if v == ct.Views.Table.Name { + ct.g.SetViewOnTop(ct.Views.Statusbar.Name) + } + if v == ct.Views.PortfolioUpdateMenu.Name { + ct.g.SetViewOnTop(ct.Views.Input.Name) + ct.g.SetCurrentView(ct.Views.Input.Name) + } + return nil +} + +func (ct *Cointop) activeViewName() string { + return ct.g.CurrentView().Name() +} + +func (ct *Cointop) setViewOnBottom(v string) error { + _, err := ct.g.SetViewOnBottom(v) + return err +}