actions_view.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. package ui
  2. import (
  3. "io"
  4. "net/http"
  5. "os"
  6. "path/filepath"
  7. "regexp"
  8. "github.com/atotto/clipboard"
  9. "github.com/diamondburned/arikawa/v3/discord"
  10. "github.com/rivo/tview"
  11. "github.com/skratchdot/open-golang/open"
  12. )
  13. var linkRegex = regexp.MustCompile("https?://.+")
  14. type ActionsView struct {
  15. *tview.List
  16. core *Core
  17. message *discord.Message
  18. }
  19. func newActionsView(c *Core, m *discord.Message) *ActionsView {
  20. v := &ActionsView{
  21. List: tview.NewList(),
  22. core: c,
  23. message: m,
  24. }
  25. v.ShowSecondaryText(false)
  26. v.SetDoneFunc(func() {
  27. c.App.SetRoot(c.View, true)
  28. c.App.SetFocus(c.MessagesView)
  29. })
  30. // If the client user has the `SEND_MESSAGES` permission, add "Reply" and "Mention Reply" actions.
  31. if channelIsInDMCategory(c.ChannelsView.selectedChannel) || hasPermission(c.State, c.ChannelsView.selectedChannel.ID, discord.PermissionSendMessages) {
  32. v.AddItem("Reply", "", 'r', v.replyAction)
  33. v.AddItem("Mention Reply", "", 'R', v.mentionReplyAction)
  34. }
  35. // If the referenced message exists, add a new action to select the reply.
  36. if m.ReferencedMessage != nil {
  37. v.AddItem("Select Reply", "", 'm', v.selectReplyAction)
  38. }
  39. // If the content of the message contains link(s), add the appropriate actions.
  40. links := linkRegex.FindAllString(m.Content, -1)
  41. if len(links) != 0 {
  42. v.AddItem("Open Link", "", 'l', func() {
  43. for _, l := range links {
  44. go open.Run(l)
  45. }
  46. c.App.SetRoot(c.View, true)
  47. c.App.SetFocus(c.MessagesView)
  48. })
  49. }
  50. // If the message contains attachments, add the appropriate actions to the actions view.
  51. if len(m.Attachments) != 0 {
  52. v.AddItem("Open Attachment", "", 'o', v.openAttachmentAction)
  53. v.AddItem("Download Attachment", "", 'd', v.downloadAttachmentAction)
  54. }
  55. // If the client user has the `MANAGE_MESSAGES` permission, add a new action to delete the message.
  56. if channelIsInDMCategory(c.ChannelsView.selectedChannel) || hasPermission(c.State, c.ChannelsView.selectedChannel.ID, discord.PermissionManageMessages) {
  57. v.AddItem("Delete", "", 'd', v.deleteAction)
  58. }
  59. v.AddItem("Copy Content", "", 'c', v.copyContentAction)
  60. v.AddItem("Copy ID", "", 'i', v.copyIDAction)
  61. v.AddItem("Copy Link", "", 'k', v.copyLinkAction)
  62. v.SetTitle("Press the Escape key to close")
  63. v.SetTitleAlign(tview.AlignLeft)
  64. v.SetBorder(true)
  65. v.SetBorderPadding(0, 0, 1, 1)
  66. return v
  67. }
  68. func (v *ActionsView) replyAction() {
  69. v.core.InputView.SetTitle("Replying to " + v.message.Author.Tag())
  70. v.core.App.SetRoot(v.core.View, true)
  71. v.core.App.SetFocus(v.core.InputView)
  72. }
  73. func (v *ActionsView) mentionReplyAction() {
  74. v.core.InputView.SetTitle("[@] Replying to " + v.message.Author.Tag())
  75. v.core.App.SetRoot(v.core.View, true)
  76. v.core.App.SetFocus(v.core.InputView)
  77. }
  78. func (v *ActionsView) selectReplyAction() {
  79. ms, err := v.core.State.Cabinet.Messages(v.message.ChannelID)
  80. if err != nil {
  81. return
  82. }
  83. v.core.MessagesView.selectedMessage, _ = findMessageByID(ms, v.message.ReferencedMessage.ID)
  84. v.core.MessagesView.
  85. Highlight(v.message.ReferencedMessage.ID.String()).
  86. ScrollToHighlight()
  87. v.core.App.SetRoot(v.core.View, true)
  88. v.core.App.SetFocus(v.core.MessagesView)
  89. }
  90. func (v *ActionsView) openAttachmentAction() {
  91. for _, a := range v.message.Attachments {
  92. cacheDirPath, _ := os.UserCacheDir()
  93. f, err := os.Create(filepath.Join(cacheDirPath, a.Filename))
  94. if err != nil {
  95. return
  96. }
  97. defer f.Close()
  98. resp, err := http.Get(a.URL)
  99. if err != nil {
  100. return
  101. }
  102. d, err := io.ReadAll(resp.Body)
  103. if err != nil {
  104. return
  105. }
  106. f.Write(d)
  107. go open.Run(f.Name())
  108. }
  109. v.core.App.SetRoot(v.core.View, true)
  110. v.core.App.SetFocus(v.core.MessagesView)
  111. }
  112. func (v *ActionsView) downloadAttachmentAction() {
  113. for _, a := range v.message.Attachments {
  114. path, err := os.UserHomeDir()
  115. if err != nil {
  116. path = os.TempDir()
  117. }
  118. path = filepath.Join(path, "Downloads", a.Filename)
  119. f, err := os.Create(path)
  120. if err != nil {
  121. return
  122. }
  123. defer f.Close()
  124. resp, err := http.Get(a.URL)
  125. if err != nil {
  126. return
  127. }
  128. d, err := io.ReadAll(resp.Body)
  129. if err != nil {
  130. return
  131. }
  132. f.Write(d)
  133. }
  134. v.core.App.SetRoot(v.core.View, true)
  135. v.core.App.SetFocus(v.core.MessagesView)
  136. }
  137. func (v *ActionsView) deleteAction() {
  138. v.core.MessagesView.Clear()
  139. err := v.core.State.MessageRemove(v.message.ChannelID, v.message.ID)
  140. if err != nil {
  141. return
  142. }
  143. err = v.core.State.DeleteMessage(v.message.ChannelID, v.message.ID, "Unknown")
  144. if err != nil {
  145. return
  146. }
  147. // The returned slice will be sorted from latest to oldest.
  148. ms, err := v.core.State.Cabinet.Messages(v.message.ChannelID)
  149. if err != nil {
  150. return
  151. }
  152. for i := len(ms) - 1; i >= 0; i-- {
  153. _, err = v.core.MessagesView.Write(buildMessage(v.core, ms[i]))
  154. if err != nil {
  155. return
  156. }
  157. }
  158. v.core.App.SetRoot(v.core.View, true)
  159. v.core.App.SetFocus(v.core.MessagesView)
  160. }
  161. func (v *ActionsView) copyContentAction() {
  162. err := clipboard.WriteAll(v.message.Content)
  163. if err != nil {
  164. return
  165. }
  166. v.core.App.SetRoot(v.core.View, true)
  167. v.core.App.SetFocus(v.core.MessagesView)
  168. }
  169. func (v *ActionsView) copyIDAction() {
  170. err := clipboard.WriteAll(v.message.ID.String())
  171. if err != nil {
  172. return
  173. }
  174. v.core.App.SetRoot(v.core.View, true)
  175. v.core.App.SetFocus(v.core.MessagesView)
  176. }
  177. func (v *ActionsView) copyLinkAction() {
  178. err := clipboard.WriteAll(v.message.URL())
  179. if err != nil {
  180. return
  181. }
  182. v.core.App.SetRoot(v.core.View, true)
  183. v.core.App.SetFocus(v.core.MessagesView)
  184. }