input_view.go 3.0 KB

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