diff --git a/pkg/tui/conversations.go b/pkg/tui/conversations.go index 2df825f..6854c52 100644 --- a/pkg/tui/conversations.go +++ b/pkg/tui/conversations.go @@ -13,18 +13,22 @@ import ( "github.com/charmbracelet/lipgloss" ) +type loadedConversation struct { + conv models.Conversation + lastReply models.Message +} + type ( - // send when conversation list is loaded - msgConversationsLoaded []models.Conversation + // sent when conversation list is loaded + msgConversationsLoaded ([]loadedConversation) + // sent when each coversation's 'last reply' is loaded ) type conversationsModel struct { basemodel - conversations []models.Conversation - lastReplies []models.Message - - content viewport.Model + conversations []loadedConversation + content viewport.Model } func newConversationsModel(tui *model) conversationsModel { @@ -102,24 +106,29 @@ func (m conversationsModel) Update(msg tea.Msg) (conversationsModel, tea.Cmd) { func (m *conversationsModel) loadConversations() tea.Cmd { return func() tea.Msg { - c, err := m.ctx.Store.Conversations() + conversations, err := m.ctx.Store.Conversations() if err != nil { return msgError(fmt.Errorf("Could not load conversations: %v", err)) } - return msgConversationsLoaded(c) + + loaded := make([]loadedConversation, len(conversations)) + for i, c := range conversations { + lastMessage, err := m.ctx.Store.LastMessage(&c) + if err != nil { + return msgError(err) + } + loaded[i].conv = c + loaded[i].lastReply = *lastMessage + } + + slices.SortFunc(loaded, func(a, b loadedConversation) int { + return b.lastReply.CreatedAt.Compare(a.lastReply.CreatedAt) + }) + + return msgConversationsLoaded(loaded) } } -//func (m *conversationsModel) loadConversationLastestReplies(conversations []models.Conversation) tea.Cmd { -// return func() tea.Msg { -// //lastMessage, err := m.ctx.Store.LastMessage(&conversation) -// return nil -// } -//} - -//func (m *conversationsModel) setConversations(conversations []models.Conversation) { -//} - func (m *conversationsModel) headerView() string { titleStyle := lipgloss.NewStyle().Bold(true) header := titleStyle.Render("Conversations") @@ -127,13 +136,12 @@ func (m *conversationsModel) headerView() string { } func (m *conversationsModel) renderConversationList() string { - sb := &strings.Builder{} - type AgeGroup struct { + type timeCategory struct { name string cutoff time.Duration } - type ConversationLine struct { + type listItem struct { id uint short string title string @@ -142,11 +150,10 @@ func (m *conversationsModel) renderConversationList() string { } now := time.Now() - midnight := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location()) dayOfWeek := int(now.Weekday()) - categories := []AgeGroup{ + categories := []timeCategory{ {"Today", now.Sub(midnight)}, {"Yesterday", now.Sub(midnight.AddDate(0, 0, -1))}, {"This week", now.Sub(midnight.AddDate(0, 0, -dayOfWeek))}, @@ -160,32 +167,6 @@ func (m *conversationsModel) renderConversationList() string { {"6 Months ago", now.Sub(monthStart.AddDate(0, -6, 0))}, {"Older", now.Sub(time.Time{})}, } - categorized := map[string][]ConversationLine{} - - for _, conversation := range m.conversations { - lastMessage, err := m.ctx.Store.LastMessage(&conversation) - if lastMessage == nil || err != nil { - continue - } - messageAge := now.Sub(lastMessage.CreatedAt) - var category string - for _, c := range categories { - if messageAge < c.cutoff { - category = c.name - break - } - } - - categorized[category] = append( - categorized[category], - ConversationLine{ - id: conversation.ID, - title: conversation.Title, - short: conversation.ShortName.String, - lastReplyAge: messageAge, - }, - ) - } // TODO: pick nice color categoryStyle := lipgloss.NewStyle(). @@ -198,35 +179,39 @@ func (m *conversationsModel) renderConversationList() string { MarginBottom(1). PaddingLeft(2) - for _, category := range categories { - conversationLines, ok := categorized[category.name] - if !ok { - continue - } + ageStyle := lipgloss.NewStyle().Faint(true) + titleStyle := lipgloss.NewStyle().Bold(true) + untitledStyle := titleStyle.Copy().Italic(true).Faint(true).SetString("(untitled)") - slices.SortFunc(conversationLines, func(a, b ConversationLine) int { - return int(a.lastReplyAge - b.lastReplyAge) - }) + var currentCategory string + sb := &strings.Builder{} + for _, c := range m.conversations { + lastReplyAge := now.Sub(c.lastReply.CreatedAt) - ageStyle := lipgloss.NewStyle().Faint(true) - titleStyle := lipgloss.NewStyle().Bold(true) - untitledStyle := titleStyle.Copy().Italic(true).SetString("(untitled)") - - fmt.Fprintf(sb, "%s\n", categoryStyle.Render(category.name)) - for _, c := range conversationLines { - tstyle := titleStyle - if c.title == "" { - tstyle = untitledStyle + var category string + for _, g := range categories { + if lastReplyAge < g.cutoff { + category = g.name + break } - heading := fmt.Sprintf( - "%s\n%s", - tstyle.Render(c.title), - ageStyle.Render(util.HumanTimeElapsedSince(c.lastReplyAge)), - ) - sb.WriteString(conversationHeadingStyle.Render(heading)) - sb.WriteRune('\n') } - } + if category != currentCategory { + currentCategory = category + fmt.Fprintf(sb, "%s\n", categoryStyle.Render(currentCategory)) + } + + tstyle := titleStyle + if c.conv.Title == "" { + tstyle = untitledStyle + } + heading := fmt.Sprintf( + "%s\n%s", + tstyle.Render(c.conv.Title), + ageStyle.Render(util.HumanTimeElapsedSince(lastReplyAge)), + ) + sb.WriteString(conversationHeadingStyle.Render(heading)) + sb.WriteRune('\n') + } return sb.String() }