view.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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. type View struct {
  17. *tview.Box
  18. app *tview.Application
  19. inner tview.Primitive
  20. chat *chat.View
  21. cfg *config.Config
  22. }
  23. func NewView(cfg *config.Config) *View {
  24. tview.Styles = tview.Theme{}
  25. v := &View{
  26. Box: tview.NewBox(),
  27. app: tview.NewApplication(),
  28. cfg: cfg,
  29. }
  30. if err := clipboard.Init(); err != nil {
  31. slog.Error("failed to init clipboard", "err", err)
  32. }
  33. return v
  34. }
  35. func (v *View) Run() error {
  36. token := os.Getenv("DISCORDO_TOKEN")
  37. if token == "" {
  38. t, err := keyring.GetToken()
  39. if err != nil {
  40. slog.Info("failed to retrieve token from keyring", "err", err)
  41. }
  42. token = t
  43. }
  44. screen, err := tcell.NewScreen()
  45. if err != nil {
  46. return fmt.Errorf("failed to create screen: %w", err)
  47. }
  48. if err := screen.Init(); err != nil {
  49. return fmt.Errorf("failed to init screen: %w", err)
  50. }
  51. if v.cfg.Mouse {
  52. screen.EnableMouse()
  53. }
  54. screen.SetTitle(consts.Name)
  55. screen.EnablePaste()
  56. screen.EnableFocus()
  57. v.app.SetScreen(screen)
  58. v.app.SetRoot(v)
  59. if token == "" {
  60. v.showLoginView()
  61. } else {
  62. if err := v.showChatView(token); err != nil {
  63. return err
  64. }
  65. }
  66. v.app.SetFocus(v)
  67. err = v.app.Run()
  68. v.closeChatViewState()
  69. return err
  70. }
  71. func (v *View) showLoginView() {
  72. loginForm := login.NewForm(v.app, v.cfg, func(token string) {
  73. if err := v.showChatView(token); err != nil {
  74. slog.Error("failed to show chat view", "err", err)
  75. }
  76. })
  77. v.inner = loginForm
  78. v.MarkDirty()
  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.MarkDirty()
  88. v.app.SetFocus(v)
  89. return nil
  90. }
  91. func (v *View) quit() {
  92. v.closeChatViewState()
  93. v.app.Stop()
  94. }
  95. func (v *View) closeChatViewState() {
  96. if v.chat != nil {
  97. if err := v.chat.CloseState(); err != nil {
  98. slog.Error("failed to close the session", "err", err)
  99. }
  100. v.chat = nil
  101. }
  102. }
  103. func (v *View) Draw(screen tcell.Screen) {
  104. if v.inner == nil {
  105. return
  106. }
  107. x, y, width, height := v.GetRect()
  108. v.inner.SetRect(x, y, width, height)
  109. v.inner.Draw(screen)
  110. }
  111. func (v *View) InputHandler(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
  112. switch {
  113. case keybind.Matches(event, v.cfg.Keybinds.Suspend.Keybind):
  114. v.suspend()
  115. return
  116. case keybind.Matches(event, v.cfg.Keybinds.Quit.Keybind):
  117. v.quit()
  118. return
  119. }
  120. if v.inner != nil {
  121. v.inner.InputHandler(event, setFocus)
  122. }
  123. }
  124. func (v *View) Focus(delegate func(p tview.Primitive)) {
  125. if v.inner != nil {
  126. delegate(v.inner)
  127. return
  128. }
  129. v.Box.Focus(delegate)
  130. }
  131. func (v *View) HasFocus() bool {
  132. if v.inner != nil && v.inner.HasFocus() {
  133. return true
  134. }
  135. return v.Box.HasFocus()
  136. }
  137. func (v *View) Blur() {
  138. if v.inner != nil {
  139. v.inner.Blur()
  140. }
  141. v.Box.Blur()
  142. }
  143. func (v *View) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
  144. return v.WrapMouseHandler(func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
  145. if v.inner == nil {
  146. return false, nil
  147. }
  148. handler := v.inner.MouseHandler()
  149. if handler == nil {
  150. return false, nil
  151. }
  152. return handler(action, event, setFocus)
  153. })
  154. }
  155. func (v *View) PasteHandler() func(text string, setFocus func(p tview.Primitive)) {
  156. return v.WrapPasteHandler(func(text string, setFocus func(p tview.Primitive)) {
  157. if v.inner == nil {
  158. return
  159. }
  160. handler := v.inner.PasteHandler()
  161. if handler == nil {
  162. return
  163. }
  164. handler(text, setFocus)
  165. })
  166. }
  167. func (v *View) IsDirty() bool {
  168. if v.Box.IsDirty() {
  169. return true
  170. }
  171. if v.inner == nil {
  172. return false
  173. }
  174. return v.inner.IsDirty()
  175. }
  176. func (v *View) MarkClean() {
  177. v.Box.MarkClean()
  178. if v.inner != nil {
  179. v.inner.MarkClean()
  180. }
  181. }