Skip to content

Commit

Permalink
feat: added custom color schemes
Browse files Browse the repository at this point in the history
  • Loading branch information
achannarasappa committed Mar 7, 2021
1 parent ce7f885 commit c774dd9
Show file tree
Hide file tree
Showing 11 changed files with 342 additions and 168 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,31 @@ It's possible to set a custom sort order with the `--sort` flag or `sort:` confi
* If a `currency` is not set (default behavior) and the `show-summary` option is enabled, the summary will be calculated in USD regardless of the exchange currency to avoid mixing currencies
* Currencies are retrieved only once at start time - currency exchange rates do fluctuate over time and thus converted values may vary depending on when ticker is started

### Custom Color Schemes

`ticker` supports setting custom color schemes from the config file. Colors are represented by a [hex triplet](https://en.wikipedia.org/wiki/Web_colors#Hex_triplet). Below is an annotated example config block from `.ticker.yaml` where custom colors are set:

```yaml
# ~/.ticker.yaml
watchlist:
- NET
- TEAM
- ESTC
- BTC-USD
colors:
text: "#005fff"
text-light: "#0087ff"
text-label: "#00d7ff"
text-line: "#00ffff"
text-tag: "#005fff"
background-tag: "#0087ff"
```

* Terminals supporting TrueColor will be able to represent the full color space and in other cases colors will be down sampled
* Any omitted or invalid colors will revert to default color scheme values



## Notes

* **Real-time quotes** - Quotes are pulled from Yahoo finance which may provide delayed stock quotes depending on the exchange. The major US exchanges (NYSE, NASDAQ) have real-time quotes however other exchanges may not. Consult the [help article](https://help.yahoo.com/kb/SLN2310.html) on exchange delays to determine which exchanges you can expect delays for or use the `--show-tags` flag to include timeliness of data alongside quotes in `ticker`.
Expand Down
3 changes: 3 additions & 0 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
. "github.com/achannarasappa/ticker/internal/common"
"github.com/achannarasappa/ticker/internal/currency"
"github.com/achannarasappa/ticker/internal/position"
"github.com/achannarasappa/ticker/internal/ui/util"

"github.com/adrg/xdg"
"github.com/go-resty/resty/v2"
Expand Down Expand Up @@ -112,9 +113,11 @@ func getReference(config Config, client resty.Client) (Reference, error) {
symbols := position.GetSymbols(config.Watchlist, aggregatedLots)

currencyRates, err := currency.GetCurrencyRates(client, symbols, config.Currency)
styles := util.GetColorScheme(config.ColorScheme)

return Reference{
CurrencyRates: currencyRates,
Styles: styles,
}, err

}
Expand Down
45 changes: 34 additions & 11 deletions internal/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,32 @@ type Context struct {
}

type Config struct {
RefreshInterval int `yaml:"interval"`
Watchlist []string `yaml:"watchlist"`
Lots []Lot `yaml:"lots"`
Separate bool `yaml:"show-separator"`
ExtraInfoExchange bool `yaml:"show-tags"`
ExtraInfoFundamentals bool `yaml:"show-fundamentals"`
ShowSummary bool `yaml:"show-summary"`
ShowHoldings bool `yaml:"show-holdings"`
Proxy string `yaml:"proxy"`
Sort string `yaml:"sort"`
Currency string `yaml:"currency"`
RefreshInterval int `yaml:"interval"`
Watchlist []string `yaml:"watchlist"`
Lots []Lot `yaml:"lots"`
Separate bool `yaml:"show-separator"`
ExtraInfoExchange bool `yaml:"show-tags"`
ExtraInfoFundamentals bool `yaml:"show-fundamentals"`
ShowSummary bool `yaml:"show-summary"`
ShowHoldings bool `yaml:"show-holdings"`
Proxy string `yaml:"proxy"`
Sort string `yaml:"sort"`
Currency string `yaml:"currency"`
ColorScheme ConfigColorScheme `yaml:"colors"`
}

type ConfigColorScheme struct {
Text string `yaml:"text"`
TextLight string `yaml:"text-light"`
TextLabel string `yaml:"text-label"`
TextLine string `yaml:"text-line"`
TextTag string `yaml:"text-tag"`
BackgroundTag string `yaml:"background-tag"`
}

type Reference struct {
CurrencyRates CurrencyRates
Styles Styles
}

type Dependencies struct {
Expand All @@ -46,3 +57,15 @@ type CurrencyRate struct {
ToCurrency string
Rate float64
}

type Styles struct {
Text StyleFn
TextLight StyleFn
TextLabel StyleFn
TextBold StyleFn
TextLine StyleFn
TextPrice func(float64, string) string
Tag StyleFn
}

type StyleFn func(string) string
33 changes: 19 additions & 14 deletions internal/ui/component/summary/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,25 @@ import (
"strings"

grid "github.com/achannarasappa/term-grid"
c "github.com/achannarasappa/ticker/internal/common"
"github.com/achannarasappa/ticker/internal/position"

. "github.com/achannarasappa/ticker/internal/ui/util"
"github.com/muesli/reflow/ansi"
)

type Model struct {
Width int
Summary position.PositionSummary
Context c.Context
styles c.Styles
}

// NewModel returns a model with default values.
func NewModel() Model {
func NewModel(ctx c.Context) Model {
return Model{
Width: 80,
Width: 80,
styles: ctx.Reference.Styles,
}
}

Expand All @@ -27,15 +32,15 @@ func (m Model) View() string {
return ""
}

textChange := StyleNeutralFaded("Day Change: ") + quoteChangeText(m.Summary.DayChange, m.Summary.DayChangePercent) +
StyleNeutralFaded(" • ") +
StyleNeutralFaded("Change: ") + quoteChangeText(m.Summary.Change, m.Summary.ChangePercent)
textChange := m.styles.TextLabel("Day Change: ") + quoteChangeText(m.Summary.DayChange, m.Summary.DayChangePercent, m.styles) +
m.styles.TextLabel(" • ") +
m.styles.TextLabel("Change: ") + quoteChangeText(m.Summary.Change, m.Summary.ChangePercent, m.styles)
widthChange := ansi.PrintableRuneWidth(textChange)
textValue := StyleNeutralFaded(" • ") +
StyleNeutralFaded("Value: ") + ValueText(m.Summary.Value)
textValue := m.styles.TextLabel(" • ") +
m.styles.TextLabel("Value: ") + ValueText(m.Summary.Value, m.styles)
widthValue := ansi.PrintableRuneWidth(textValue)
textCost := StyleNeutralFaded(" • ") +
StyleNeutralFaded("Cost: ") + ValueText(m.Summary.Cost)
textCost := m.styles.TextLabel(" • ") +
m.styles.TextLabel("Cost: ") + ValueText(m.Summary.Cost, m.styles)
widthCost := ansi.PrintableRuneWidth(textValue)

return grid.Render(grid.Grid{
Expand All @@ -62,7 +67,7 @@ func (m Model) View() string {
{
Width: m.Width,
Cells: []grid.Cell{
{Text: StyleLine(strings.Repeat("━", m.Width))},
{Text: m.styles.TextLine(strings.Repeat("━", m.Width))},
},
},
},
Expand All @@ -71,14 +76,14 @@ func (m Model) View() string {

}

func quoteChangeText(change float64, changePercent float64) string {
func quoteChangeText(change float64, changePercent float64, styles c.Styles) string {
if change == 0.0 {
return StyleNeutralFaded(ConvertFloatToString(change, false) + " (" + ConvertFloatToString(changePercent, false) + "%)")
return styles.TextLabel(ConvertFloatToString(change, false) + " (" + ConvertFloatToString(changePercent, false) + "%)")
}

if change > 0.0 {
return StylePrice(changePercent, "↑ "+ConvertFloatToString(change, false)+" ("+ConvertFloatToString(changePercent, false)+"%)")
return styles.TextPrice(changePercent, "↑ "+ConvertFloatToString(change, false)+" ("+ConvertFloatToString(changePercent, false)+"%)")
}

return StylePrice(changePercent, "↓ "+ConvertFloatToString(change, false)+" ("+ConvertFloatToString(changePercent, false)+"%)")
return styles.TextPrice(changePercent, "↓ "+ConvertFloatToString(change, false)+" ("+ConvertFloatToString(changePercent, false)+"%)")
}
19 changes: 15 additions & 4 deletions internal/ui/component/summary/summary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package summary_test
import (
"strings"

c "github.com/achannarasappa/ticker/internal/common"
"github.com/achannarasappa/ticker/internal/position"
. "github.com/achannarasappa/ticker/internal/ui/component/summary"

Expand All @@ -17,9 +18,19 @@ func removeFormatting(text string) string {

var _ = Describe("Summary", func() {

ctxFixture := c.Context{Reference: c.Reference{Styles: c.Styles{
Text: func(v string) string { return v },
TextLight: func(v string) string { return v },
TextLabel: func(v string) string { return v },
TextBold: func(v string) string { return v },
TextLine: func(v string) string { return v },
TextPrice: func(percent float64, text string) string { return text },
Tag: func(v string) string { return v },
}}}

When("the change is positive", func() {
It("should render a summary with up arrow", func() {
m := NewModel()
m := NewModel(ctxFixture)
m.Width = 120
m.Summary = position.PositionSummary{
Value: 10000,
Expand All @@ -38,7 +49,7 @@ var _ = Describe("Summary", func() {

When("the change is negative", func() {
It("should render a summary with down arrow", func() {
m := NewModel()
m := NewModel(ctxFixture)
m.Width = 120
m.Summary = position.PositionSummary{
Value: 1000,
Expand All @@ -57,7 +68,7 @@ var _ = Describe("Summary", func() {

When("no quotes are set", func() {
It("should render an empty summary", func() {
m := NewModel()
m := NewModel(ctxFixture)
Expect(removeFormatting(m.View())).To(Equal(strings.Join([]string{
"Day Change: 0.00 (0.00%) • Change: 0.00 (0.00%) • Value: • Cost: ",
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
Expand All @@ -67,7 +78,7 @@ var _ = Describe("Summary", func() {

When("the window width is less than the minimum", func() {
It("should render an empty summary", func() {
m := NewModel()
m := NewModel(ctxFixture)
m.Width = 10
Expect(m.View()).To(Equal(""))
})
Expand Down
Loading

0 comments on commit c774dd9

Please sign in to comment.