view.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. package root
  2. import (
  3. "fmt"
  4. "log/slog"
  5. "os"
  6. "github.com/ayn2op/discordo/internal/clipboard"
  7. "github.com/ayn2op/discordo/internal/config"
  8. "github.com/ayn2op/discordo/internal/consts"
  9. "github.com/ayn2op/discordo/internal/keyring"
  10. "github.com/ayn2op/discordo/internal/ui/chat"
  11. "github.com/ayn2op/discordo/internal/ui/login"
  12. "github.com/ayn2op/tview"
  13. "github.com/ayn2op/tview/keybind"
  14. "github.com/gdamore/tcell/v3"
  15. )
  16. const tokenEnvVarKey = "DISCORDO_TOKEN"
  17. type View struct {
  18. *tview.Box
  19. app *tview.Application
  20. inner tview.Primitive
  21. chat *chat.View
  22. cfg *config.Config
  23. }
  24. func NewView(cfg *config.Config) *View {
  25. tview.Styles = tview.Theme{}
  26. v := &View{
  27. Box: tview.NewBox(),
  28. app: tview.NewApplication(),
  29. cfg: cfg,
  30. }
  31. if err := clipboard.Init(); err != nil {
  32. slog.Error("failed to init clipboard", "err", err)
  33. }
  34. return v
  35. }
  36. func (v *View) Run() error {
  37. token := os.Getenv(tokenEnvVarKey)
  38. if token == "" {
  39. t, err := keyring.GetToken()
  40. if err != nil {
  41. slog.Info("failed to retrieve token from keyring", "err", err)
  42. }
  43. token = t
  44. }
  45. screen, err := tcell.NewScreen()
  46. if err != nil {
  47. return fmt.Errorf("failed to create screen: %w", err)
  48. }
  49. if err := screen.Init(); err != nil {
  50. return fmt.Errorf("failed to init screen: %w", err)
  51. }
  52. if v.cfg.Mouse {
  53. screen.EnableMouse()
  54. }
  55. screen.SetTitle(consts.Name)
  56. screen.EnablePaste()
  57. screen.EnableFocus()
  58. v.app.SetScreen(screen)
  59. v.app.SetRoot(v)
  60. if token == "" {
  61. v.showLoginView()
  62. } else {
  63. if err := v.showChatView(token); err != nil {
  64. return err
  65. }
  66. }
  67. v.app.SetFocus(v)
  68. err = v.app.Run()
  69. v.closeChatViewState()
  70. return err
  71. }
  72. func (v *View) showLoginView() {
  73. loginForm := login.NewForm(v.app, v.cfg, func(token string) {
  74. if err := v.showChatView(token); err != nil {
  75. slog.Error("failed to show chat view", "err", err)
  76. }
  77. })
  78. v.inner = loginForm
  79. v.app.SetFocus(v)
  80. }
  81. func (v *View) showChatView(token string) error {
  82. v.chat = chat.NewView(v.app, v.cfg, v.showLoginView)
  83. if err := v.chat.OpenState(token); err != nil {
  84. return err
  85. }
  86. v.inner = v.chat
  87. v.app.SetFocus(v)
  88. return nil
  89. }
  90. func (v *View) closeChatViewState() {
  91. if v.chat != nil {
  92. if err := v.chat.CloseState(); err != nil {
  93. slog.Error("failed to close the session", "err", err)
  94. }
  95. v.chat = nil
  96. }
  97. }
  98. func (v *View) Draw(screen tcell.Screen) {
  99. if v.inner == nil {
  100. return
  101. }
  102. x, y, width, height := v.GetRect()
  103. v.inner.SetRect(x, y, width, height)
  104. v.inner.Draw(screen)
  105. }
  106. func (v *View) HandleEvent(event tcell.Event) tview.Command {
  107. switch event := event.(type) {
  108. case *tview.KeyEvent:
  109. switch {
  110. case keybind.Matches(event, v.cfg.Keybinds.Suspend.Keybind):
  111. v.suspend()
  112. return tview.ConsumeEventCommand{}
  113. case keybind.Matches(event, v.cfg.Keybinds.Quit.Keybind):
  114. v.closeChatViewState()
  115. return tview.QuitCommand{}
  116. }
  117. if v.inner != nil {
  118. return v.inner.HandleEvent(event)
  119. }
  120. case *tview.MouseEvent, *tview.PasteEvent:
  121. if v.inner != nil {
  122. return v.inner.HandleEvent(event)
  123. }
  124. }
  125. return nil
  126. }
  127. func (v *View) Focus(delegate func(p tview.Primitive)) {
  128. if v.inner != nil {
  129. delegate(v.inner)
  130. return
  131. }
  132. v.Box.Focus(delegate)
  133. }
  134. func (v *View) HasFocus() bool {
  135. if v.inner != nil && v.inner.HasFocus() {
  136. return true
  137. }
  138. return v.Box.HasFocus()
  139. }
  140. func (v *View) Blur() {
  141. if v.inner != nil {
  142. v.inner.Blur()
  143. }
  144. v.Box.Blur()
  145. }