feat(chat): style DM channel nodes based on recipients presence (#784)

This commit is contained in:
ayn2op 2026-03-29 21:53:38 -04:00 • committed by GitHub
parent 5cbcaa75e0
commit 48f3b5316c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 49 additions and 9 deletions

View file

@ -197,6 +197,12 @@ auto_expand_folders = true
graphics = true
graphics_color = "default"
# Direct-message recipient presence styles.
online_style = { foreground = "green" }
idle_style = { foreground = "yellow" }
dnd_style = { foreground = "red" }
offline_style = { foreground = "gray" }
# Guilds tree indentation for different item types
[theme.guilds_tree.indents]
dm = 2

View file

@ -209,6 +209,11 @@ type (
Graphics bool `toml:"graphics"`
GraphicsColor string `toml:"graphics_color"`
Indents GuildsTreeIndents `toml:"indents"`
OnlineStyle StyleWrapper `toml:"online_style"`
IdleStyle StyleWrapper `toml:"idle_style"`
DNDStyle StyleWrapper `toml:"dnd_style"`
OfflineStyle StyleWrapper `toml:"offline_style"`
}
GuildsTreeIndents struct {

View file

@ -182,14 +182,37 @@ func (gt *guildsTree) unreadStyle(indication ningen.UnreadIndication) tcell.Styl
return style
}
func (gt *guildsTree) getGuildNodeStyle(guildID discord.GuildID) tcell.Style {
func (gt *guildsTree) guildNodeStyle(guildID discord.GuildID) tcell.Style {
indication := gt.chat.state.GuildIsUnread(guildID, ningen.GuildUnreadOpts{UnreadOpts: ningen.UnreadOpts{IncludeMutedCategories: true}})
return gt.unreadStyle(indication)
}
func (gt *guildsTree) getChannelNodeStyle(channelID discord.ChannelID) tcell.Style {
indication := gt.chat.state.ChannelIsUnread(channelID, ningen.UnreadOpts{IncludeMutedCategories: true})
return gt.unreadStyle(indication)
func (gt *guildsTree) channelNodeStyle(channel discord.Channel) tcell.Style {
unread := gt.unreadStyle(gt.chat.state.ChannelIsUnread(channel.ID, ningen.UnreadOpts{IncludeMutedCategories: true}))
if channel.Type != discord.DirectMessage || len(channel.DMRecipients) != 1 {
return unread
}
recipient := channel.DMRecipients[0]
presence, err := gt.chat.state.Cabinet.Presence(discord.NullGuildID, recipient.ID)
if err != nil {
return ui.MergeStyle(gt.dmStatusStyle(discord.OfflineStatus), unread)
}
return ui.MergeStyle(gt.dmStatusStyle(presence.Status), unread)
}
func (gt *guildsTree) dmStatusStyle(status discord.Status) tcell.Style {
switch status {
case discord.DoNotDisturbStatus:
return gt.cfg.Theme.GuildsTree.DNDStyle.Style
case discord.IdleStatus:
return gt.cfg.Theme.GuildsTree.IdleStyle.Style
case discord.OnlineStatus:
return gt.cfg.Theme.GuildsTree.OnlineStyle.Style
default:
return gt.cfg.Theme.GuildsTree.OfflineStyle.Style
}
}
func (gt *guildsTree) createGuildNode(n *tview.TreeNode, guild discord.Guild) {
@ -198,7 +221,7 @@ func (gt *guildsTree) createGuildNode(n *tview.TreeNode, guild discord.Guild) {
SetExpandable(true).
SetExpanded(false).
SetIndent(gt.cfg.Theme.GuildsTree.Indents.Guild)
gt.setNodeLineStyle(guildNode, gt.getGuildNodeStyle(guild.ID))
gt.setNodeLineStyle(guildNode, gt.guildNodeStyle(guild.ID))
n.AddChild(guildNode)
gt.guildNodeByID[guild.ID] = guildNode
}
@ -209,7 +232,7 @@ func (gt *guildsTree) createChannelNode(node *tview.TreeNode, channel discord.Ch
}
channelNode := tview.NewTreeNode(ui.ChannelToString(channel, gt.cfg.Icons, gt.chat.state)).SetReference(channel.ID)
gt.setNodeLineStyle(channelNode, gt.getChannelNodeStyle(channel.ID))
gt.setNodeLineStyle(channelNode, gt.channelNodeStyle(channel))
switch channel.Type {
case discord.DirectMessage:
channelNode.SetIndent(gt.cfg.Theme.GuildsTree.Indents.DM)
@ -226,7 +249,6 @@ func (gt *guildsTree) createChannelNode(node *tview.TreeNode, channel discord.Ch
}
node.AddChild(channelNode)
gt.channelNodeByID[channel.ID] = channelNode
gt.setNodeLineStyle(channelNode, gt.getChannelNodeStyle(channel.ID))
}
func (gt *guildsTree) setNodeLineStyle(node *tview.TreeNode, style tcell.Style) {

View file

@ -10,6 +10,7 @@ import (
"github.com/diamondburned/arikawa/v3/gateway"
"github.com/diamondburned/arikawa/v3/utils/httputil/httpdriver"
"github.com/diamondburned/arikawa/v3/utils/ws"
"github.com/diamondburned/ningen/v3"
"github.com/diamondburned/ningen/v3/states/read"
"github.com/gdamore/tcell/v3"
)
@ -206,13 +207,19 @@ func (m *Model) onReadUpdate(event *read.UpdateEvent) {
// event. This runs frequently while reading/typing across channels.
if event.GuildID.IsValid() {
if guildNode := m.guildsTree.findNodeByReference(event.GuildID); guildNode != nil {
m.guildsTree.setNodeLineStyle(guildNode, m.guildsTree.getGuildNodeStyle(event.GuildID))
m.guildsTree.setNodeLineStyle(guildNode, m.guildsTree.guildNodeStyle(event.GuildID))
}
}
// Channel style is always updated for the target channel regardless of
// whether it's in a guild or DM.
if channelNode := m.guildsTree.findNodeByReference(event.ChannelID); channelNode != nil {
m.guildsTree.setNodeLineStyle(channelNode, m.guildsTree.getChannelNodeStyle(event.ChannelID))
channel, err := m.state.Cabinet.Channel(event.ChannelID)
if err != nil {
indication := m.state.ChannelIsUnread(event.ChannelID, ningen.UnreadOpts{IncludeMutedCategories: true})
m.guildsTree.setNodeLineStyle(channelNode, m.guildsTree.unreadStyle(indication))
return
}
m.guildsTree.setNodeLineStyle(channelNode, m.guildsTree.channelNodeStyle(*channel))
}
}