input_view.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package ui
  2. import (
  3. "io"
  4. "log"
  5. "os"
  6. "os/exec"
  7. "strings"
  8. "github.com/atotto/clipboard"
  9. "github.com/diamondburned/arikawa/v3/api"
  10. "github.com/diamondburned/arikawa/v3/discord"
  11. "github.com/diamondburned/arikawa/v3/utils/json/option"
  12. "github.com/gdamore/tcell/v2"
  13. "github.com/rivo/tview"
  14. )
  15. type InputView struct {
  16. *tview.InputField
  17. core *Core
  18. }
  19. func newInputView(c *Core) *InputView {
  20. v := &InputView{
  21. InputField: tview.NewInputField(),
  22. core: c,
  23. }
  24. v.SetFieldBackgroundColor(tview.Styles.PrimitiveBackgroundColor)
  25. v.SetPlaceholder("Message...")
  26. v.SetPlaceholderStyle(tcell.StyleDefault.Background(tview.Styles.PrimitiveBackgroundColor))
  27. v.SetInputCapture(v.inputCapture)
  28. v.SetTitleAlign(tview.AlignLeft)
  29. v.SetBorder(true)
  30. v.SetBorderPadding(0, 0, 1, 1)
  31. return v
  32. }
  33. func (v *InputView) inputCapture(event *tcell.EventKey) *tcell.EventKey {
  34. switch event.Name() {
  35. case "Enter":
  36. return v.sendMessage()
  37. case v.core.Config.Keys.InputView.OpenExternalEditor:
  38. return v.openExternalEditor()
  39. case v.core.Config.Keys.InputView.PasteClipboard:
  40. return v.pasteClipboard()
  41. case "Esc":
  42. v.
  43. SetText("").
  44. SetTitle("")
  45. v.core.MessagesView.selectedMessage = -1
  46. v.core.MessagesView.Highlight()
  47. return nil
  48. }
  49. return event
  50. }
  51. func (v *InputView) sendMessage() *tcell.EventKey {
  52. if v.core.ChannelsView.selectedChannel == nil {
  53. return nil
  54. }
  55. t := strings.TrimSpace(v.GetText())
  56. if t == "" {
  57. return nil
  58. }
  59. ms, err := v.core.State.Messages(v.core.ChannelsView.selectedChannel.ID, v.core.Config.MessagesLimit)
  60. if err != nil {
  61. log.Println(err)
  62. return nil
  63. }
  64. if len(v.core.MessagesView.GetHighlights()) != 0 {
  65. mID, err := discord.ParseSnowflake(v.core.MessagesView.GetHighlights()[0])
  66. if err != nil {
  67. log.Println(err)
  68. return nil
  69. }
  70. _, m := findMessageByID(ms, discord.MessageID(mID))
  71. d := api.SendMessageData{
  72. Content: t,
  73. Reference: &discord.MessageReference{
  74. MessageID: m.ID,
  75. },
  76. AllowedMentions: &api.AllowedMentions{RepliedUser: option.False},
  77. }
  78. // If the title of the input view has "[@]" as a prefix, send the message as a reply and mention the replied user.
  79. if strings.HasPrefix(v.GetTitle(), "[@]") {
  80. d.AllowedMentions.RepliedUser = option.True
  81. }
  82. go v.core.State.SendMessageComplex(m.ChannelID, d)
  83. v.core.MessagesView.selectedMessage = -1
  84. v.core.MessagesView.Highlight()
  85. v.SetTitle("")
  86. } else {
  87. go v.core.State.SendMessage(v.core.ChannelsView.selectedChannel.ID, t)
  88. }
  89. v.SetText("")
  90. return nil
  91. }
  92. func (v *InputView) pasteClipboard() *tcell.EventKey {
  93. text, err := clipboard.ReadAll()
  94. if err != nil {
  95. log.Println(err)
  96. return nil
  97. }
  98. text = v.GetText() + text
  99. v.SetText(text)
  100. return nil
  101. }
  102. func (v *InputView) openExternalEditor() *tcell.EventKey {
  103. e := os.Getenv("EDITOR")
  104. if e == "" {
  105. log.Println("environment variable EDITOR is empty")
  106. return nil
  107. }
  108. f, err := os.CreateTemp(os.TempDir(), "discordo-*.md")
  109. if err != nil {
  110. log.Println(err)
  111. return nil
  112. }
  113. defer os.Remove(f.Name())
  114. cmd := exec.Command(e, f.Name())
  115. cmd.Stdin = os.Stdin
  116. cmd.Stdout = os.Stdout
  117. v.core.App.Suspend(func() {
  118. err = cmd.Run()
  119. if err != nil {
  120. log.Println(err)
  121. return
  122. }
  123. })
  124. b, err := io.ReadAll(f)
  125. if err != nil {
  126. log.Println(err)
  127. return nil
  128. }
  129. v.SetText(string(b))
  130. return nil
  131. }