discordo/internal/ui/chat/guildstree/msg.go
2026-10-11 18:16:53 -04:00

125 lines
3.2 KiB
Go

package guildstree
import (
"log/slog"
"github.com/ayn2op/arikawa/v3/discord"
"github.com/ayn2op/discordo/internal/ui"
"github.com/ayn2op/ningen/v3"
"github.com/ayn2op/tview"
"github.com/ayn2op/tview/tree"
)
// Msg moves the tree.
type Msg tree.Change
// SelectedMsg is sent when the user selects a node.
type SelectedMsg struct {
Node *tree.Node
}
type ChannelLoadedMsg struct {
Channel discord.Channel
Messages []discord.Message
}
type NavigateMsg struct{ ChannelID discord.ChannelID }
// VoiceMsg is sent when who is speaking or the state of the voice channel joined changes.
type VoiceMsg struct{}
// Init waits for that.
func (m Model) Init() tview.Cmd {
return func() tview.Msg {
<-m.voiceSession.Updates()
return VoiceMsg{}
}
}
func (m *Model) navigate(channelID discord.ChannelID) tview.Cmd {
channel, err := m.state.Cabinet.Channel(channelID)
if err != nil {
slog.Error("failed to get channel from state", "err", err, "channel_id", channelID)
return nil
}
node := m.findNodeByChannelID(channelID)
if node == nil {
slog.Error("failed to locate channel in tree", "channel_id", channelID)
return nil
}
m.expandPathToNode(node)
m.setCurrentNode(node)
if channel.Type == discord.GuildCategory {
return nil
}
return m.selectNode(node)
}
func (m Model) selectNode(node *tree.Node) tview.Cmd {
if m.expandable(node) {
node.SetExpanded(!node.Expanded())
return nil
}
switch ref := node.Reference().(type) {
case discord.GuildID:
go m.state.MemberState.Subscribe(ref)
channels, err := m.state.Cabinet.Channels(ref)
if err != nil {
slog.Error("failed to get channels", "err", err, "guild_id", ref)
return nil
}
ui.SortGuildChannels(channels)
m.createChannelNodes(node, channels)
m.showVoiceMembers(ref)
node.Expand()
case discord.ChannelID:
channel, err := m.state.Cabinet.Channel(ref)
if err != nil {
slog.Error("failed to get channel from state", "err", err, "channel_id", ref)
return nil
}
if channel.Type != discord.GuildForum {
return loadChannel(m.state, uint(m.cfg.MessagesLimit), *channel)
}
channels, err := m.state.Cabinet.Channels(channel.GuildID)
if err != nil {
slog.Error("failed to get channels for forum threads", "err", err, "guild_id", channel.GuildID)
return nil
}
for _, child := range channels {
if child.ParentID == channel.ID && isThread(child.Type) {
m.createChannelNode(node, child)
}
}
node.Expand()
case dmNode:
channels, err := m.state.PrivateChannels()
if err != nil {
slog.Error("failed to get private channels", "err", err)
return nil
}
ui.SortPrivateChannels(channels)
for _, channel := range channels {
m.createChannelNode(node, channel)
}
node.Expand()
}
return nil
}
func loadChannel(state *ningen.State, limit uint, channel discord.Channel) tview.Cmd {
return func() tview.Msg {
messages, err := state.Messages(channel.ID, limit)
if err != nil {
slog.Error("failed to get messages", "err", err, "channel_id", channel.ID, "limit", limit)
return nil
}
if lastMessageID := state.LastMessage(channel.ID); lastMessageID.IsValid() {
go state.ReadState.MarkRead(channel.ID, lastMessageID)
}
return ChannelLoadedMsg{Channel: channel, Messages: messages}
}
}