From 1bebb88d0ecc1949da6645f05a7c33e6265b1e88 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Tue, 28 May 2024 18:01:26 +0100 Subject: [PATCH] Rename Stocks to Markets Also fix bug that would remove markets if a network request failed and not show them again until Glance was restarted --- docs/configuration.md | 24 +++++----- ...preview.png => markets-widget-preview.png} | Bin internal/assets/static/main.css | 6 +-- internal/assets/templates.go | 2 +- .../templates/{stocks.html => markets.html} | 14 +++--- internal/feed/primitives.go | 18 +++++--- internal/feed/yahoo.go | 41 ++++++++---------- internal/widget/stocks.go | 33 ++++++++------ internal/widget/widget.go | 4 +- 9 files changed, 74 insertions(+), 68 deletions(-) rename docs/images/{stocks-widget-preview.png => markets-widget-preview.png} (100%) rename internal/assets/templates/{stocks.html => markets.html} (78%) diff --git a/docs/configuration.md b/docs/configuration.md index 90d9d7d..f57c0ad 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -20,7 +20,7 @@ - [Bookmarks](#bookmarks) - [Calendar](#calendar) - [Clock](#clock) - - [Stocks](#stocks) + - [Markets](#markets) - [Twitch Channels](#twitch-channels) - [Twitch Top Games](#twitch-top-games) - [iframe](#iframe) @@ -80,8 +80,8 @@ pages: - type: weather location: London, United Kingdom - - type: stocks - stocks: + - type: markets + markets: - symbol: SPY name: S&P 500 - symbol: BTC-USD @@ -1146,14 +1146,14 @@ Preview: > > There is currently no customizability available for the calendar. Extra features will be added in the future. -### Stocks -Display a list of stocks, their current value, change for the day and a small 21d chart. Data is taken from Yahoo Finance. +### Markets +Display a list of markets, their current value, change for the day and a small 21d chart. Data is taken from Yahoo Finance. Example: ```yaml -- type: stocks - stocks: +- type: markets + markets: - symbol: SPY name: S&P 500 - symbol: BTC-USD @@ -1168,21 +1168,21 @@ Example: Preview: -![](images/stocks-widget-preview.png) +![](images/markets-widget-preview.png) #### Properties | Name | Type | Required | | ---- | ---- | -------- | -| stocks | array | yes | +| markets | array | yes | | sort-by | string | no | | style | string | no | -##### `stocks` -An array of stocks for which to display information about. +##### `markets` +An array of markets for which to display information about. ##### `sort-by` -By default the stocks are displayed in the order they were defined. You can customize their ordering by setting the `sort-by` property to `absolute-change` for descending order based on the stock's absolute price change. +By default the markets are displayed in the order they were defined. You can customize their ordering by setting the `sort-by` property to `absolute-change` for descending order based on the stock's absolute price change. ##### `style` To make the widget scale appropriately in a `full` size column, set the style to the experimental `dynamic-columns-experimental` option. diff --git a/docs/images/stocks-widget-preview.png b/docs/images/markets-widget-preview.png similarity index 100% rename from docs/images/stocks-widget-preview.png rename to docs/images/markets-widget-preview.png diff --git a/internal/assets/static/main.css b/internal/assets/static/main.css index 4546d8e..d975c8b 100644 --- a/internal/assets/static/main.css +++ b/internal/assets/static/main.css @@ -622,16 +622,16 @@ kbd:active { color: var(--color-text-highlight); } -.stock-chart { +.market-chart { margin-left: auto; width: 6.5rem; } -.stock-chart svg { +.market-chart svg { width: 100%; } -.stock-values { +.market-values { min-width: 8rem; } diff --git a/internal/assets/templates.go b/internal/assets/templates.go index 6ff1c89..cc91b49 100644 --- a/internal/assets/templates.go +++ b/internal/assets/templates.go @@ -26,7 +26,7 @@ var ( ChangeDetectionTemplate = compileTemplate("change-detection.html", "widget-base.html") VideosTemplate = compileTemplate("videos.html", "widget-base.html", "video-card-contents.html") VideosGridTemplate = compileTemplate("videos-grid.html", "widget-base.html", "video-card-contents.html") - StocksTemplate = compileTemplate("stocks.html", "widget-base.html") + MarketsTemplate = compileTemplate("markets.html", "widget-base.html") RSSListTemplate = compileTemplate("rss-list.html", "widget-base.html") RSSDetailedListTemplate = compileTemplate("rss-detailed-list.html", "widget-base.html") RSSHorizontalCardsTemplate = compileTemplate("rss-horizontal-cards.html", "widget-base.html") diff --git a/internal/assets/templates/stocks.html b/internal/assets/templates/markets.html similarity index 78% rename from internal/assets/templates/stocks.html rename to internal/assets/templates/markets.html index eb348c0..9d8b33e 100644 --- a/internal/assets/templates/stocks.html +++ b/internal/assets/templates/markets.html @@ -3,31 +3,31 @@ {{ define "widget-content" }} {{ if ne .Style "dynamic-columns-experimental" }} {{ else }}
- {{ range .Stocks }} + {{ range .Markets }}
- {{ template "stock" . }} + {{ template "market" . }}
{{ end }}
{{ end }} {{ end }} -{{ define "stock" }} +{{ define "market" }}
{{ .Symbol }}
{{ .Name }}
- - + + diff --git a/internal/feed/primitives.go b/internal/feed/primitives.go index 33c3247..6e0c98f 100644 --- a/internal/feed/primitives.go +++ b/internal/feed/primitives.go @@ -85,20 +85,24 @@ var currencyToSymbol = map[string]string{ "PHP": "₱", } -type Stock struct { - Name string `yaml:"name"` - Symbol string `yaml:"symbol"` - ChartLink string `yaml:"chart-link"` - SymbolLink string `yaml:"symbol-link"` +type MarketRequest struct { + Name string `yaml:"name"` + Symbol string `yaml:"symbol"` + ChartLink string `yaml:"chart-link"` + SymbolLink string `yaml:"symbol-link"` +} + +type Market struct { + MarketRequest Currency string `yaml:"-"` Price float64 `yaml:"-"` PercentChange float64 `yaml:"-"` SvgChartPoints string `yaml:"-"` } -type Stocks []Stock +type Markets []Market -func (t Stocks) SortByAbsChange() { +func (t Markets) SortByAbsChange() { sort.Slice(t, func(i, j int) bool { return math.Abs(t[i].PercentChange) > math.Abs(t[j].PercentChange) }) diff --git a/internal/feed/yahoo.go b/internal/feed/yahoo.go index 69c2731..f962695 100644 --- a/internal/feed/yahoo.go +++ b/internal/feed/yahoo.go @@ -6,7 +6,7 @@ import ( "net/http" ) -type stockResponseJson struct { +type marketResponseJson struct { Chart struct { Result []struct { Meta struct { @@ -25,30 +25,30 @@ type stockResponseJson struct { } // TODO: allow changing chart time frame -const stockChartDays = 21 +const marketChartDays = 21 -func FetchStocksDataFromYahoo(stockRequests Stocks) (Stocks, error) { - requests := make([]*http.Request, 0, len(stockRequests)) +func FetchMarketsDataFromYahoo(marketRequests []MarketRequest) (Markets, error) { + requests := make([]*http.Request, 0, len(marketRequests)) - for i := range stockRequests { - request, _ := http.NewRequest("GET", fmt.Sprintf("https://query1.finance.yahoo.com/v8/finance/chart/%s?range=1mo&interval=1d", stockRequests[i].Symbol), nil) + for i := range marketRequests { + request, _ := http.NewRequest("GET", fmt.Sprintf("https://query1.finance.yahoo.com/v8/finance/chart/%s?range=1mo&interval=1d", marketRequests[i].Symbol), nil) requests = append(requests, request) } - job := newJob(decodeJsonFromRequestTask[stockResponseJson](defaultClient), requests) + job := newJob(decodeJsonFromRequestTask[marketResponseJson](defaultClient), requests) responses, errs, err := workerPoolDo(job) if err != nil { return nil, fmt.Errorf("%w: %v", ErrNoContent, err) } - stocks := make(Stocks, 0, len(responses)) + markets := make(Markets, 0, len(responses)) var failed int for i := range responses { if errs[i] != nil { failed++ - slog.Error("Failed to fetch stock data", "symbol", stockRequests[i].Symbol, "error", errs[i]) + slog.Error("Failed to fetch market data", "symbol", marketRequests[i].Symbol, "error", errs[i]) continue } @@ -56,14 +56,14 @@ func FetchStocksDataFromYahoo(stockRequests Stocks) (Stocks, error) { if len(response.Chart.Result) == 0 { failed++ - slog.Error("Stock response contains no data", "symbol", stockRequests[i].Symbol) + slog.Error("Market response contains no data", "symbol", marketRequests[i].Symbol) continue } prices := response.Chart.Result[0].Indicators.Quote[0].Close - if len(prices) > stockChartDays { - prices = prices[len(prices)-stockChartDays:] + if len(prices) > marketChartDays { + prices = prices[len(prices)-marketChartDays:] } previous := response.Chart.Result[0].Meta.RegularMarketPrice @@ -80,13 +80,10 @@ func FetchStocksDataFromYahoo(stockRequests Stocks) (Stocks, error) { currency = response.Chart.Result[0].Meta.Currency } - stocks = append(stocks, Stock{ - Name: stockRequests[i].Name, - Symbol: response.Chart.Result[0].Meta.Symbol, - SymbolLink: stockRequests[i].SymbolLink, - ChartLink: stockRequests[i].ChartLink, - Price: response.Chart.Result[0].Meta.RegularMarketPrice, - Currency: currency, + markets = append(markets, Market{ + MarketRequest: marketRequests[i], + Price: response.Chart.Result[0].Meta.RegularMarketPrice, + Currency: currency, PercentChange: percentChange( response.Chart.Result[0].Meta.RegularMarketPrice, previous, @@ -95,13 +92,13 @@ func FetchStocksDataFromYahoo(stockRequests Stocks) (Stocks, error) { }) } - if len(stocks) == 0 { + if len(markets) == 0 { return nil, ErrNoContent } if failed > 0 { - return stocks, fmt.Errorf("%w: could not fetch data for %d stock(s)", ErrPartialContent, failed) + return markets, fmt.Errorf("%w: could not fetch data for %d market(s)", ErrPartialContent, failed) } - return stocks, nil + return markets, nil } diff --git a/internal/widget/stocks.go b/internal/widget/stocks.go index 27f292b..8a10898 100644 --- a/internal/widget/stocks.go +++ b/internal/widget/stocks.go @@ -9,34 +9,39 @@ import ( "github.com/glanceapp/glance/internal/feed" ) -// TODO: rename to Markets at some point -type Stocks struct { - widgetBase `yaml:",inline"` - Stocks feed.Stocks `yaml:"stocks"` - Sort string `yaml:"sort-by"` - Style string `yaml:"style"` +type Markets struct { + widgetBase `yaml:",inline"` + StocksRequests []feed.MarketRequest `yaml:"stocks"` + MarketRequests []feed.MarketRequest `yaml:"markets"` + Sort string `yaml:"sort-by"` + Style string `yaml:"style"` + Markets feed.Markets `yaml:"-"` } -func (widget *Stocks) Initialize() error { - widget.withTitle("Stocks").withCacheDuration(time.Hour) +func (widget *Markets) Initialize() error { + widget.withTitle("Markets").withCacheDuration(time.Hour) + + if len(widget.MarketRequests) == 0 { + widget.MarketRequests = widget.StocksRequests + } return nil } -func (widget *Stocks) Update(ctx context.Context) { - stocks, err := feed.FetchStocksDataFromYahoo(widget.Stocks) +func (widget *Markets) Update(ctx context.Context) { + markets, err := feed.FetchMarketsDataFromYahoo(widget.MarketRequests) if !widget.canContinueUpdateAfterHandlingErr(err) { return } if widget.Sort == "absolute-change" { - stocks.SortByAbsChange() + markets.SortByAbsChange() } - widget.Stocks = stocks + widget.Markets = markets } -func (widget *Stocks) Render() template.HTML { - return widget.render(widget, assets.StocksTemplate) +func (widget *Markets) Render() template.HTML { + return widget.render(widget, assets.MarketsTemplate) } diff --git a/internal/widget/widget.go b/internal/widget/widget.go index 5465b63..bee61ec 100644 --- a/internal/widget/widget.go +++ b/internal/widget/widget.go @@ -33,8 +33,8 @@ func New(widgetType string) (Widget, error) { return &Releases{}, nil case "videos": return &Videos{}, nil - case "stocks": - return &Stocks{}, nil + case "markets", "stocks": + return &Markets{}, nil case "reddit": return &Reddit{}, nil case "rss":