channels_picker.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package chat
  2. import (
  3. "log/slog"
  4. "strings"
  5. "github.com/ayn2op/discordo/internal/config"
  6. "github.com/ayn2op/discordo/internal/ui"
  7. "github.com/ayn2op/discordo/pkg/picker"
  8. "github.com/ayn2op/tview"
  9. "github.com/diamondburned/arikawa/v3/discord"
  10. )
  11. type channelsPicker struct {
  12. *picker.Picker
  13. chatView *View
  14. }
  15. func newChannelsPicker(cfg *config.Config, chatView *View) *channelsPicker {
  16. cp := &channelsPicker{picker.New(), chatView}
  17. cp.Box = ui.ConfigureBox(tview.NewBox(), &cfg.Theme)
  18. // When a child of tview.Flex is focused, tview.Flex 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.
  19. cp.
  20. SetBlurFunc(nil).
  21. SetFocusFunc(nil).
  22. SetBorderSet(cfg.Theme.Border.ActiveSet.BorderSet).
  23. SetBorderStyle(cfg.Theme.Border.ActiveStyle.Style).
  24. SetTitleStyle(cfg.Theme.Title.ActiveStyle.Style).
  25. SetFooterStyle(cfg.Theme.Footer.ActiveStyle.Style)
  26. cp.SetSelectedFunc(cp.onSelected)
  27. cp.SetTitle("Channels")
  28. cp.SetKeyMap(&picker.KeyMap{
  29. Cancel: cfg.Keys.Picker.Cancel,
  30. Up: cfg.Keys.Picker.Up,
  31. Down: cfg.Keys.Picker.Down,
  32. Top: cfg.Keys.Picker.Top,
  33. Bottom: cfg.Keys.Picker.Bottom,
  34. Select: cfg.Keys.Picker.Select,
  35. })
  36. return cp
  37. }
  38. func (cp *channelsPicker) onSelected(item picker.Item) {
  39. channelID, ok := item.Reference.(discord.ChannelID)
  40. if !ok || !channelID.IsValid() {
  41. return
  42. }
  43. channel, err := cp.chatView.state.Cabinet.Channel(channelID)
  44. if err != nil {
  45. slog.Error("failed to get channel from state", "err", err, "channel_id", channelID)
  46. return
  47. }
  48. node := cp.chatView.guildsTree.findNodeByChannelID(channel.ID)
  49. if node == nil {
  50. slog.Error("failed to locate channel in tree", "channel_id", channel.ID)
  51. return
  52. }
  53. cp.chatView.guildsTree.expandPathToNode(node)
  54. cp.chatView.guildsTree.SetCurrentNode(node)
  55. if channel.Type != discord.GuildCategory {
  56. cp.chatView.guildsTree.onSelected(node)
  57. }
  58. cp.chatView.closePicker()
  59. cp.chatView.focusMessageInput()
  60. }
  61. func (cp *channelsPicker) update() {
  62. cp.ClearItems()
  63. state := cp.chatView.state
  64. privateChannels, err := state.Cabinet.PrivateChannels()
  65. if err != nil {
  66. slog.Error("failed to get private channels from state", "err", err)
  67. return
  68. }
  69. ui.SortPrivateChannels(privateChannels)
  70. for _, channel := range privateChannels {
  71. cp.addChannel(nil, channel)
  72. }
  73. guilds, err := state.Cabinet.Guilds()
  74. if err != nil {
  75. slog.Error("failed to get guilds from state", "err", err)
  76. return
  77. }
  78. for _, guild := range guilds {
  79. channels, err := state.Cabinet.Channels(guild.ID)
  80. if err != nil {
  81. slog.Error("failed to get channels from state", "err", err, "guild_id", guild.ID)
  82. continue
  83. }
  84. for _, channel := range channels {
  85. cp.addChannel(&guild, channel)
  86. }
  87. }
  88. cp.Update()
  89. }
  90. func (cp *channelsPicker) addChannel(guild *discord.Guild, channel discord.Channel) {
  91. var b strings.Builder
  92. b.WriteString(ui.ChannelToString(channel, cp.chatView.cfg.Icons))
  93. if guild != nil {
  94. b.WriteString(" - ")
  95. b.WriteString(guild.Name)
  96. }
  97. name := b.String()
  98. cp.AddItem(picker.Item{Text: name, FilterText: name, Reference: channel.ID})
  99. }