channels_tree.go 3.6 KB

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