application.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. package ui
  2. import (
  3. "context"
  4. "fmt"
  5. "log"
  6. "runtime"
  7. "strings"
  8. "github.com/ayn2op/discordo/config"
  9. "github.com/diamondburned/arikawa/v3/api"
  10. "github.com/diamondburned/arikawa/v3/discord"
  11. "github.com/diamondburned/arikawa/v3/gateway"
  12. "github.com/diamondburned/arikawa/v3/state"
  13. "github.com/gdamore/tcell/v2"
  14. "github.com/rivo/tview"
  15. )
  16. func init() {
  17. tview.Borders.TopLeftFocus = tview.Borders.TopLeft
  18. tview.Borders.TopRightFocus = tview.Borders.TopRight
  19. tview.Borders.BottomLeftFocus = tview.Borders.BottomLeft
  20. tview.Borders.BottomRightFocus = tview.Borders.BottomRight
  21. tview.Borders.HorizontalFocus = tview.Borders.Horizontal
  22. tview.Borders.VerticalFocus = tview.Borders.Vertical
  23. tview.Borders.TopLeft = 0
  24. tview.Borders.TopRight = 0
  25. tview.Borders.BottomLeft = 0
  26. tview.Borders.BottomRight = 0
  27. tview.Borders.Horizontal = 0
  28. tview.Borders.Vertical = 0
  29. api.UserAgent = fmt.Sprintf("%s/%s %s/%s", config.Name, "0.1", "arikawa", "v3")
  30. gateway.DefaultIdentity = gateway.IdentifyProperties{
  31. OS: runtime.GOOS,
  32. Browser: config.Name,
  33. Device: "",
  34. }
  35. }
  36. // Application is responsible for initialization and management of the application, widgets, configuration, and state.
  37. type Application struct {
  38. *tview.Application
  39. view *View
  40. config *config.Config
  41. state *state.State
  42. }
  43. func NewApplication(cfg *config.Config) *Application {
  44. app := &Application{
  45. Application: tview.NewApplication(),
  46. config: cfg,
  47. }
  48. app.EnableMouse(app.config.Mouse)
  49. app.SetBeforeDrawFunc(app.onBeforeDraw)
  50. // The styles must be assigned before initializing a new view.
  51. tview.Styles.PrimitiveBackgroundColor = tcell.GetColor(cfg.Theme.Background)
  52. tview.Styles.BorderColor = tcell.GetColor(cfg.Theme.Border)
  53. tview.Styles.TitleColor = tcell.GetColor(cfg.Theme.Title)
  54. app.view = newView(app)
  55. return app
  56. }
  57. func (app *Application) Run(token string) {
  58. if token != "" {
  59. if err := app.Connect(token); err != nil {
  60. log.Fatal(err)
  61. }
  62. app.SetRoot(app.view, true)
  63. app.SetFocus(app.view.GuildsTree)
  64. } else {
  65. loginView := newLoginForm(app)
  66. app.SetRoot(loginView, true)
  67. }
  68. if err := app.Application.Run(); err != nil {
  69. log.Fatal(err)
  70. }
  71. }
  72. func (app *Application) Connect(token string) error {
  73. app.state = state.New(token)
  74. app.state.AddHandler(app.onReady)
  75. app.state.AddHandler(app.onGuildCreate)
  76. app.state.AddHandler(app.onGuildDelete)
  77. app.state.AddHandler(app.onMessageCreate)
  78. return app.state.Open(context.Background())
  79. }
  80. func (app *Application) onBeforeDraw(screen tcell.Screen) bool {
  81. if app.config.Theme.Background == "default" {
  82. screen.Clear()
  83. }
  84. return false
  85. }
  86. func (c *Application) onReady(r *gateway.ReadyEvent) {
  87. root := c.view.GuildsTree.GetRoot()
  88. for _, gf := range r.UserSettings.GuildFolders {
  89. if gf.ID == 0 {
  90. for _, gID := range gf.GuildIDs {
  91. g, err := c.state.Cabinet.Guild(gID)
  92. if err != nil {
  93. log.Println(err)
  94. continue
  95. }
  96. guildNode := tview.NewTreeNode(g.Name)
  97. guildNode.SetReference(g.ID)
  98. root.AddChild(guildNode)
  99. }
  100. } else {
  101. var b strings.Builder
  102. if gf.Color != discord.NullColor {
  103. b.WriteByte('[')
  104. b.WriteString(gf.Color.String())
  105. b.WriteByte(']')
  106. } else {
  107. b.WriteString("[#ED4245]")
  108. }
  109. if gf.Name != "" {
  110. b.WriteString(gf.Name)
  111. } else {
  112. b.WriteString("Folder")
  113. }
  114. b.WriteString("[-]")
  115. folderNode := tview.NewTreeNode(b.String())
  116. root.AddChild(folderNode)
  117. for _, gID := range gf.GuildIDs {
  118. g, err := c.state.Cabinet.Guild(gID)
  119. if err != nil {
  120. log.Println(err)
  121. continue
  122. }
  123. guildNode := tview.NewTreeNode(g.Name)
  124. guildNode.SetReference(g.ID)
  125. folderNode.AddChild(guildNode)
  126. }
  127. }
  128. }
  129. c.view.GuildsTree.SetCurrentNode(root)
  130. c.SetFocus(c.view.GuildsTree)
  131. }
  132. func (c *Application) onGuildCreate(g *gateway.GuildCreateEvent) {
  133. guildNode := tview.NewTreeNode(g.Name)
  134. guildNode.SetReference(g.ID)
  135. rootNode := c.view.GuildsTree.GetRoot()
  136. rootNode.AddChild(guildNode)
  137. c.view.GuildsTree.SetCurrentNode(rootNode)
  138. c.SetFocus(c.view.GuildsTree)
  139. c.Draw()
  140. }
  141. func (c *Application) onGuildDelete(g *gateway.GuildDeleteEvent) {
  142. rootNode := c.view.GuildsTree.GetRoot()
  143. var parentNode *tview.TreeNode
  144. rootNode.Walk(func(node, _ *tview.TreeNode) bool {
  145. if node.GetReference() == g.ID {
  146. parentNode = node
  147. return false
  148. }
  149. return true
  150. })
  151. if parentNode != nil {
  152. rootNode.RemoveChild(parentNode)
  153. }
  154. c.Draw()
  155. }
  156. func (c *Application) onMessageCreate(m *gateway.MessageCreateEvent) {
  157. if c.view.ChannelsTree.selected != nil && m.ChannelID == c.view.ChannelsTree.selected.ID {
  158. _, err := c.view.MessagesText.Write(buildMessage(c, m.Message))
  159. if err != nil {
  160. return
  161. }
  162. if len(c.view.MessagesText.GetHighlights()) == 0 {
  163. c.view.MessagesText.ScrollToEnd()
  164. }
  165. }
  166. }