refactor(chat): adapt to read-only list view

This commit is contained in:
ayn2op 2026-08-07 17:40:54 -04:00
parent 8a64c33ce5
commit 7d66f3b20b
3 changed files with 11 additions and 36 deletions

2
go.mod
View file

@ -10,7 +10,7 @@ require (
github.com/andybalholm/brotli v1.2.2
github.com/ayn2op/arikawa/v3 v3.0.0-20260708075022-9cc760fe507e
github.com/ayn2op/ningen/v3 v3.0.1-0.20260708075321-0ba1ffc70bd6
github.com/ayn2op/tview v0.0.0-20260807061323-573f4b04d4a8
github.com/ayn2op/tview v0.0.0-20260807213801-ef1b0c0bb239
github.com/bogdanfinn/fhttp v0.6.8
github.com/bogdanfinn/tls-client v1.15.1
github.com/gdamore/tcell/v3 v3.4.1

4
go.sum
View file

@ -14,8 +14,8 @@ github.com/ayn2op/arikawa/v3 v3.0.0-20260708075022-9cc760fe507e h1:rsMifRXwxkbha
github.com/ayn2op/arikawa/v3 v3.0.0-20260708075022-9cc760fe507e/go.mod h1:gqklpHK7WHYTzpd1ZY6Ho/p9GURBghzBCzRiBYpaUP8=
github.com/ayn2op/ningen/v3 v3.0.1-0.20260708075321-0ba1ffc70bd6 h1:/khHm6G+GWwXpmwA8bJoaXoUhubqMEUG6spjZsnklIw=
github.com/ayn2op/ningen/v3 v3.0.1-0.20260708075321-0ba1ffc70bd6/go.mod h1:vqLIvETLkjc2vIgQzVEBqpGNCrgNe7YJqS9TUAJsXRI=
github.com/ayn2op/tview v0.0.0-20260807061323-573f4b04d4a8 h1:IZVthAp7XM3Z3EZX6mjtgbJUMVzJiYpJcU+5PN9k1J4=
github.com/ayn2op/tview v0.0.0-20260807061323-573f4b04d4a8/go.mod h1:bJZdl/FfspPLEW3nuX6ubt7YbmDoSj7f2IoWH37w1NE=
github.com/ayn2op/tview v0.0.0-20260807213801-ef1b0c0bb239 h1:xxtbWv2Rp8DvHu9MSd0ChChR0mRifSQVlzYb3QFIwR4=
github.com/ayn2op/tview v0.0.0-20260807213801-ef1b0c0bb239/go.mod h1:bJZdl/FfspPLEW3nuX6ubt7YbmDoSj7f2IoWH37w1NE=
github.com/bdandy/go-errors v1.2.2 h1:WdFv/oukjTJCLa79UfkGmwX7ZxONAihKu4V0mLIs11Q=
github.com/bdandy/go-errors v1.2.2/go.mod h1:NkYHl4Fey9oRRdbB1CoC6e84tuqQHiqrOcZpqFEkBxM=
github.com/bdandy/go-socks4 v1.2.3 h1:Q6Y2heY1GRjCtHbmlKfnwrKVU/k81LS8mRGLRlmDlic=

View file

@ -46,10 +46,7 @@ type messagesList struct {
cfg *config.Config
chat *Model
messages []discord.Message
// rows is the virtual list model rendered by tview (message rows +
// date-separator rows). It is rebuilt lazily when rowsDirty is true.
rows []messagesListRow
rowsDirty bool
rows []messagesListRow
renderer *markdown.Renderer
// itemByID caches rendered message TextViews.
@ -106,7 +103,6 @@ func newMessagesList(cfg *config.Config, chat *Model) *messagesList {
func (ml *messagesList) reset() {
ml.messages = nil
ml.rows = nil
ml.rowsDirty = false
clear(ml.itemByID)
ml.
Clear().
@ -126,17 +122,14 @@ func (ml *messagesList) setTitle(channel discord.Channel) {
func (ml *messagesList) setMessages(messages []discord.Message) {
ml.messages = slices.Clone(messages)
slices.Reverse(ml.messages)
ml.invalidateRows()
// New channel payload / refetch: replace the cache wholesale to keep it in
// lockstep with the current message slice.
clear(ml.itemByID)
ml.rebuildRows()
}
func (ml *messagesList) addMessage(message discord.Message) {
ml.messages = append(ml.messages, message)
ml.invalidateRows()
// Defensive invalidation for ID reuse/edits delivered out-of-order.
delete(ml.itemByID, message.ID)
ml.rebuildRows()
}
func (ml *messagesList) setMessage(index int, message discord.Message) {
@ -146,7 +139,7 @@ func (ml *messagesList) setMessage(index int, message discord.Message) {
ml.messages[index] = message
delete(ml.itemByID, message.ID)
ml.invalidateRows()
ml.rebuildRows()
}
func (ml *messagesList) deleteMessage(index int) {
@ -156,7 +149,7 @@ func (ml *messagesList) deleteMessage(index int) {
delete(ml.itemByID, ml.messages[index].ID)
ml.messages = append(ml.messages[:index], ml.messages[index+1:]...)
ml.invalidateRows()
ml.rebuildRows()
}
func (ml *messagesList) clearSelection() {
@ -164,8 +157,6 @@ func (ml *messagesList) clearSelection() {
}
func (ml *messagesList) buildItem(index int) list.Item {
ml.ensureRows()
if index < 0 || index >= len(ml.rows) {
return nil
}
@ -248,21 +239,7 @@ func (ml *messagesList) rebuildRows() {
}
ml.rows = rows
ml.rowsDirty = false
}
func (ml *messagesList) invalidateRows() {
ml.rowsDirty = true
}
// ensureRows lazily rebuilds list rows. This avoids repeated O(n) row rebuild
// work when multiple message mutations happen close together.
func (ml *messagesList) ensureRows() {
if !ml.rowsDirty {
return
}
ml.rebuildRows()
ml.SetBuilder(ml.buildItem)
}
func sameLocalDate(a discord.Timestamp, b discord.Timestamp) bool {
@ -273,7 +250,6 @@ func sameLocalDate(a discord.Timestamp, b discord.Timestamp) bool {
// Cursor returns the selected message index, skipping separator rows.
func (ml *messagesList) Cursor() int {
ml.ensureRows()
rowIndex := ml.Model.Cursor()
if rowIndex < 0 || rowIndex >= len(ml.rows) {
return -1
@ -292,7 +268,6 @@ func (ml *messagesList) SetCursor(index int) {
}
func (ml *messagesList) messageToRowIndex(messageIndex int) int {
ml.ensureRows()
if messageIndex < 0 || messageIndex >= len(ml.messages) {
return -1
}
@ -307,7 +282,6 @@ func (ml *messagesList) messageToRowIndex(messageIndex int) int {
}
func (ml *messagesList) onRowCursorChanged(rowIndex int) {
ml.ensureRows()
if rowIndex < 0 || rowIndex >= len(ml.rows) || ml.rows[rowIndex].kind == messagesListRowMessage {
return
}
@ -927,7 +901,7 @@ func (ml *messagesList) Update(msg tview.Msg) tview.Cmd {
delete(ml.itemByID, message.ID)
}
ml.messages = slices.Concat(msg.Older, ml.messages)
ml.invalidateRows()
ml.rebuildRows()
switch {
case prevCursor == 0:
@ -1378,6 +1352,7 @@ func (ml *messagesList) requestGuildMembers(guildID discord.GuildID, messages []
func (ml *messagesList) invalidateRenderedMessages() {
clear(ml.itemByID)
ml.SetBuilder(ml.buildItem)
}
func (ml *messagesList) ShortHelp() []keybind.Keybind {