Snapshot mutable tree display state

This commit is contained in:
Brandon McGinty (chatgpt)
2026-08-10 10:27:12 -04:00
committed by Brandon McGinty
parent 461f172035
commit b407a20132
3 changed files with 66 additions and 20 deletions
+4
View File
@@ -16,6 +16,10 @@ import (
type TreeItem struct { type TreeItem struct {
User *gumble.User User *gumble.User
Channel *gumble.Channel Channel *gumble.Channel
display string
userSession uint32
channelID uint32
snapshot bool
} }
type Barnard struct { type Barnard struct {
+44 -18
View File
@@ -8,6 +8,9 @@ import (
) )
func (ti TreeItem) String() string { func (ti TreeItem) String() string {
if ti.display != "" {
return ti.display
}
if ti.User != nil { if ti.User != nil {
if ti.User.LocallyMuted() { if ti.User.LocallyMuted() {
return "[MUTED] " + esc(ti.User.Name) return "[MUTED] " + esc(ti.User.Name)
@@ -94,7 +97,13 @@ func (b *Barnard) TreeItemBuild(item uiterm.TreeItem) []uiterm.TreeItem {
if root == nil { if root == nil {
return nil return nil
} }
return []uiterm.TreeItem{TreeItem{Channel: root}} var display string
var channelID uint32
b.Client.Do(func() {
display = "#" + esc(root.Name)
channelID = root.ID
})
return []uiterm.TreeItem{TreeItem{Channel: root, display: display, channelID: channelID, snapshot: true}}
} else { } else {
treeItem = ti treeItem = ti
} }
@@ -104,41 +113,52 @@ func (b *Barnard) TreeItemBuild(item uiterm.TreeItem) []uiterm.TreeItem {
} }
users := []uiterm.TreeItem{} users := []uiterm.TreeItem{}
ul := []*gumble.User{} type userDisplay struct {
cl := []*gumble.Channel{} user *gumble.User
display string
name string
session uint32
}
type channelDisplay struct {
channel *gumble.Channel
name string
id uint32
}
ul := []userDisplay{}
cl := []channelDisplay{}
// TCP handlers mutate both maps; snapshot them while Client.Do holds its // TCP handlers mutate both maps; snapshot them while Client.Do holds its
// read lock, then sort/render outside the protocol critical section. // read lock, then sort/render outside the protocol critical section.
b.Client.Do(func() { b.Client.Do(func() {
for _, user := range treeItem.Channel.Users { for _, user := range treeItem.Channel.Users {
ul = append(ul, user) boostPercent := float32(user.Boost()-1) * 10
totalVolume := user.Volume()*100 + boostPercent
display := fmt.Sprintf("%s [%.0f%%]", esc(user.Name), totalVolume)
if user.LocallyMuted() {
display = "[MUTED] " + display
}
ul = append(ul, userDisplay{user: user, name: user.Name, session: user.Session, display: display})
} }
for _, subchannel := range treeItem.Channel.Children { for _, subchannel := range treeItem.Channel.Children {
cl = append(cl, subchannel) cl = append(cl, channelDisplay{channel: subchannel, name: subchannel.Name, id: subchannel.ID})
} }
}) })
sort.Slice(ul, func(i, j int) bool { sort.Slice(ul, func(i, j int) bool {
return ul[i].Name < ul[j].Name return ul[i].name < ul[j].name
}) })
for _, user := range ul { for _, user := range ul {
users = append(users, TreeItem{ users = append(users, TreeItem{User: user.user, display: user.display, userSession: user.session, snapshot: true})
User: user,
})
} }
channels := []uiterm.TreeItem{} channels := []uiterm.TreeItem{}
sort.Slice(cl, func(i, j int) bool { sort.Slice(cl, func(i, j int) bool {
return cl[i].Name < cl[j].Name return cl[i].name < cl[j].name
}) })
for _, subchannel := range cl { for _, subchannel := range cl {
displayName := subchannel.Name displayName := "#" + esc(subchannel.name)
if b.isChannelMuted(subchannel.ID) { if b.isChannelMuted(subchannel.id) {
displayName = "[MUTED] #" + displayName displayName = "[MUTED] " + displayName
} else {
displayName = "#" + displayName
} }
channels = append(channels, TreeItem{ channels = append(channels, TreeItem{Channel: subchannel.channel, display: displayName, channelID: subchannel.id, snapshot: true})
Channel: subchannel,
})
} }
return append(users, channels...) return append(users, channels...)
@@ -158,9 +178,15 @@ func sameUserChannelTreeItem(previous, current uiterm.TreeItem) bool {
return false return false
} }
if prev.User != nil && cur.User != nil { if prev.User != nil && cur.User != nil {
if prev.snapshot && cur.snapshot {
return prev.userSession == cur.userSession
}
return prev.User.Session == cur.User.Session return prev.User.Session == cur.User.Session
} }
if prev.Channel != nil && cur.Channel != nil { if prev.Channel != nil && cur.Channel != nil {
if prev.snapshot && cur.snapshot {
return prev.channelID == cur.channelID
}
return prev.Channel.ID == cur.Channel.ID return prev.Channel.ID == cur.Channel.ID
} }
return false return false
+16
View File
@@ -0,0 +1,16 @@
package main
import (
"testing"
"git.stormux.org/storm/barnard/gumble/gumble"
)
func TestTreeItemUsesCapturedDisplaySnapshot(t *testing.T) {
user := &gumble.User{Name: "before"}
item := TreeItem{User: user, display: "before [100%]"}
user.Name = "after"
if got := item.String(); got != "before [100%]" {
t.Fatalf("tree display read mutable user state: %q", got)
}
}