app.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. package ui
  2. import (
  3. "context"
  4. "strings"
  5. "github.com/ayntgl/discordo/config"
  6. "github.com/diamondburned/arikawa/v3/api"
  7. "github.com/diamondburned/arikawa/v3/discord"
  8. "github.com/diamondburned/arikawa/v3/gateway"
  9. "github.com/diamondburned/arikawa/v3/state"
  10. "github.com/gdamore/tcell/v2"
  11. "github.com/rivo/tview"
  12. )
  13. type App struct {
  14. *tview.Application
  15. MainFlex *tview.Flex
  16. GuildsTree *GuildsTree
  17. ChannelsTree *ChannelsTree
  18. MessagesTextView *MessagesTextView
  19. MessageInputField *MessageInput
  20. Config *config.Config
  21. State *state.State
  22. SelectedChannel *discord.Channel
  23. SelectedMessage int
  24. }
  25. func NewApp(token string, c *config.Config) *App {
  26. app := &App{
  27. Application: tview.NewApplication(),
  28. MainFlex: tview.NewFlex(),
  29. Config: c,
  30. State: state.NewWithIdentifier(gateway.NewIdentifier(gateway.IdentifyCommand{
  31. Token: token,
  32. Intents: nil,
  33. Properties: gateway.IdentifyProperties{
  34. OS: c.Identify.Os,
  35. Browser: c.Identify.Browser,
  36. },
  37. // The official client sets the compress field as false.
  38. Compress: false,
  39. })),
  40. SelectedMessage: -1,
  41. }
  42. app.GuildsTree = NewGuildsTree(app)
  43. app.ChannelsTree = NewChannelsTree(app)
  44. app.MessagesTextView = NewMessagesTextView(app)
  45. app.MessageInputField = NewMessageInput(app)
  46. app.EnableMouse(app.Config.Mouse)
  47. app.MainFlex.SetInputCapture(app.onInputCapture)
  48. return app
  49. }
  50. func (app *App) Connect() error {
  51. // For user accounts, all of the guilds, the user is in, are dispatched in the READY gateway event.
  52. // Whereas, for bot accounts, the guilds are dispatched discretely in the GUILD_CREATE gateway events.
  53. if !strings.HasPrefix(app.State.Token, "Bot") {
  54. api.UserAgent = app.Config.Identify.UserAgent
  55. app.State.AddHandler(app.onStateReady)
  56. }
  57. app.State.AddHandler(app.onStateGuildCreate)
  58. app.State.AddHandler(app.onStateGuildDelete)
  59. app.State.AddHandler(app.onStateMessageCreate)
  60. return app.State.Open(context.Background())
  61. }
  62. func (app *App) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
  63. if app.MessageInputField.HasFocus() {
  64. return e
  65. }
  66. if app.MainFlex.GetItemCount() != 0 {
  67. switch e.Name() {
  68. case app.Config.Keys.ToggleGuildsTree:
  69. app.SetFocus(app.GuildsTree)
  70. return nil
  71. case app.Config.Keys.ToggleChannelsTree:
  72. app.SetFocus(app.ChannelsTree)
  73. return nil
  74. case app.Config.Keys.ToggleMessagesTextView:
  75. app.SetFocus(app.MessagesTextView)
  76. return nil
  77. case app.Config.Keys.ToggleMessageInput:
  78. app.SetFocus(app.MessageInputField)
  79. return nil
  80. }
  81. }
  82. return e
  83. }
  84. func (app *App) DrawMainFlex() {
  85. leftFlex := tview.NewFlex().
  86. SetDirection(tview.FlexRow).
  87. AddItem(app.GuildsTree, 10, 1, false).
  88. AddItem(app.ChannelsTree, 0, 1, false)
  89. rightFlex := tview.NewFlex().
  90. SetDirection(tview.FlexRow).
  91. AddItem(app.MessagesTextView, 0, 1, false).
  92. AddItem(app.MessageInputField, 3, 1, false)
  93. app.MainFlex.
  94. AddItem(leftFlex, 0, 1, false).
  95. AddItem(rightFlex, 0, 4, false)
  96. }
  97. func (app *App) onStateReady(r *gateway.ReadyEvent) {
  98. rootNode := app.GuildsTree.GetRoot()
  99. for _, gf := range r.UserSettings.GuildFolders {
  100. if gf.ID == 0 {
  101. for _, gID := range gf.GuildIDs {
  102. g, err := app.State.Cabinet.Guild(gID)
  103. if err != nil {
  104. return
  105. }
  106. guildNode := tview.NewTreeNode(g.Name)
  107. guildNode.SetReference(g.ID)
  108. rootNode.AddChild(guildNode)
  109. }
  110. } else {
  111. var b strings.Builder
  112. if gf.Color != discord.NullColor {
  113. b.WriteByte('[')
  114. b.WriteString(gf.Color.String())
  115. b.WriteByte(']')
  116. } else {
  117. b.WriteString("[#ED4245]")
  118. }
  119. if gf.Name != "" {
  120. b.WriteString(gf.Name)
  121. } else {
  122. b.WriteString("Folder")
  123. }
  124. b.WriteString("[-]")
  125. folderNode := tview.NewTreeNode(b.String())
  126. rootNode.AddChild(folderNode)
  127. for _, gID := range gf.GuildIDs {
  128. g, err := app.State.Cabinet.Guild(gID)
  129. if err != nil {
  130. return
  131. }
  132. guildNode := tview.NewTreeNode(g.Name)
  133. guildNode.SetReference(g.ID)
  134. folderNode.AddChild(guildNode)
  135. }
  136. }
  137. }
  138. app.GuildsTree.SetCurrentNode(rootNode)
  139. app.SetFocus(app.GuildsTree)
  140. }
  141. func (app *App) onStateGuildCreate(g *gateway.GuildCreateEvent) {
  142. rootNode := app.GuildsTree.GetRoot()
  143. guildNode := tview.NewTreeNode(g.Name)
  144. guildNode.SetReference(g.ID)
  145. rootNode.AddChild(guildNode)
  146. app.Draw()
  147. }
  148. func (app *App) onStateGuildDelete(g *gateway.GuildDeleteEvent) {
  149. rootNode := app.GuildsTree.GetRoot()
  150. var parentNode *tview.TreeNode
  151. rootNode.Walk(func(node, _ *tview.TreeNode) bool {
  152. if node.GetReference() == g.ID {
  153. parentNode = node
  154. return false
  155. }
  156. return true
  157. })
  158. if parentNode != nil {
  159. rootNode.RemoveChild(parentNode)
  160. }
  161. app.Draw()
  162. }
  163. func (app *App) onStateMessageCreate(m *gateway.MessageCreateEvent) {
  164. if app.SelectedChannel != nil && app.SelectedChannel.ID == m.ChannelID {
  165. _, err := app.MessagesTextView.Write(buildMessage(app, m.Message))
  166. if err != nil {
  167. return
  168. }
  169. if len(app.MessagesTextView.GetHighlights()) == 0 {
  170. app.MessagesTextView.ScrollToEnd()
  171. }
  172. }
  173. }