channels_tree.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package ui
  2. import (
  3. "sort"
  4. "github.com/diamondburned/arikawa/v3/discord"
  5. "github.com/rivo/tview"
  6. lua "github.com/yuin/gopher-lua"
  7. )
  8. type ChannelsTree struct {
  9. *tview.TreeView
  10. SelectedChannel *discord.Channel
  11. core *Core
  12. }
  13. func NewChannelsTree(c *Core) *ChannelsTree {
  14. ct := &ChannelsTree{
  15. TreeView: tview.NewTreeView(),
  16. core: c,
  17. }
  18. ct.SetRoot(tview.NewTreeNode(""))
  19. ct.SetTopLevel(1)
  20. ct.SetSelectedFunc(ct.onSelected)
  21. ct.SetTitle("Channels")
  22. ct.SetTitleAlign(tview.AlignLeft)
  23. ct.SetBorder(true)
  24. ct.SetBorderPadding(0, 0, 1, 1)
  25. return ct
  26. }
  27. func (ct *ChannelsTree) onSelected(node *tview.TreeNode) {
  28. ct.SelectedChannel = nil
  29. ct.core.MessagesPanel.SelectedMessage = -1
  30. ct.core.MessagesPanel.
  31. Highlight().
  32. Clear().
  33. SetTitle("")
  34. ct.core.MessageInput.SetText("")
  35. ref := node.GetReference()
  36. c, err := ct.core.State.Cabinet.Channel(ref.(discord.ChannelID))
  37. if err != nil {
  38. return
  39. }
  40. // If the channel is a category channel, expand the selected node if it is collapsed, otherwise collapse.
  41. if c.Type == discord.GuildCategory {
  42. node.SetExpanded(!node.IsExpanded())
  43. return
  44. }
  45. ct.SelectedChannel = c
  46. ct.core.Application.SetFocus(ct.core.MessageInput)
  47. title := channelToString(*c)
  48. if c.Topic != "" {
  49. title += " - " + parseMarkdown(c.Topic)
  50. }
  51. ct.core.MessagesPanel.SetTitle(title)
  52. go func() {
  53. messagesLimit := ct.core.Config.State.GetGlobal("messagesLimit")
  54. // The returned slice will be sorted from latest to oldest.
  55. ms, err := ct.core.State.Messages(c.ID, uint(lua.LVAsNumber(messagesLimit)))
  56. if err != nil {
  57. return
  58. }
  59. for i := len(ms) - 1; i >= 0; i-- {
  60. _, err = ct.core.MessagesPanel.Write(buildMessage(ct.core, ms[i]))
  61. if err != nil {
  62. return
  63. }
  64. }
  65. ct.core.MessagesPanel.ScrollToEnd()
  66. }()
  67. }
  68. func (ct *ChannelsTree) createChannelNode(c discord.Channel) *tview.TreeNode {
  69. channelNode := tview.NewTreeNode(channelToString(c))
  70. channelNode.SetReference(c.ID)
  71. return channelNode
  72. }
  73. func (ct *ChannelsTree) createPrivateChannelNodes(rootNode *tview.TreeNode) {
  74. cs, err := ct.core.State.Cabinet.PrivateChannels()
  75. if err != nil {
  76. return
  77. }
  78. sort.Slice(cs, func(i, j int) bool {
  79. return cs[i].LastMessageID > cs[j].LastMessageID
  80. })
  81. for _, c := range cs {
  82. rootNode.AddChild(ct.createChannelNode(c))
  83. }
  84. }
  85. func (ct *ChannelsTree) createGuildChannelNodes(rootNode *tview.TreeNode, gID discord.GuildID) {
  86. cs, err := ct.core.State.Cabinet.Channels(gID)
  87. if err != nil {
  88. return
  89. }
  90. sort.Slice(cs, func(i, j int) bool {
  91. return cs[i].Position < cs[j].Position
  92. })
  93. ct.createOrphanChannelNodes(rootNode, cs)
  94. ct.createCategoryChannelNodes(rootNode, cs)
  95. ct.createChildrenChannelNodes(rootNode, cs)
  96. }
  97. func (ct *ChannelsTree) createOrphanChannelNodes(rootNode *tview.TreeNode, cs []discord.Channel) {
  98. for _, c := range cs {
  99. if (c.Type == discord.GuildText || c.Type == discord.GuildNews) && (!c.ParentID.IsValid()) {
  100. rootNode.AddChild(ct.createChannelNode(c))
  101. }
  102. }
  103. }
  104. func (ct *ChannelsTree) createCategoryChannelNodes(rootNode *tview.TreeNode, cs []discord.Channel) {
  105. CATEGORY:
  106. for _, c := range cs {
  107. if c.Type == discord.GuildCategory {
  108. for _, nestedChannel := range cs {
  109. if nestedChannel.ParentID == c.ID {
  110. rootNode.AddChild(ct.createChannelNode(c))
  111. continue CATEGORY
  112. }
  113. }
  114. rootNode.AddChild(ct.createChannelNode(c))
  115. }
  116. }
  117. }
  118. func (ct *ChannelsTree) createChildrenChannelNodes(rootNode *tview.TreeNode, cs []discord.Channel) {
  119. for _, c := range cs {
  120. if (c.Type == discord.GuildText || c.Type == discord.GuildNews) && (c.ParentID.IsValid()) {
  121. var parentNode *tview.TreeNode
  122. rootNode.Walk(func(node, _ *tview.TreeNode) bool {
  123. if node.GetReference() == c.ParentID {
  124. parentNode = node
  125. return false
  126. }
  127. return true
  128. })
  129. if parentNode != nil {
  130. parentNode.AddChild(ct.createChannelNode(c))
  131. }
  132. }
  133. }
  134. }