diff --git a/aggregator/aggregator.go b/aggregator/aggregator.go index 698cf07..677bc19 100644 --- a/aggregator/aggregator.go +++ b/aggregator/aggregator.go @@ -4,7 +4,9 @@ import ( "encoding/json" "os" "sort" + "strings" + "github.com/mrusme/gobbs/models/forum" "github.com/mrusme/gobbs/models/post" "github.com/mrusme/gobbs/models/reply" "github.com/mrusme/gobbs/ui/ctx" @@ -21,6 +23,33 @@ func New(c *ctx.Ctx) (*Aggregator, error) { return a, nil } +func (a *Aggregator) ListForums() ([]forum.Forum, []error) { + var errs []error = make([]error, len(a.ctx.Systems)) + var forums []forum.Forum + + for idx, sys := range a.ctx.Systems { + if curSysIDX := a.ctx.GetCurrentSystem(); curSysIDX != -1 { + if idx != curSysIDX { + continue + } + } + + sysForums, err := (*sys).ListForums() + if err != nil { + errs[idx] = err + continue + } + forums = append(forums, sysForums...) + } + + sort.SliceStable(forums, func(i, j int) bool { + return strings.Compare(forums[i].Name, forums[j].Name) == 1 + }) + + return forums, errs + +} + func (a *Aggregator) ListPosts() ([]post.Post, []error) { var errs []error = make([]error, len(a.ctx.Systems)) var posts []post.Post @@ -37,7 +66,13 @@ func (a *Aggregator) ListPosts() ([]post.Post, []error) { } for idx, sys := range a.ctx.Systems { - sysPosts, err := (*sys).ListPosts() + if curSysIDX := a.ctx.GetCurrentSystem(); curSysIDX != -1 { + if idx != curSysIDX { + continue + } + } + + sysPosts, err := (*sys).ListPosts(a.ctx.GetCurrentForum().ID) if err != nil { errs[idx] = err continue diff --git a/cmd/root.go b/cmd/root.go index aae27d1..a3f4755 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -92,6 +92,7 @@ func loadSystems(c *ctx.Ctx) []error { } c.AddSystem(&sys) + c.Logger.Debugf("setting system ID to %d", c.NumSystems()-1) sys.SetID(c.NumSystems() - 1) } diff --git a/models/forum/forum.go b/models/forum/forum.go index 165d132..7e1d7e5 100644 --- a/models/forum/forum.go +++ b/models/forum/forum.go @@ -3,4 +3,18 @@ package forum type Forum struct { ID string Name string + + SysIDX int +} + +func (forum Forum) FilterValue() string { + return forum.Name +} + +func (forum Forum) Title() string { + return forum.Name +} + +func (forum Forum) Description() string { + return forum.ID } diff --git a/system/all/all.go b/system/all/all.go index 159e226..f41f896 100644 --- a/system/all/all.go +++ b/system/all/all.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" + "github.com/mrusme/gobbs/models/forum" "github.com/mrusme/gobbs/models/post" "github.com/mrusme/gobbs/models/reply" "github.com/mrusme/gobbs/system/adapter" @@ -65,7 +66,11 @@ func (sys *System) Connect(sysURL string) error { return errors.New("This system can't be connected to") } -func (sys *System) ListPosts() ([]post.Post, error) { +func (sys *System) ListForums() ([]forum.Forum, error) { + return []forum.Forum{}, nil +} + +func (sys *System) ListPosts(forumID string) ([]post.Post, error) { return []post.Post{}, nil } diff --git a/system/discourse/api/topics.go b/system/discourse/api/topics.go index 202ad01..7b20ea2 100644 --- a/system/discourse/api/topics.go +++ b/system/discourse/api/topics.go @@ -2,6 +2,7 @@ package api import ( "context" + "fmt" "net/http" ) @@ -82,6 +83,8 @@ type TopicsService interface { ) (*SingleTopicResponse, error) ListLatest( ctx context.Context, + categorySlug string, + categoryID int, ) (*LatestTopicsResponse, error) } @@ -112,8 +115,15 @@ func (a *TopicServiceHandler) Show( // List func (a *TopicServiceHandler) ListLatest( ctx context.Context, + categorySlug string, + categoryID int, ) (*LatestTopicsResponse, error) { - uri := "/latest.json" + var uri string + if categoryID == -1 { + uri = "/latest.json" + } else { + uri = fmt.Sprintf("/c/%s/%d.json", categorySlug, categoryID) + } req, err := a.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { diff --git a/system/discourse/discourse.go b/system/discourse/discourse.go index 6f86450..a5e4bab 100644 --- a/system/discourse/discourse.go +++ b/system/discourse/discourse.go @@ -115,13 +115,51 @@ func (sys *System) Load() error { return nil } -func (sys *System) ListPosts() ([]post.Post, error) { +func (sys *System) ListForums() ([]forum.Forum, error) { + var models []forum.Forum + + cats, err := sys.client.Categories.List(context.Background()) + if err != nil { + return []forum.Forum{}, err + } + + for _, cat := range cats.CategoryList.Categories { + models = append(models, forum.Forum{ + ID: strconv.Itoa(cat.ID), + Name: cat.Name, + + SysIDX: sys.ID, + }) + } + + return models, nil +} + +func (sys *System) ListPosts(forumID string) ([]post.Post, error) { + var catSlug string = "" + var catID int = -1 + var err error + cats, err := sys.client.Categories.List(context.Background()) if err != nil { return []post.Post{}, err } - items, err := sys.client.Topics.ListLatest(context.Background()) + if forumID != "" { + catID, err = strconv.Atoi(forumID) + if err != nil { + return []post.Post{}, err + } + + for _, cat := range cats.CategoryList.Categories { + if cat.ID == catID { + catSlug = cat.Slug + break + } + } + } + + items, err := sys.client.Topics.ListLatest(context.Background(), catSlug, catID) if err != nil { return []post.Post{}, err } @@ -181,6 +219,8 @@ func (sys *System) ListPosts() ([]post.Post, error) { Forum: forum.Forum{ ID: strconv.Itoa(i.CategoryID), Name: forumName, + + SysIDX: sys.ID, }, SysIDX: sys.ID, diff --git a/system/hackernews/hackernews.go b/system/hackernews/hackernews.go index 293955a..6975c62 100644 --- a/system/hackernews/hackernews.go +++ b/system/hackernews/hackernews.go @@ -82,8 +82,59 @@ func (sys *System) Load() error { return nil } -func (sys *System) ListPosts() ([]post.Post, error) { - stories, err := sys.client.TopStories(context.Background()) +func (sys *System) ListForums() ([]forum.Forum, error) { + return []forum.Forum{ + { + ID: "top", + Name: "Top HN Stories", + SysIDX: sys.ID, + }, + { + ID: "best", + Name: "Best HN Stories", + SysIDX: sys.ID, + }, + { + ID: "new", + Name: "New HN Stories", + SysIDX: sys.ID, + }, + { + ID: "ask", + Name: "Ask HN", + SysIDX: sys.ID, + }, + { + ID: "show", + Name: "Show HN", + SysIDX: sys.ID, + }, + { + ID: "jobs", + Name: "Jobs HN", + SysIDX: sys.ID, + }, + }, nil +} + +func (sys *System) ListPosts(forumID string) ([]post.Post, error) { + var stories []int + var err error + + switch forumID { + case "top": + stories, err = sys.client.TopStories(context.Background()) + case "best": + stories, err = sys.client.BestStories(context.Background()) + case "ask": + stories, err = sys.client.AskStories(context.Background()) + case "show": + stories, err = sys.client.ShowStories(context.Background()) + case "jobs": + stories, err = sys.client.JobStories(context.Background()) + default: + stories, err = sys.client.NewStories(context.Background()) + } if err != nil { return []post.Post{}, err } @@ -142,6 +193,8 @@ func (sys *System) ListPosts() ([]post.Post, error) { Forum: forum.Forum{ ID: "new", Name: "New", + + SysIDX: sys.ID, }, Replies: replies, diff --git a/system/lemmy/lemmy.go b/system/lemmy/lemmy.go index 099952d..fd6f94e 100644 --- a/system/lemmy/lemmy.go +++ b/system/lemmy/lemmy.go @@ -2,6 +2,7 @@ package lemmy import ( "context" + "encoding/json" "fmt" "net/url" "strconv" @@ -113,7 +114,31 @@ func (sys *System) Load() error { return nil } -func (sys *System) ListPosts() ([]post.Post, error) { +func (sys *System) ListForums() ([]forum.Forum, error) { + resp, err := sys.client.Communities(context.Background(), types.ListCommunities{ + Type: types.NewOptional(types.ListingSubscribed), + }) + if err != nil { + return []forum.Forum{}, err + } + + var models []forum.Forum + for _, i := range resp.Communities { + sys.logger.Debugf("FORUM:") + b, _ := json.Marshal(i) + sys.logger.Debug(string(b)) + models = append(models, forum.Forum{ + ID: strconv.Itoa(i.CommunitySafe.ID), + Name: i.CommunitySafe.Name, + + SysIDX: sys.ID, + }) + } + + return models, nil +} + +func (sys *System) ListPosts(forumID string) ([]post.Post, error) { resp, err := sys.client.Posts(context.Background(), types.GetPosts{ Type: types.NewOptional(types.ListingSubscribed), Sort: types.NewOptional(types.New), @@ -163,6 +188,8 @@ func (sys *System) ListPosts() ([]post.Post, error) { Forum: forum.Forum{ ID: strconv.Itoa(i.Post.CommunityID), Name: i.Community.Name, + + SysIDX: sys.ID, }, SysIDX: sys.ID, diff --git a/system/system.go b/system/system.go index 5e0ae28..df01765 100644 --- a/system/system.go +++ b/system/system.go @@ -3,6 +3,7 @@ package system import ( "errors" + "github.com/mrusme/gobbs/models/forum" "github.com/mrusme/gobbs/models/post" "github.com/mrusme/gobbs/models/reply" "github.com/mrusme/gobbs/system/adapter" @@ -28,7 +29,8 @@ type System interface { Connect(sysURL string) error Load() error - ListPosts() ([]post.Post, error) + ListForums() ([]forum.Forum, error) + ListPosts(forumID string) ([]post.Post, error) LoadPost(p *post.Post) error CreatePost(p *post.Post) error CreateReply(r *reply.Reply) error diff --git a/ui/ctx/ctx.go b/ui/ctx/ctx.go index f52b8b3..020487a 100644 --- a/ui/ctx/ctx.go +++ b/ui/ctx/ctx.go @@ -2,6 +2,7 @@ package ctx import ( "github.com/mrusme/gobbs/config" + "github.com/mrusme/gobbs/models/forum" "github.com/mrusme/gobbs/system" "github.com/mrusme/gobbs/ui/theme" "go.uber.org/zap" @@ -17,7 +18,7 @@ type Ctx struct { Theme *theme.Theme currentSystem int - currentForum string + currentForum forum.Forum } func New( @@ -33,7 +34,7 @@ func New( Theme: theme.New(cfg), currentSystem: -1, - currentForum: "", + currentForum: forum.Forum{}, } } @@ -54,10 +55,10 @@ func (c *Ctx) GetCurrentSystem() int { return c.currentSystem } -func (c *Ctx) SetCurrentForum(id string) { - c.currentForum = id +func (c *Ctx) SetCurrentForum(f forum.Forum) { + c.currentForum = f } -func (c *Ctx) GetCurrentForum() string { +func (c *Ctx) GetCurrentForum() forum.Forum { return c.currentForum } diff --git a/ui/header/header.go b/ui/header/header.go index 59eee92..e4546e6 100644 --- a/ui/header/header.go +++ b/ui/header/header.go @@ -77,8 +77,14 @@ func (m Model) View() string { currentSystem = (*m.ctx.Systems[curSysIdx]).Title() } + curForum := m.ctx.GetCurrentForum() + var currentForum string = "All" + if curForum.ID != "" { + currentForum = curForum.Title() + } + systemSelector := selector.Render(fmt.Sprintf("⏷ %s", currentSystem)) - forumSelector := selector.Render("⏷ All") + forumSelector := selector.Render(fmt.Sprintf("⏷ %s", currentForum)) selectorColumn := lipgloss.JoinVertical(lipgloss.Center, lipgloss.JoinHorizontal(lipgloss.Bottom, "System: \n "+ diff --git a/ui/ui.go b/ui/ui.go index bb4e38e..343e19e 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -5,6 +5,7 @@ import ( "strings" + "github.com/mrusme/gobbs/aggregator" "github.com/mrusme/gobbs/models/forum" "github.com/mrusme/gobbs/system" "github.com/mrusme/gobbs/ui/cmd" @@ -54,6 +55,8 @@ type Model struct { wm *windowmanager.WM ctx *ctx.Ctx + a *aggregator.Aggregator + viewcache string viewcacheID string renderOnlyFocused bool @@ -73,6 +76,8 @@ func NewModel(c *ctx.Ctx) Model { m.header = header.NewModel(m.ctx) m.views = append(m.views, posts.NewModel(m.ctx)) + m.a, _ = aggregator.New(m.ctx) + return m } @@ -130,6 +135,39 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { ) return m, tea.Batch(ccmds...) + case key.Matches(msg, m.keymap.ForumSelect): + var listItems []list.Item + + all := forum.Forum{ID: "", Name: "All", SysIDX: -1} + listItems = append(listItems, all) + + forums, errs := m.a.ListForums() + if len(errs) > 0 { + // TODO: Handle errors + } + for _, f := range forums { + m.ctx.Logger.Debugf("Adding forum to list: %s ID %d\n", f.Title(), f.SysIDX) + listItems = append(listItems, f) + } + + ccmds := m.wm.Open( + popuplist.WIN_ID, + popuplist.NewModel(m.ctx), + [4]int{ + int(m.ctx.Content[1] / 2), + int(m.ctx.Content[1] / 4), + int(m.ctx.Content[1] / 2), + int(m.ctx.Content[1] / 4), + }, + cmd.New( + cmd.WinOpen, + popuplist.WIN_ID, + cmd.Arg{Name: "selectionID", Value: "forum"}, + cmd.Arg{Name: "items", Value: listItems}, + ), + ) + return m, tea.Batch(ccmds...) + default: if m.wm.GetNumberOpen() > 0 { cmd := m.wm.Update(m.wm.Focused(), msg) @@ -198,9 +236,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "system": selected := msg.GetArg("selected").(system.System) m.ctx.SetCurrentSystem(selected.GetID()) + m.ctx.SetCurrentForum(forum.Forum{}) case "forum": selected := msg.GetArg("selected").(forum.Forum) - m.ctx.SetCurrentForum(selected.ID) + m.ctx.Logger.Debugf("selecting system ID %d\n", selected.SysIDX) + m.ctx.SetCurrentSystem(selected.SysIDX) + m.ctx.SetCurrentForum(selected) } }