Add LastMessageAt field to conversation
Replaced `LatestConversationMessages` with `LoadConversationList`, which utilizes `LastMessageAt` for much faster conversation loading in the conversation listing TUI and `lmcli list` command.
This commit is contained in:
@@ -19,9 +19,9 @@ import (
|
||||
|
||||
type (
|
||||
// sent when conversation list is loaded
|
||||
msgConversationsLoaded ([]model.LoadedConversation)
|
||||
// sent when a conversation is selected
|
||||
msgConversationSelected conversation.Conversation
|
||||
msgConversationsLoaded conversation.ConversationList
|
||||
// sent when a single conversation is loaded
|
||||
msgConversationLoaded *conversation.Conversation
|
||||
// sent when a conversation is deleted
|
||||
msgConversationDeleted struct{}
|
||||
)
|
||||
@@ -56,19 +56,17 @@ func (m *Model) handleInput(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
}
|
||||
|
||||
conversations := m.App.Conversations.Items
|
||||
|
||||
switch msg.String() {
|
||||
case "enter":
|
||||
if len(m.App.Conversations) > 0 && m.cursor < len(m.App.Conversations) {
|
||||
m.App.ClearConversation()
|
||||
m.App.Conversation = &m.App.Conversations[m.cursor].Conv
|
||||
return func() tea.Msg {
|
||||
return shared.MsgViewChange(shared.ViewChat)
|
||||
}
|
||||
if len(conversations) > 0 && m.cursor < len(conversations) {
|
||||
return m.loadConversation(conversations[m.cursor].ID)
|
||||
}
|
||||
case "j", "down":
|
||||
if m.cursor < len(m.App.Conversations)-1 {
|
||||
if m.cursor < len(conversations)-1 {
|
||||
m.cursor++
|
||||
if m.cursor == len(m.App.Conversations)-1 {
|
||||
if m.cursor == len(conversations)-1 {
|
||||
m.content.GotoBottom()
|
||||
} else {
|
||||
// this hack positions the *next* conversatoin slightly
|
||||
@@ -78,7 +76,7 @@ func (m *Model) handleInput(msg tea.KeyMsg) tea.Cmd {
|
||||
}
|
||||
m.content.SetContent(m.renderConversationList())
|
||||
} else {
|
||||
m.cursor = len(m.App.Conversations) - 1
|
||||
m.cursor = len(conversations) - 1
|
||||
m.content.GotoBottom()
|
||||
}
|
||||
return shared.KeyHandled(msg)
|
||||
@@ -100,14 +98,14 @@ func (m *Model) handleInput(msg tea.KeyMsg) tea.Cmd {
|
||||
m.App.NewConversation()
|
||||
return shared.ChangeView(shared.ViewChat)
|
||||
case "d":
|
||||
if !m.confirmPrompt.Focused() && len(m.App.Conversations) > 0 && m.cursor < len(m.App.Conversations) {
|
||||
title := m.App.Conversations[m.cursor].Conv.Title
|
||||
if !m.confirmPrompt.Focused() && len(conversations) > 0 && m.cursor < len(conversations) {
|
||||
title := conversations[m.cursor].Title
|
||||
if title == "" {
|
||||
title = "(untitled)"
|
||||
}
|
||||
m.confirmPrompt = bubbles.NewConfirmPrompt(
|
||||
fmt.Sprintf("Delete '%s'?", title),
|
||||
m.App.Conversations[m.cursor].Conv,
|
||||
conversations[m.cursor],
|
||||
)
|
||||
m.confirmPrompt.Style = lipgloss.NewStyle().
|
||||
Bold(true).
|
||||
@@ -148,9 +146,15 @@ func (m *Model) Update(msg tea.Msg) (shared.ViewModel, tea.Cmd) {
|
||||
m.width, m.height = msg.Width, msg.Height
|
||||
m.content.SetContent(m.renderConversationList())
|
||||
case msgConversationsLoaded:
|
||||
m.App.Conversations = msg
|
||||
m.cursor = max(0, min(len(m.App.Conversations), m.cursor))
|
||||
m.App.Conversations = conversation.ConversationList(msg)
|
||||
m.cursor = max(0, min(len(m.App.Conversations.Items), m.cursor))
|
||||
m.content.SetContent(m.renderConversationList())
|
||||
case msgConversationLoaded:
|
||||
m.App.ClearConversation()
|
||||
m.App.Conversation = msg
|
||||
cmds = append(cmds, func() tea.Msg {
|
||||
return shared.MsgViewChange(shared.ViewChat)
|
||||
})
|
||||
case bubbles.MsgConfirmPromptAnswered:
|
||||
m.confirmPrompt.Blur()
|
||||
if msg.Value {
|
||||
@@ -180,11 +184,21 @@ func (m *Model) Update(msg tea.Msg) (shared.ViewModel, tea.Cmd) {
|
||||
|
||||
func (m *Model) loadConversations() tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
err, conversations := m.App.LoadConversations()
|
||||
list, err := m.App.Ctx.Conversations.LoadConversationList()
|
||||
if err != nil {
|
||||
return shared.AsMsgError(fmt.Errorf("Could not load conversations: %v", err))
|
||||
}
|
||||
return msgConversationsLoaded(conversations)
|
||||
return msgConversationsLoaded(list)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) loadConversation(conversationID uint) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
conversation, err := m.App.Ctx.Conversations.GetConversationByID(conversationID)
|
||||
if err != nil {
|
||||
return shared.AsMsgError(fmt.Errorf("Could not load conversation %d: %v", conversationID, err))
|
||||
}
|
||||
return msgConversationLoaded(conversation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,12 +273,12 @@ func (m *Model) renderConversationList() string {
|
||||
sb strings.Builder
|
||||
)
|
||||
|
||||
m.itemOffsets = make([]int, len(m.App.Conversations))
|
||||
m.itemOffsets = make([]int, len(m.App.Conversations.Items))
|
||||
sb.WriteRune('\n')
|
||||
currentOffset += 1
|
||||
|
||||
for i, c := range m.App.Conversations {
|
||||
lastReplyAge := now.Sub(c.LastReply.CreatedAt)
|
||||
for i, c := range m.App.Conversations.Items {
|
||||
lastReplyAge := now.Sub(c.LastMessageAt)
|
||||
|
||||
var category string
|
||||
for _, g := range categories {
|
||||
@@ -284,14 +298,14 @@ func (m *Model) renderConversationList() string {
|
||||
}
|
||||
|
||||
tStyle := titleStyle
|
||||
if c.Conv.Title == "" {
|
||||
if c.Title == "" {
|
||||
tStyle = tStyle.Inherit(untitledStyle).SetString("(untitled)")
|
||||
}
|
||||
if i == m.cursor {
|
||||
tStyle = tStyle.Inherit(selectedStyle)
|
||||
}
|
||||
|
||||
title := tStyle.Width(m.width - 3).PaddingLeft(2).Render(c.Conv.Title)
|
||||
title := tStyle.Width(m.width - 3).PaddingLeft(2).Render(c.Title)
|
||||
if i == m.cursor {
|
||||
title = ">" + title[1:]
|
||||
}
|
||||
@@ -304,7 +318,7 @@ func (m *Model) renderConversationList() string {
|
||||
))
|
||||
sb.WriteString(item)
|
||||
currentOffset += tuiutil.Height(item)
|
||||
if i < len(m.App.Conversations)-1 {
|
||||
if i < len(m.App.Conversations.Items)-1 {
|
||||
sb.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user