message_input.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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 MessageInput struct {
  15. *tview.InputField
  16. app *App
  17. }
  18. func NewMessageInput(app *App) *MessageInput {
  19. mi := &MessageInput{
  20. InputField: tview.NewInputField(),
  21. app: app,
  22. }
  23. mi.SetFieldBackgroundColor(tview.Styles.PrimitiveBackgroundColor)
  24. mi.SetPlaceholder("Message...")
  25. mi.SetPlaceholderStyle(tcell.StyleDefault.Background(tview.Styles.PrimitiveBackgroundColor))
  26. mi.SetInputCapture(mi.onInputCapture)
  27. mi.SetTitleAlign(tview.AlignLeft)
  28. mi.SetBorder(true)
  29. mi.SetBorderPadding(0, 0, 1, 1)
  30. return mi
  31. }
  32. func (mi *MessageInput) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
  33. switch e.Name() {
  34. case "Enter":
  35. if mi.app.ChannelsTree.SelectedChannel == nil {
  36. return nil
  37. }
  38. t := strings.TrimSpace(mi.app.MessageInputField.GetText())
  39. if t == "" {
  40. return nil
  41. }
  42. ms, err := mi.app.State.Messages(mi.app.ChannelsTree.SelectedChannel.ID, mi.app.Config.MessagesLimit)
  43. if err != nil {
  44. return nil
  45. }
  46. if len(mi.app.MessagesPanel.GetHighlights()) != 0 {
  47. mID, err := discord.ParseSnowflake(mi.app.MessagesPanel.GetHighlights()[0])
  48. if err != nil {
  49. return nil
  50. }
  51. _, m := findMessageByID(ms, discord.MessageID(mID))
  52. d := api.SendMessageData{
  53. Content: t,
  54. Reference: m.Reference,
  55. AllowedMentions: &api.AllowedMentions{RepliedUser: option.False},
  56. }
  57. // If the title of the message InputField widget has "[@]" as a prefix, send the message as a reply and mention the replied user.
  58. if strings.HasPrefix(mi.app.MessageInputField.GetTitle(), "[@]") {
  59. d.AllowedMentions.RepliedUser = option.True
  60. }
  61. go mi.app.State.SendMessageComplex(m.ChannelID, d)
  62. mi.app.MessagesPanel.SelectedMessage = -1
  63. mi.app.MessagesPanel.Highlight()
  64. mi.app.MessageInputField.SetTitle("")
  65. } else {
  66. go mi.app.State.SendMessage(mi.app.ChannelsTree.SelectedChannel.ID, t)
  67. }
  68. mi.app.MessageInputField.SetText("")
  69. return nil
  70. case "Ctrl+V":
  71. text, _ := clipboard.ReadAll()
  72. text = mi.app.MessageInputField.GetText() + text
  73. mi.app.MessageInputField.SetText(text)
  74. return nil
  75. case "Esc":
  76. mi.app.MessageInputField.
  77. SetText("").
  78. SetTitle("")
  79. mi.app.SetFocus(mi.app.MainFlex)
  80. mi.app.MessagesPanel.SelectedMessage = -1
  81. mi.app.MessagesPanel.Highlight()
  82. return nil
  83. case mi.app.Config.Keys.OpenExternalEditor:
  84. e := os.Getenv("EDITOR")
  85. if e == "" {
  86. return nil
  87. }
  88. f, err := os.CreateTemp(os.TempDir(), "discordo-*.md")
  89. if err != nil {
  90. return nil
  91. }
  92. defer os.Remove(f.Name())
  93. cmd := exec.Command(e, f.Name())
  94. cmd.Stdin = os.Stdin
  95. cmd.Stdout = os.Stdout
  96. mi.app.Suspend(func() {
  97. err = cmd.Run()
  98. if err != nil {
  99. return
  100. }
  101. })
  102. b, err := io.ReadAll(f)
  103. if err != nil {
  104. return nil
  105. }
  106. mi.app.MessageInputField.SetText(string(b))
  107. return nil
  108. }
  109. return e
  110. }