diff --git a/cmd/root.go b/cmd/root.go index dafb3f4..3b37847 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -82,12 +82,13 @@ func loadSystems(c *ctx.Ctx) []error { var errs []error for _, sysCfg := range c.Config.Systems { + c.Logger.Debugf("loading system of type %s ...", sysCfg.Type) sys, err := system.New(sysCfg.Type, &sysCfg.Config, LOG) if err != nil { c.Logger.Errorf("error loading system: %s", err) errs = append(errs, err) } else { - c.Logger.Debugln("loaded system") + c.Logger.Debugf("loaded %s system", sysCfg.Type) } c.AddSystem(&sys) @@ -109,19 +110,6 @@ var rootCmd = &cobra.Command{ c := ctx.New(&CFG, LOG) _ = loadSystems(&c) // TODO: Handle errs - // a, _ := aggregator.New(&c) - // posts, errs := a.ListPosts() - // // posts, err := (*c.Systems[0]).ListPosts() - // fmt.Println("-----------------------") - // fmt.Printf("%v\n", posts) - // fmt.Printf("%v\n", errs) - // - // // err = s(*c.Systems[0]).LoadPost(&posts[4]) - // err := a.LoadPost(&posts[4]) - // fmt.Printf("%v\n", posts[4].Replies[2]) - // fmt.Printf("%v\n", err) - // os.Exit(0) - tui := tea.NewProgram(ui.NewModel(&c), tea.WithAltScreen()) err = tui.Start() if err != nil { diff --git a/models/reply/reply.go b/models/reply/reply.go index 6c3fd85..9be261d 100644 --- a/models/reply/reply.go +++ b/models/reply/reply.go @@ -11,7 +11,11 @@ type Reply struct { Body string + Deleted bool + CreatedAt time.Time Author author.Author + + Replies []Reply } diff --git a/system/hackernews/hackernews.go b/system/hackernews/hackernews.go index dfceac4..7f4d850 100644 --- a/system/hackernews/hackernews.go +++ b/system/hackernews/hackernews.go @@ -57,7 +57,7 @@ func (sys *System) Load() error { } func (sys *System) ListPosts(sysIdx int) ([]post.Post, error) { - stories, err := sys.client.NewStories(context.Background()) + stories, err := sys.client.TopStories(context.Background()) if err != nil { return []post.Post{}, err } @@ -121,10 +121,18 @@ func (sys *System) ListPosts(sysIdx int) ([]post.Post, error) { } func (sys *System) LoadPost(p *post.Post) error { - for r := 0; r < len(p.Replies); r++ { - reply := &p.Replies[r] + sys.logger.Debug(p.Replies) + err := sys.loadReplies(&p.Replies) + sys.logger.Debug(p.Replies) + return err +} - id, err := strconv.Atoi(reply.ID) +func (sys *System) loadReplies(replies *[]reply.Reply) error { + sys.logger.Debug("loading replies") + for r := 0; r < len(*replies); r++ { + re := &(*replies)[r] + + id, err := strconv.Atoi(re.ID) if err != nil { sys.logger.Error(err) continue @@ -136,16 +144,31 @@ func (sys *System) LoadPost(p *post.Post) error { continue } + if i.Deleted || i.Dead { + re.Deleted = true + } + createdAt := time.Unix(int64(i.Time), 0) - reply.Body = i.Text + re.Body = i.Text - reply.CreatedAt = createdAt + re.CreatedAt = createdAt - reply.Author = author.Author{ + re.Author = author.Author{ ID: i.By, Name: i.By, } + + for _, commentID := range i.Kids { + re.Replies = append(re.Replies, reply.Reply{ + ID: strconv.Itoa(commentID), + }) + } + + if err := sys.loadReplies(&re.Replies); err != nil { + sys.logger.Error(err) + } } + return nil } diff --git a/ui/views/posts/posts.go b/ui/views/posts/posts.go index 2bc66d0..b8215ac 100644 --- a/ui/views/posts/posts.go +++ b/ui/views/posts/posts.go @@ -1,6 +1,7 @@ package posts import ( + "encoding/json" "fmt" "strings" @@ -12,6 +13,7 @@ import ( "github.com/charmbracelet/lipgloss" "github.com/mrusme/gobbs/aggregator" "github.com/mrusme/gobbs/models/post" + "github.com/mrusme/gobbs/models/reply" "github.com/mrusme/gobbs/ui/ctx" "github.com/mrusme/gobbs/ui/helpers" ) @@ -147,10 +149,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmds = append(cmds, m.refresh()) case key.Matches(msg, m.keymap.Select): + m.ctx.Loading = true i, ok := m.list.SelectedItem().(post.Post) if ok { - m.viewport.SetContent(m.renderViewport(&i)) - return m, nil + cmds = append(cmds, m.loadItem(&i)) } case key.Matches(msg, m.keymap.Close): @@ -184,6 +186,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.items = msg m.list.SetItems(m.items) m.ctx.Loading = false + + case *post.Post: + m.viewport.SetContent(m.renderViewport(msg)) + m.ctx.Loading = false + return m, nil + } var cmd tea.Cmd @@ -249,10 +257,15 @@ func (m *Model) refresh() tea.Cmd { } } -func (m *Model) renderViewport(post *post.Post) string { - var vp string = "" +func (m *Model) loadItem(p *post.Post) tea.Cmd { + return func() tea.Msg { + m.a.LoadPost(p) + return p + } +} - m.a.LoadPost(post) +func (m *Model) renderViewport(p *post.Post) string { + var out string = "" var err error m.glam, err = glamour.NewTermRenderer( @@ -265,41 +278,73 @@ func (m *Model) renderViewport(post *post.Post) string { } adj := "writes" - if post.Subject[len(post.Subject)-1:] == "?" { + if p.Subject[len(p.Subject)-1:] == "?" { adj = "asks" } - body, err := m.glam.Render(post.Body) + body, err := m.glam.Render(p.Body) if err != nil { m.ctx.Logger.Error(err) - body = post.Body + body = p.Body } - vp = fmt.Sprintf( + out += fmt.Sprintf( " %s\n %s\n%s", postAuthorStyle.Render( - fmt.Sprintf("%s %s:", post.Author.Name, adj), + fmt.Sprintf("%s %s:", p.Author.Name, adj), ), - postSubjectStyle.Render(post.Subject), + postSubjectStyle.Render(p.Subject), body, ) - for _, reply := range post.Replies { - body, err := m.glam.Render(reply.Body) - if err != nil { - m.ctx.Logger.Error(err) - body = reply.Body - } - vp = fmt.Sprintf( - "%s\n\n %s %s\n%s", - vp, - replyAuthorStyle.Render( - reply.Author.Name, - ), - lipgloss.NewStyle().Foreground(lipgloss.Color("#874BFD")).Render("writes:"), - body, - ) - } + bla, _ := json.Marshal(p.Replies) + m.ctx.Logger.Debugf("%s", bla) + out += m.renderReplies(0, p.Author.Name, &p.Replies) m.viewportOpen = true - return vp + return out +} + +func (m *Model) renderReplies( + level int, + inReplyTo string, + replies *[]reply.Reply, +) string { + var out string = "" + + if replies == nil { + return "" + } + + for _, re := range *replies { + var err error = nil + var body string = "" + var author string = "" + + if re.Deleted { + body = "\n DELETED" + author = "DELETED" + } else { + body, err = m.glam.Render(re.Body) + if err != nil { + m.ctx.Logger.Error(err) + body = re.Body + } + + author = re.Author.Name + } + out += fmt.Sprintf( + "\n\n %s %s\n%s", + replyAuthorStyle.Render( + author, + ), + lipgloss.NewStyle().Foreground(lipgloss.Color("#874BFD")).Render( + fmt.Sprintf("writes in reply to %s:", inReplyTo), + ), + body, + ) + + out += m.renderReplies(level+1, re.Author.Name, &re.Replies) + } + + return out }