You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cointop/cointop/table_header.go

314 lines
7.0 KiB
Go

package cointop
import (
"fmt"
"math"
"strings"
"unicode/utf8"
"github.com/cointop-sh/cointop/pkg/pad"
"github.com/cointop-sh/cointop/pkg/ui"
log "github.com/sirupsen/logrus"
)
// ArrowUp is up arrow unicode character
var ArrowUp = "▲"
// ArrowDown is down arrow unicode character
var ArrowDown = "▼"
// HeaderColumn is header column struct
type HeaderColumn struct {
Slug string
Label string
ShortLabel string // only columns with a ShortLabel can be scaled?
PlainLabel string
}
// HeaderColumns are the header column widths
var HeaderColumns = map[string]*HeaderColumn{
"rank": {
Slug: "rank",
Label: "[r]ank",
PlainLabel: "rank",
},
"name": {
Slug: "name",
Label: "[n]ame",
PlainLabel: "name",
},
"symbol": {
Slug: "symbol",
Label: "[s]ymbol",
PlainLabel: "symbol",
},
"target_price": {
Slug: "target_price",
Label: "[t]target price",
PlainLabel: "target price",
},
"price": {
Slug: "price",
Label: "[p]rice",
PlainLabel: "price",
},
"frequency": {
Slug: "frequency",
Label: "frequency",
PlainLabel: "frequency",
},
"holdings": {
Slug: "holdings",
Label: "[h]oldings",
PlainLabel: "holdings",
},
"balance": {
Slug: "balance",
Label: "[b]alance",
PlainLabel: "balance",
},
"market_cap": {
Slug: "market_cap",
Label: "[m]arket cap",
ShortLabel: "[m]cap",
PlainLabel: "market cap",
},
"24h_volume": {
Slug: "24h_volume",
Label: "24H [v]olume",
ShortLabel: "24[v]",
PlainLabel: "24H volume",
},
"1h_change": {
Slug: "1h_change",
Label: "[1]H%",
PlainLabel: "1H%",
},
"24h_change": {
Slug: "24h_change",
Label: "[2]4H%",
PlainLabel: "24H%",
},
"7d_change": {
Slug: "7d_change",
Label: "[7]D%",
PlainLabel: "7D%",
},
"30d_change": {
Slug: "30d_change",
Label: "[3]0D%",
PlainLabel: "30D%",
},
"1y_change": {
Slug: "1y_change",
Label: "1[y]%",
PlainLabel: "1Y%",
},
"total_supply": {
Slug: "total_supply",
Label: "[t]otal supply",
ShortLabel: "[t]ot",
PlainLabel: "total supply",
},
"available_supply": {
Slug: "available_supply",
Label: "[a]vailable supply",
ShortLabel: "[a]vl",
PlainLabel: "available supply",
},
"percent_holdings": {
Slug: "percent_holdings",
Label: "[%]holdings",
PlainLabel: "%holdings",
},
"last_updated": {
Slug: "last_updated",
Label: "last [u]pdated",
PlainLabel: "last updated",
},
"cost_price": {
Slug: "cost_price",
Label: "cost price",
PlainLabel: "cost price",
},
"cost": {
Slug: "cost",
Label: "[!]cost",
PlainLabel: "cost",
},
"pnl": {
Slug: "pnl",
Label: "[@]PNL",
PlainLabel: "PNL",
},
"pnl_percent": {
Slug: "pnl_percent",
Label: "[#]PNL%",
PlainLabel: "PNL%",
},
}
// GetLabel fetch the label to use for the heading (depends on configuration)
func (ct *Cointop) GetLabel(h *HeaderColumn) string {
// TODO: technically this should support nosort
if ct.IsActiveTableCompactNotation() && h.ShortLabel != "" {
return h.ShortLabel
}
return h.Label
}
// TableHeaderView is structure for table header view
type TableHeaderView = ui.View
// NewTableHeaderView returns a new table header view
func NewTableHeaderView() *TableHeaderView {
return ui.NewView("table_header")
}
// GetActiveTableHeaders returns the list of active table headers
func (ct *Cointop) GetActiveTableHeaders() []string {
var cols []string
switch ct.State.selectedView {
case PortfolioView:
cols = ct.GetPortfolioTableHeaders()
case PriceAlertsView:
cols = ct.GetPriceAlertsTableHeaders()
default:
cols = ct.GetCoinsTableHeaders()
}
return cols
}
// GetActiveTableHeaders returns the list of active table headers
func (ct *Cointop) IsActiveTableCompactNotation() bool {
var compact bool
switch ct.State.selectedView {
case PortfolioView:
compact = ct.State.portfolioCompactNotation
case CoinsView:
compact = ct.State.tableCompactNotation
case FavoritesView:
compact = ct.State.favoritesCompactNotation
default:
compact = ct.State.tableCompactNotation
}
return compact
}
// UpdateTableHeader renders the table header
func (ct *Cointop) UpdateTableHeader() error {
log.Debug("UpdateTableHeader()")
baseColor := ct.colorscheme.TableHeaderSprintf()
noSort := ct.IsPriceAlertsVisible()
cols := ct.GetActiveTableHeaders()
var headers []string
for i, col := range cols {
hc, ok := HeaderColumns[col]
if !ok {
continue
}
width := ct.GetTableColumnWidth(col)
if width == 0 {
continue
}
arrow := " "
colorfn := baseColor
if !noSort {
if ct.State.sortBy == col {
colorfn = ct.colorscheme.TableHeaderColumnActiveSprintf()
if ct.State.sortDesc {
arrow = ArrowDown
} else {
arrow = ArrowUp
}
}
}
label := ct.GetLabel(hc)
if noSort {
label = hc.PlainLabel
}
leftAlign := ct.GetTableColumnAlignLeft(col)
switch col {
case "price", "balance", "pnl", "cost":
label = fmt.Sprintf("%s%s", ct.CurrencySymbol(), label)
}
if leftAlign {
label = label + arrow
} else {
label = arrow + label
}
padfn := pad.Left
padLeft := 1
if !noSort && i == 0 {
padLeft = 0
}
if leftAlign {
padfn = pad.Right
}
colStr := fmt.Sprintf(
"%s%s%s",
strings.Repeat(" ", padLeft),
colorfn(padfn(label, width+(1-padLeft), " ")),
strings.Repeat(" ", 1),
)
headers = append(headers, colStr)
}
ct.UpdateUI(func() error {
return ct.Views.TableHeader.Update(strings.Join(headers, ""))
})
return nil
}
// SetTableColumnAlignLeft sets the column alignment direction for header
func (ct *Cointop) SetTableColumnAlignLeft(header string, alignLeft bool) {
ct.State.tableColumnAlignLeft.Store(header, alignLeft)
}
// GetTableColumnAlignLeft gets the column alignment direction for header
func (ct *Cointop) GetTableColumnAlignLeft(header string) bool {
ifc, ok := ct.State.tableColumnAlignLeft.Load(header)
if ok {
return ifc.(bool)
}
return false
}
// SetTableColumnWidth sets the column width for header
func (ct *Cointop) SetTableColumnWidth(header string, width int) {
prevIfc, ok := ct.State.tableColumnWidths.Load(header)
var prev int
if ok {
prev = prevIfc.(int)
} else {
hc := HeaderColumns[header]
if hc == nil {
log.Warnf("SetTableColumnWidth(%s) not found", header)
}
prev = utf8.RuneCountInString(ct.GetLabel(hc)) + 1
switch header {
case "price", "balance":
prev++
}
}
ct.State.tableColumnWidths.Store(header, int(math.Max(float64(width), float64(prev))))
}
// SetTableColumnWidthFromString sets the column width for header given size of string
func (ct *Cointop) SetTableColumnWidthFromString(header string, text string) {
ct.SetTableColumnWidth(header, utf8.RuneCountInString(text))
}
// GetTableColumnWidth gets the column width for header
func (ct *Cointop) GetTableColumnWidth(header string) int {
ifc, ok := ct.State.tableColumnWidths.Load(header)
if ok {
return ifc.(int)
}
return 0
}