package chat import ( "log/slog" "strings" "github.com/ayn2op/discordo/internal/config" "github.com/ayn2op/discordo/internal/ui" "github.com/ayn2op/discordo/pkg/picker" "github.com/ayn2op/tview" "github.com/ayn2op/tview/help" "github.com/ayn2op/tview/keybind" "github.com/diamondburned/arikawa/v3/discord" "github.com/gdamore/tcell/v3" ) type channelsPicker struct { *picker.Model chatView *Model } var _ help.KeyMap = (*channelsPicker)(nil) func newChannelsPicker(cfg *config.Config, chatView *Model) *channelsPicker { cp := &channelsPicker{picker.NewModel(), chatView} cp.Box = ui.ConfigureBox(tview.NewBox(), &cfg.Theme) // When a child of the parent flex is focused, the parent layout itself is not reported as focused. // Instead, the focused child (picker) is considered focused. // Therefore, we manually set the active border style on the picker to ensure it displays the correct focused appearance. cp. SetBlurFunc(nil). SetFocusFunc(nil). SetBorderSet(cfg.Theme.Border.ActiveSet.BorderSet). SetBorderStyle(cfg.Theme.Border.ActiveStyle.Style). SetTitleStyle(cfg.Theme.Title.ActiveStyle.Style). SetFooterStyle(cfg.Theme.Footer.ActiveStyle.Style) cp.SetTitle("Channels") cp.SetScrollBarVisibility(cfg.Theme.ScrollBar.Visibility.ScrollBarVisibility) cp.SetScrollBar(tview.NewScrollBar(). SetTrackStyle(cfg.Theme.ScrollBar.TrackStyle.Style). SetThumbStyle(cfg.Theme.ScrollBar.ThumbStyle.Style). SetGlyphSet(cfg.Theme.ScrollBar.GlyphSet.GlyphSet)) cp.SetKeyMap(&picker.KeyMap{ Cancel: cfg.Keybinds.Picker.Cancel.Keybind, Up: cfg.Keybinds.Picker.Up.Keybind, Down: cfg.Keybinds.Picker.Down.Keybind, Top: cfg.Keybinds.Picker.Top.Keybind, Bottom: cfg.Keybinds.Picker.Bottom.Keybind, Select: cfg.Keybinds.Picker.Select.Keybind, }) return cp } func (cp *channelsPicker) HandleEvent(event tcell.Event) tview.Command { switch event := event.(type) { case *picker.SelectedEvent: channelID, ok := event.Reference.(discord.ChannelID) if !ok || !channelID.IsValid() { return nil } channel, err := cp.chatView.state.Cabinet.Channel(channelID) if err != nil { slog.Error("failed to get channel from state", "err", err, "channel_id", channelID) return nil } node := cp.chatView.guildsTree.findNodeByChannelID(channel.ID) if node == nil { slog.Error("failed to locate channel in tree", "channel_id", channel.ID) return nil } cp.chatView.guildsTree.expandPathToNode(node) cp.chatView.guildsTree.SetCurrentNode(node) if channel.Type != discord.GuildCategory { cp.chatView.guildsTree.onSelected(node) } cp.chatView.closePicker() cp.chatView.focusMessageInput() return nil case *picker.CancelEvent: cp.chatView.closePicker() return nil } return cp.Model.HandleEvent(event) } func (cp *channelsPicker) update() { cp.ClearItems() state := cp.chatView.state privateChannels, err := state.Cabinet.PrivateChannels() if err != nil { slog.Error("failed to get private channels from state", "err", err) return } ui.SortPrivateChannels(privateChannels) for _, channel := range privateChannels { cp.addChannel(nil, channel) } guilds, err := state.Cabinet.Guilds() if err != nil { slog.Error("failed to get guilds from state", "err", err) return } for _, guild := range guilds { channels, err := state.Cabinet.Channels(guild.ID) if err != nil { slog.Error("failed to get channels from state", "err", err, "guild_id", guild.ID) continue } for _, channel := range channels { cp.addChannel(&guild, channel) } } cp.Update() } func (cp *channelsPicker) addChannel(guild *discord.Guild, channel discord.Channel) { var b strings.Builder b.WriteString(ui.ChannelToString(channel, cp.chatView.cfg.Icons, cp.chatView.state)) if guild != nil { b.WriteString(" - ") b.WriteString(guild.Name) } name := b.String() cp.AddItem(picker.Item{Text: name, FilterText: name, Reference: channel.ID}) } func (cp *channelsPicker) ShortHelp() []keybind.Keybind { cfg := cp.chatView.cfg.Keybinds.Picker return []keybind.Keybind{cfg.Up.Keybind, cfg.Down.Keybind, cfg.Select.Keybind, cfg.Cancel.Keybind} } func (cp *channelsPicker) FullHelp() [][]keybind.Keybind { cfg := cp.chatView.cfg.Keybinds.Picker return [][]keybind.Keybind{ {cfg.Up.Keybind, cfg.Down.Keybind, cfg.Top.Keybind, cfg.Bottom.Keybind}, {cfg.Select.Keybind, cfg.Cancel.Keybind}, } }