From 344f5189916b0a96c322f06b869d4cefff43226e Mon Sep 17 00:00:00 2001 From: Jonas Knobloch Date: Sun, 12 May 2024 13:20:34 +0200 Subject: [PATCH 01/13] Add Lobsters widget --- internal/feed/lobsters.go | 138 ++++++++++++++++++++++++++++++++++++ internal/widget/lobsters.go | 55 ++++++++++++++ internal/widget/widget.go | 2 + 3 files changed, 195 insertions(+) create mode 100644 internal/feed/lobsters.go create mode 100644 internal/widget/lobsters.go diff --git a/internal/feed/lobsters.go b/internal/feed/lobsters.go new file mode 100644 index 0000000..b503492 --- /dev/null +++ b/internal/feed/lobsters.go @@ -0,0 +1,138 @@ +package feed + +import ( + "context" + "fmt" + "github.com/mmcdole/gofeed" + "log/slog" + "net/http" + "strings" + "time" +) + +type lobstersPostResponseJson struct { + ShortID string `json:"short_id"` + ShortIDURL string `json:"short_id_url"` + CreatedAt string `json:"created_at"` + Title string `json:"title"` + URL string `json:"url"` + Score int `json:"score"` + Flags int `json:"flags"` + CommentCount int `json:"comment_count"` + Description string `json:"description"` + DescriptionPlain string `json:"description_plain"` + CommentsURL string `json:"comments_url"` + SubmitterUser string `json:"submitter_user"` + UserIsAuthor bool `json:"user_is_author"` + Tags []string `json:"tags"` + Comments []struct { + ShortID string `json:"short_id"` + ShortIDURL string `json:"short_id_url"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + IsDeleted bool `json:"is_deleted"` + IsModerated bool `json:"is_moderated"` + Score int `json:"score"` + Flags int `json:"flags"` + ParentComment any `json:"parent_comment"` + Comment string `json:"comment"` + CommentPlain string `json:"comment_plain"` + URL string `json:"url"` + Depth int `json:"depth"` + CommentingUser string `json:"commenting_user"` + } `json:"comments"` +} + +var lobstersParser = gofeed.NewParser() + +func getLobstersTopPostIds(feed string) ([]string, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + request := &RSSFeedRequest{ + Url: feed, + Title: "Lobsters", + } + + f, err := lobstersParser.ParseURLWithContext(request.Url, ctx) + + if err != nil { + return nil, fmt.Errorf("%w: could not fetch posts from %s", ErrNoContent, feed) + } + + postIds := make([]string, 0, len(f.Items)) + + for i := range f.Items { + postIds = append(postIds, f.Items[i].GUID) + } + + return postIds, nil +} + +func getLobstersPostsFromIds(postIds []string) (ForumPosts, error) { + requests := make([]*http.Request, len(postIds)) + + for i, id := range postIds { + request, _ := http.NewRequest("GET", id+".json", nil) + requests[i] = request + } + + task := decodeJsonFromRequestTask[lobstersPostResponseJson](defaultClient) + job := newJob(task, requests).withWorkers(30) + results, errs, err := workerPoolDo(job) + + if err != nil { + return nil, err + } + + posts := make(ForumPosts, 0, len(postIds)) + + for i := range results { + if errs[i] != nil { + slog.Error("Failed to fetch or parse lobsters post", "error", errs[i], "url", requests[i].URL) + continue + } + + tags := strings.Join(results[i].Tags, ",") + + if tags != "" { + tags = " [" + tags + "]" + } + + createdAt, _ := time.Parse(time.RFC3339, results[i].CreatedAt) + + posts = append(posts, ForumPost{ + Title: results[i].Title + tags, + DiscussionUrl: results[i].CommentsURL, + TargetUrl: results[i].URL, + TargetUrlDomain: extractDomainFromUrl(results[i].URL), + CommentCount: results[i].CommentCount, + Score: results[i].Score, + TimePosted: createdAt, + }) + } + + if len(posts) == 0 { + return nil, ErrNoContent + } + + if len(posts) != len(postIds) { + return posts, fmt.Errorf("%w could not fetch some lobsters posts", ErrPartialContent) + } + + return posts, nil +} + +func FetchLobstersTopPosts(feed string, limit int) (ForumPosts, error) { + postIds, err := getLobstersTopPostIds(feed) + + if err != nil { + return nil, err + } + + if len(postIds) > limit { + postIds = postIds[:limit] + } + + return getLobstersPostsFromIds(postIds) +} diff --git a/internal/widget/lobsters.go b/internal/widget/lobsters.go new file mode 100644 index 0000000..1e91613 --- /dev/null +++ b/internal/widget/lobsters.go @@ -0,0 +1,55 @@ +package widget + +import ( + "context" + "html/template" + "time" + + "github.com/glanceapp/glance/internal/assets" + "github.com/glanceapp/glance/internal/feed" +) + +type Lobsters struct { + widgetBase `yaml:",inline"` + Feed string `yaml:"feed"` + Posts feed.ForumPosts `yaml:"-"` + Limit int `yaml:"limit"` + CollapseAfter int `yaml:"collapse-after"` + ShowThumbnails bool `yaml:"-"` +} + +func (widget *Lobsters) Initialize() error { + widget.withTitle("Lobsters").withCacheDuration(30 * time.Minute) + + if widget.Feed == "" { + widget.Feed = "https://lobste.rs/rss" + } + + if widget.Limit <= 0 { + widget.Limit = 15 + } + + if widget.CollapseAfter == 0 || widget.CollapseAfter < -1 { + widget.CollapseAfter = 5 + } + + return nil +} + +func (widget *Lobsters) Update(ctx context.Context) { + posts, err := feed.FetchLobstersTopPosts(widget.Feed, widget.Limit) + + if !widget.canContinueUpdateAfterHandlingErr(err) { + return + } + + if widget.Limit < len(posts) { + posts = posts[:widget.Limit] + } + + widget.Posts = posts +} + +func (widget *Lobsters) Render() template.HTML { + return widget.render(widget, assets.ForumPostsTemplate) +} diff --git a/internal/widget/widget.go b/internal/widget/widget.go index 367d822..debda3c 100644 --- a/internal/widget/widget.go +++ b/internal/widget/widget.go @@ -43,6 +43,8 @@ func New(widgetType string) (Widget, error) { return &TwitchGames{}, nil case "twitch-channels": return &TwitchChannels{}, nil + case "lobsters": + return &Lobsters{}, nil default: return nil, fmt.Errorf("unknown widget type: %s", widgetType) } From c05607934b5bbe46604210c8b4a37bfdb8d3c9bb Mon Sep 17 00:00:00 2001 From: Jonas Knobloch Date: Sun, 12 May 2024 17:14:04 +0200 Subject: [PATCH 02/13] Fetch posts via JSON endpoint --- internal/feed/lobsters.go | 102 ++++++++---------------------------- internal/widget/lobsters.go | 8 +-- 2 files changed, 25 insertions(+), 85 deletions(-) diff --git a/internal/feed/lobsters.go b/internal/feed/lobsters.go index b503492..d18b607 100644 --- a/internal/feed/lobsters.go +++ b/internal/feed/lobsters.go @@ -1,10 +1,6 @@ package feed import ( - "context" - "fmt" - "github.com/mmcdole/gofeed" - "log/slog" "net/http" "strings" "time" @@ -25,89 +21,41 @@ type lobstersPostResponseJson struct { SubmitterUser string `json:"submitter_user"` UserIsAuthor bool `json:"user_is_author"` Tags []string `json:"tags"` - Comments []struct { - ShortID string `json:"short_id"` - ShortIDURL string `json:"short_id_url"` - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` - IsDeleted bool `json:"is_deleted"` - IsModerated bool `json:"is_moderated"` - Score int `json:"score"` - Flags int `json:"flags"` - ParentComment any `json:"parent_comment"` - Comment string `json:"comment"` - CommentPlain string `json:"comment_plain"` - URL string `json:"url"` - Depth int `json:"depth"` - CommentingUser string `json:"commenting_user"` - } `json:"comments"` } -var lobstersParser = gofeed.NewParser() +type lobstersFeedResponseJson []lobstersPostResponseJson -func getLobstersTopPostIds(feed string) ([]string, error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - - request := &RSSFeedRequest{ - Url: feed, - Title: "Lobsters", - } - - f, err := lobstersParser.ParseURLWithContext(request.Url, ctx) - - if err != nil { - return nil, fmt.Errorf("%w: could not fetch posts from %s", ErrNoContent, feed) - } - - postIds := make([]string, 0, len(f.Items)) - - for i := range f.Items { - postIds = append(postIds, f.Items[i].GUID) - } - - return postIds, nil -} - -func getLobstersPostsFromIds(postIds []string) (ForumPosts, error) { - requests := make([]*http.Request, len(postIds)) - - for i, id := range postIds { - request, _ := http.NewRequest("GET", id+".json", nil) - requests[i] = request - } - - task := decodeJsonFromRequestTask[lobstersPostResponseJson](defaultClient) - job := newJob(task, requests).withWorkers(30) - results, errs, err := workerPoolDo(job) +func getLobstersPostsFromFeed(feedUrl string) (ForumPosts, error) { + request, err := http.NewRequest("GET", feedUrl, nil) if err != nil { return nil, err } - posts := make(ForumPosts, 0, len(postIds)) + feed, err := decodeJsonFromRequest[lobstersFeedResponseJson](defaultClient, request) - for i := range results { - if errs[i] != nil { - slog.Error("Failed to fetch or parse lobsters post", "error", errs[i], "url", requests[i].URL) - continue - } + if err != nil { + return nil, err + } - tags := strings.Join(results[i].Tags, ",") + posts := make(ForumPosts, 0, len(feed)) + + for i := range feed { + tags := strings.Join(feed[i].Tags, ",") if tags != "" { tags = " [" + tags + "]" } - createdAt, _ := time.Parse(time.RFC3339, results[i].CreatedAt) + createdAt, _ := time.Parse(time.RFC3339, feed[i].CreatedAt) posts = append(posts, ForumPost{ - Title: results[i].Title + tags, - DiscussionUrl: results[i].CommentsURL, - TargetUrl: results[i].URL, - TargetUrlDomain: extractDomainFromUrl(results[i].URL), - CommentCount: results[i].CommentCount, - Score: results[i].Score, + Title: feed[i].Title + tags, + DiscussionUrl: feed[i].CommentsURL, + TargetUrl: feed[i].URL, + TargetUrlDomain: extractDomainFromUrl(feed[i].URL), + CommentCount: feed[i].CommentCount, + Score: feed[i].Score, TimePosted: createdAt, }) } @@ -116,23 +64,15 @@ func getLobstersPostsFromIds(postIds []string) (ForumPosts, error) { return nil, ErrNoContent } - if len(posts) != len(postIds) { - return posts, fmt.Errorf("%w could not fetch some lobsters posts", ErrPartialContent) - } - return posts, nil } -func FetchLobstersTopPosts(feed string, limit int) (ForumPosts, error) { - postIds, err := getLobstersTopPostIds(feed) +func FetchLobstersTopPosts(feedUrl string) (ForumPosts, error) { + posts, err := getLobstersPostsFromFeed(feedUrl) if err != nil { return nil, err } - if len(postIds) > limit { - postIds = postIds[:limit] - } - - return getLobstersPostsFromIds(postIds) + return posts, nil } diff --git a/internal/widget/lobsters.go b/internal/widget/lobsters.go index 1e91613..1b508d7 100644 --- a/internal/widget/lobsters.go +++ b/internal/widget/lobsters.go @@ -11,7 +11,7 @@ import ( type Lobsters struct { widgetBase `yaml:",inline"` - Feed string `yaml:"feed"` + FeedUrl string `yaml:"feed"` Posts feed.ForumPosts `yaml:"-"` Limit int `yaml:"limit"` CollapseAfter int `yaml:"collapse-after"` @@ -21,8 +21,8 @@ type Lobsters struct { func (widget *Lobsters) Initialize() error { widget.withTitle("Lobsters").withCacheDuration(30 * time.Minute) - if widget.Feed == "" { - widget.Feed = "https://lobste.rs/rss" + if widget.FeedUrl == "" { + widget.FeedUrl = "https://lobste.rs/active.json" } if widget.Limit <= 0 { @@ -37,7 +37,7 @@ func (widget *Lobsters) Initialize() error { } func (widget *Lobsters) Update(ctx context.Context) { - posts, err := feed.FetchLobstersTopPosts(widget.Feed, widget.Limit) + posts, err := feed.FetchLobstersTopPosts(widget.FeedUrl) if !widget.canContinueUpdateAfterHandlingErr(err) { return From eb9f92f04a06c57283baaa544941733bb3d6cbcc Mon Sep 17 00:00:00 2001 From: Jonas Knobloch Date: Sun, 12 May 2024 17:14:40 +0200 Subject: [PATCH 03/13] Update tag delimiter --- internal/feed/lobsters.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/feed/lobsters.go b/internal/feed/lobsters.go index d18b607..4c4e005 100644 --- a/internal/feed/lobsters.go +++ b/internal/feed/lobsters.go @@ -41,7 +41,7 @@ func getLobstersPostsFromFeed(feedUrl string) (ForumPosts, error) { posts := make(ForumPosts, 0, len(feed)) for i := range feed { - tags := strings.Join(feed[i].Tags, ",") + tags := strings.Join(feed[i].Tags, ", ") if tags != "" { tags = " [" + tags + "]" From c07a1ab63600808bcbab3ccd0800379532b5f38b Mon Sep 17 00:00:00 2001 From: Jonas Knobloch Date: Sun, 12 May 2024 17:37:04 +0200 Subject: [PATCH 04/13] Update default feed --- internal/widget/lobsters.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/widget/lobsters.go b/internal/widget/lobsters.go index 1b508d7..aab52a3 100644 --- a/internal/widget/lobsters.go +++ b/internal/widget/lobsters.go @@ -22,7 +22,7 @@ func (widget *Lobsters) Initialize() error { widget.withTitle("Lobsters").withCacheDuration(30 * time.Minute) if widget.FeedUrl == "" { - widget.FeedUrl = "https://lobste.rs/active.json" + widget.FeedUrl = "https://lobste.rs/hottest.json" } if widget.Limit <= 0 { From 53a661861a56dad1fbd7e13c0d59c16d1af64271 Mon Sep 17 00:00:00 2001 From: Wyatt Gill Date: Sat, 18 May 2024 17:41:21 -0500 Subject: [PATCH 05/13] Build app in Dockerfile This change makes it simpler and more convenient to build the app. --- Dockerfile.single-platform | 9 ++++++++- README.md | 6 ------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Dockerfile.single-platform b/Dockerfile.single-platform index 1930f99..5309059 100644 --- a/Dockerfile.single-platform +++ b/Dockerfile.single-platform @@ -1,7 +1,14 @@ +FROM golang:1.22.3-alpine3.19 AS builder + +WORKDIR /app +COPY . /app +RUN CGO_ENABLED=0 go build . + + FROM alpine:3.19 WORKDIR /app -COPY build/glance /app/glance +COPY --from=builder /app/glance . EXPOSE 8080/tcp ENTRYPOINT ["/app/glance"] diff --git a/README.md b/README.md index 715c8e5..11ffb42 100644 --- a/README.md +++ b/README.md @@ -92,12 +92,6 @@ go run . ### Building Docker image -Build Glance with CGO disabled: - -```bash -CGO_ENABLED=0 go build -o build/glance . -``` - Build the image: **Make sure to replace "owner" with your name or organization.** From e3ad646baee618e9b45624ce05b81939d863ac8b Mon Sep 17 00:00:00 2001 From: Wyatt Gill Date: Sat, 18 May 2024 17:42:02 -0500 Subject: [PATCH 06/13] Add .dockerignore to keep build context small --- .dockerignore | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..03da0ed --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +# https://docs.docker.com/build/building/context/#dockerignore-files +# Ignore all files by default +* + +# Only add necessary files to the Docker build context (Dockerfiles are always included implicitly) +!/internal/ +!/go.mod +!/go.sum +!main.go From 6de0f73ec1c8c02e97393714749d75d8823cb776 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Sun, 19 May 2024 21:33:51 +0100 Subject: [PATCH 07/13] Fix build breaking because of missing build dir --- .dockerignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.dockerignore b/.dockerignore index 03da0ed..8708dce 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,6 +3,7 @@ * # Only add necessary files to the Docker build context (Dockerfiles are always included implicitly) +!/build/ !/internal/ !/go.mod !/go.sum From c270afb5e580b05709cb1c7299bbc46400bcec80 Mon Sep 17 00:00:00 2001 From: NoxesP <87928637+NoxesP@users.noreply.github.com> Date: Mon, 20 May 2024 04:47:57 +0300 Subject: [PATCH 08/13] docs: update configuration.md Won't admit how long it took for me to notice this in my glance.yml file after wondering why it wasn't working ahahah. Small fix :) --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 7c04d04..18e23b2 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -626,7 +626,7 @@ https://your.proxy/?url={REQUEST-URL} ##### `sort-by` Can be used to specify the order in which the posts should get returned. Possible values are `hot`, `new`, `top` and `rising`. -##### `top-perid` +##### `top-period` Available only when `sort-by` is set to `top`. Possible values are `hour`, `day`, `week`, `month`, `year` and `all`. ##### `search` From 0a55c12f4f43f65c91023b136ddf11e0edda68a5 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Sun, 2 Jun 2024 17:50:46 +0100 Subject: [PATCH 09/13] Add tags to forum posts --- internal/feed/primitives.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/feed/primitives.go b/internal/feed/primitives.go index 99d6763..46afa12 100644 --- a/internal/feed/primitives.go +++ b/internal/feed/primitives.go @@ -16,6 +16,7 @@ type ForumPost struct { Score int Engagement float64 TimePosted time.Time + Tags []string } type ForumPosts []ForumPost From 5105ebb8701395f07785ab9368df7e1cdb7863f5 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Sun, 2 Jun 2024 17:51:07 +0100 Subject: [PATCH 10/13] Don't parse unused fields --- internal/feed/lobsters.go | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/internal/feed/lobsters.go b/internal/feed/lobsters.go index 4c4e005..6e56cd8 100644 --- a/internal/feed/lobsters.go +++ b/internal/feed/lobsters.go @@ -7,20 +7,13 @@ import ( ) type lobstersPostResponseJson struct { - ShortID string `json:"short_id"` - ShortIDURL string `json:"short_id_url"` - CreatedAt string `json:"created_at"` - Title string `json:"title"` - URL string `json:"url"` - Score int `json:"score"` - Flags int `json:"flags"` - CommentCount int `json:"comment_count"` - Description string `json:"description"` - DescriptionPlain string `json:"description_plain"` - CommentsURL string `json:"comments_url"` - SubmitterUser string `json:"submitter_user"` - UserIsAuthor bool `json:"user_is_author"` - Tags []string `json:"tags"` + CreatedAt string `json:"created_at"` + Title string `json:"title"` + URL string `json:"url"` + Score int `json:"score"` + CommentCount int `json:"comment_count"` + CommentsURL string `json:"comments_url"` + Tags []string `json:"tags"` } type lobstersFeedResponseJson []lobstersPostResponseJson From 3aa1f273b62254f98257d211a597fc91b0f2fd1e Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Sun, 2 Jun 2024 17:51:49 +0100 Subject: [PATCH 11/13] Add tags to dedicated field rather than to title --- internal/feed/lobsters.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/internal/feed/lobsters.go b/internal/feed/lobsters.go index 6e56cd8..dc53e65 100644 --- a/internal/feed/lobsters.go +++ b/internal/feed/lobsters.go @@ -2,7 +2,6 @@ package feed import ( "net/http" - "strings" "time" ) @@ -34,22 +33,17 @@ func getLobstersPostsFromFeed(feedUrl string) (ForumPosts, error) { posts := make(ForumPosts, 0, len(feed)) for i := range feed { - tags := strings.Join(feed[i].Tags, ", ") - - if tags != "" { - tags = " [" + tags + "]" - } - createdAt, _ := time.Parse(time.RFC3339, feed[i].CreatedAt) posts = append(posts, ForumPost{ - Title: feed[i].Title + tags, + Title: feed[i].Title, DiscussionUrl: feed[i].CommentsURL, TargetUrl: feed[i].URL, TargetUrlDomain: extractDomainFromUrl(feed[i].URL), CommentCount: feed[i].CommentCount, Score: feed[i].Score, TimePosted: createdAt, + Tags: feed[i].Tags, }) } From 720549ec7e3a74d1af1a1c841b88e40a2f6945b9 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Sun, 2 Jun 2024 18:08:29 +0100 Subject: [PATCH 12/13] Allow specifying sort order and tags through config --- internal/feed/lobsters.go | 18 +++++++++++++++++- internal/widget/lobsters.go | 9 +++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/internal/feed/lobsters.go b/internal/feed/lobsters.go index dc53e65..e103f56 100644 --- a/internal/feed/lobsters.go +++ b/internal/feed/lobsters.go @@ -2,6 +2,7 @@ package feed import ( "net/http" + "strings" "time" ) @@ -54,7 +55,22 @@ func getLobstersPostsFromFeed(feedUrl string) (ForumPosts, error) { return posts, nil } -func FetchLobstersTopPosts(feedUrl string) (ForumPosts, error) { +func FetchLobstersPosts(sortBy string, tags []string) (ForumPosts, error) { + var feedUrl string + + if sortBy == "hot" { + sortBy = "hottest" + } else if sortBy == "new" { + sortBy = "newest" + } + + if len(tags) == 0 { + feedUrl = "https://lobste.rs/" + sortBy + ".json" + } else { + tags := strings.Join(tags, ",") + feedUrl = "https://lobste.rs/t/" + tags + ".json" + } + posts, err := getLobstersPostsFromFeed(feedUrl) if err != nil { diff --git a/internal/widget/lobsters.go b/internal/widget/lobsters.go index aab52a3..d9a8c43 100644 --- a/internal/widget/lobsters.go +++ b/internal/widget/lobsters.go @@ -11,18 +11,19 @@ import ( type Lobsters struct { widgetBase `yaml:",inline"` - FeedUrl string `yaml:"feed"` Posts feed.ForumPosts `yaml:"-"` Limit int `yaml:"limit"` CollapseAfter int `yaml:"collapse-after"` + SortBy string `yaml:"sort-by"` + Tags []string `yaml:"tags"` ShowThumbnails bool `yaml:"-"` } func (widget *Lobsters) Initialize() error { widget.withTitle("Lobsters").withCacheDuration(30 * time.Minute) - if widget.FeedUrl == "" { - widget.FeedUrl = "https://lobste.rs/hottest.json" + if widget.SortBy == "" || (widget.SortBy != "hot" && widget.SortBy != "new") { + widget.SortBy = "hot" } if widget.Limit <= 0 { @@ -37,7 +38,7 @@ func (widget *Lobsters) Initialize() error { } func (widget *Lobsters) Update(ctx context.Context) { - posts, err := feed.FetchLobstersTopPosts(widget.FeedUrl) + posts, err := feed.FetchLobstersPosts(widget.SortBy, widget.Tags) if !widget.canContinueUpdateAfterHandlingErr(err) { return From 038c43bc0992521784c77553bd51a0fdaf55c45a Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Sun, 2 Jun 2024 18:17:20 +0100 Subject: [PATCH 13/13] Document lobsters widget --- docs/configuration.md | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index 0022c82..41da31b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -10,6 +10,7 @@ - [RSS](#rss) - [Videos](#videos) - [Hacker News](#hacker-news) + - [Lobsters](#lobsters) - [Reddit](#reddit) - [Weather](#weather) - [Monitor](#monitor) @@ -491,6 +492,49 @@ Placeholders: `{POST-ID}` - the ID of the post +### Lobsters +Display a list of posts from [Lobsters](https://lobste.rs). + +Example: + +```yaml +- type: lobsters + sort-by: hot + tags: + - go + - security + - linux + limit: 15 + collapse-after: 5 +``` + + + +#### Properties +| Name | Type | Required | Default | +| ---- | ---- | -------- | ------- | +| limit | integer | no | 15 | +| collapse-after | integer | no | 5 | +| sort-by | string | no | hot | +| tags | array | no | | + +##### `limit` +The maximum number of posts to show. + +##### `collapse-after` +How many posts are visible before the "SHOW MORE" button appears. Set to `-1` to never collapse. + +##### `sort-by` +The sort order in which posts are returned. Possible options are `hot` and `new`. + +##### `tags` +Limit to posts containing one of the given tags. **You cannot specify a sort order when filtering by tags, it will default to `hot`.** + ### Reddit Display a list of posts from a specific subreddit.