view.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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) InputHandler(event *tcell.EventKey) tview.Command {
  107. switch {
  108. case keybind.Matches(event, v.cfg.Keybinds.Suspend.Keybind):
  109. v.suspend()
  110. return tview.ConsumeEventCommand{}
  111. case keybind.Matches(event, v.cfg.Keybinds.Quit.Keybind):
  112. v.closeChatViewState()
  113. return tview.QuitCommand{}
  114. }
  115. if v.inner != nil {
  116. return v.inner.InputHandler(event)
  117. }
  118. return nil
  119. }
  120. func (v *View) Focus(delegate func(p tview.Primitive)) {
  121. if v.inner != nil {
  122. delegate(v.inner)
  123. return
  124. }
  125. v.Box.Focus(delegate)
  126. }
  127. func (v *View) HasFocus() bool {
  128. if v.inner != nil && v.inner.HasFocus() {
  129. return true
  130. }
  131. return v.Box.HasFocus()
  132. }
  133. func (v *View) Blur() {
  134. if v.inner != nil {
  135. v.inner.Blur()
  136. }
  137. v.Box.Blur()
  138. }
  139. func (v *View) MouseHandler(action tview.MouseAction, event *tcell.EventMouse) (tview.Primitive, tview.Command) {
  140. if v.inner == nil {
  141. return nil, nil
  142. }
  143. return v.inner.MouseHandler(action, event)
  144. }
  145. func (v *View) PasteHandler(text string) tview.Command {
  146. if v.inner == nil {
  147. return nil
  148. }
  149. return v.inner.PasteHandler(text)
  150. }