diff --git a/cmd/message_input.go b/cmd/message_input.go index 53710e4..18afbe1 100644 --- a/cmd/message_input.go +++ b/cmd/message_input.go @@ -22,6 +22,7 @@ import ( "github.com/diamondburned/arikawa/v3/api" "github.com/diamondburned/arikawa/v3/discord" "github.com/diamondburned/arikawa/v3/state" + "github.com/diamondburned/arikawa/v3/utils/json/option" "github.com/diamondburned/arikawa/v3/utils/sendpart" "github.com/diamondburned/ningen/v3/discordmd" "github.com/gdamore/tcell/v2" @@ -39,6 +40,7 @@ type messageInput struct { *tview.TextArea cfg *config.Config + edit bool sendMessageData *api.SendMessageData cache *cache.Cache mentionsList *tview.List @@ -157,13 +159,33 @@ func (mi *messageInput) send() { return } - data := mi.sendMessageData - if text := strings.TrimSpace(mi.GetText()); text != "" { - data.Content = processText(app.guildsTree.selectedChannelID, []byte(text)) + text := strings.TrimSpace(mi.GetText()) + if text == "" { + return } - if _, err := discordState.SendMessageComplex(app.guildsTree.selectedChannelID, *data); err != nil { - slog.Error("failed to send message in channel", "channel_id", app.guildsTree.selectedChannelID, "err", err) + text = processText(app.guildsTree.selectedChannelID, []byte(text)) + if text == "" { + return + } + + if mi.edit { + m, err := app.messagesList.selectedMessage() + if err != nil { + slog.Error("failed to get selected message", "err", err) + return + } + + data := api.EditMessageData{Content: option.NewNullableString(text)} + if _, err := discordState.EditMessageComplex(m.ChannelID, m.ID, data); err != nil { + slog.Error("failed to edit message", "err", err) + } + } else { + data := mi.sendMessageData + data.Content = text + if _, err := discordState.SendMessageComplex(app.guildsTree.selectedChannelID, *data); err != nil { + slog.Error("failed to send message in channel", "channel_id", app.guildsTree.selectedChannelID, "err", err) + } } // Close the attached files after sending the message. diff --git a/cmd/messages_list.go b/cmd/messages_list.go index 3217292..71f7588 100644 --- a/cmd/messages_list.go +++ b/cmd/messages_list.go @@ -81,7 +81,7 @@ func (ml *messagesList) setTitle(channel discord.Channel) { title += " - " + topic } - app.messagesList.SetTitle(title) + ml.SetTitle(title) } func (ml *messagesList) drawMessages(messages []discord.Message) { @@ -139,20 +139,20 @@ func (ml *messagesList) drawTimestamps(ts discord.Timestamp) { fmt.Fprintf(ml, "[::d]%s[::D] ", ml.formatTimestamp(ts)) } -func (ml *messagesList) drawAuthor(msg discord.Message) { - name := msg.Author.DisplayOrUsername() +func (ml *messagesList) drawAuthor(message discord.Message) { + name := message.Author.DisplayOrUsername() foreground := tcell.ColorDefault - if msg.GuildID.IsValid() { - member, err := discordState.Cabinet.Member(msg.GuildID, msg.Author.ID) + if message.GuildID.IsValid() { + member, err := discordState.Cabinet.Member(message.GuildID, message.Author.ID) if err != nil { - slog.Error("failed to get member from state", "guild_id", msg.GuildID, "member_id", msg.Author.ID, "err", err) + slog.Error("failed to get member from state", "guild_id", message.GuildID, "member_id", message.Author.ID, "err", err) } else { if member.Nick != "" { name = member.Nick } color, ok := state.MemberColor(member, func(id discord.RoleID) *discord.Role { - r, _ := discordState.Cabinet.Role(msg.GuildID, id) + r, _ := discordState.Cabinet.Role(message.GuildID, id) return r }) if ok { @@ -164,10 +164,10 @@ func (ml *messagesList) drawAuthor(msg discord.Message) { fmt.Fprintf(ml, "[%s::b]%s[-::B] ", foreground, name) } -func (ml *messagesList) drawContent(msg discord.Message) { - c := []byte(tview.Escape(msg.Content)) +func (ml *messagesList) drawContent(message discord.Message) { + c := []byte(tview.Escape(message.Content)) if app.cfg.Markdown { - ast := discordmd.ParseWithMessage(c, *discordState.Cabinet, &msg, false) + ast := discordmd.ParseWithMessage(c, *discordState.Cabinet, &message, false) markdown.DefaultRenderer.Render(ml, c, ast) } else { ml.Write(c) // write the content as is @@ -215,10 +215,10 @@ func (ml *messagesList) drawForwardedMessage(message discord.Message) { func (ml *messagesList) drawReplyMessage(message discord.Message) { // reply fmt.Fprintf(ml, "[::d]%s ", ml.cfg.Theme.MessagesList.ReplyIndicator) - if refMsg := message.ReferencedMessage; refMsg != nil { - refMsg.GuildID = message.GuildID - ml.drawAuthor(*refMsg) - ml.drawContent(*refMsg) + if m := message.ReferencedMessage; m != nil { + m.GuildID = message.GuildID + ml.drawAuthor(*m) + ml.drawContent(*m) } else { io.WriteString(ml, "Original message was deleted") } @@ -232,20 +232,20 @@ func (ml *messagesList) drawPinnedMessage(message discord.Message) { fmt.Fprintf(ml, "%s pinned a message", message.Author.DisplayOrUsername()) } -func (ml *messagesList) selectedMsg() (*discord.Message, error) { +func (ml *messagesList) selectedMessage() (*discord.Message, error) { if !ml.selectedMessageID.IsValid() { return nil, errors.New("no message is currently selected") } - msg, err := discordState.Cabinet.Message(app.guildsTree.selectedChannelID, ml.selectedMessageID) + m, err := discordState.Cabinet.Message(app.guildsTree.selectedChannelID, ml.selectedMessageID) if err != nil { return nil, fmt.Errorf("could not retrieve selected message: %w", err) } - return msg, nil + return m, nil } -func (ml *messagesList) selectedMsgIndex() (int, error) { +func (ml *messagesList) selectedMessageIndex() (int, error) { ms, err := discordState.Cabinet.Messages(app.guildsTree.selectedChannelID) if err != nil { return -1, err @@ -280,11 +280,14 @@ func (ml *messagesList) onInputCapture(event *tcell.EventKey) *tcell.EventKey { ml.reply(false) case ml.cfg.Keys.MessagesList.ReplyMention: ml.reply(true) + case ml.cfg.Keys.MessagesList.Edit: + ml.edit() case ml.cfg.Keys.MessagesList.Delete: ml.delete() case ml.cfg.Keys.MessagesList.DeleteConfirm: ml.confirmDelete() } + return nil } @@ -295,7 +298,7 @@ func (ml *messagesList) _select(name string) { return } - msgIdx, err := ml.selectedMsgIndex() + msgIdx, err := ml.selectedMessageIndex() if err != nil { slog.Error("failed to get selected message", "err", err) return @@ -355,7 +358,7 @@ func (ml *messagesList) onHighlighted(added, removed, remaining []string) { } func (ml *messagesList) yankID() { - msg, err := ml.selectedMsg() + msg, err := ml.selectedMessage() if err != nil { slog.Error("failed to get selected message", "err", err) return @@ -365,7 +368,7 @@ func (ml *messagesList) yankID() { } func (ml *messagesList) yankContent() { - msg, err := ml.selectedMsg() + msg, err := ml.selectedMessage() if err != nil { slog.Error("failed to get selected message", "err", err) return @@ -375,7 +378,7 @@ func (ml *messagesList) yankContent() { } func (ml *messagesList) yankURL() { - msg, err := ml.selectedMsg() + msg, err := ml.selectedMessage() if err != nil { slog.Error("failed to get selected message", "err", err) return @@ -385,7 +388,7 @@ func (ml *messagesList) yankURL() { } func (ml *messagesList) open() { - msg, err := ml.selectedMsg() + msg, err := ml.selectedMessage() if err != nil { slog.Error("failed to get selected message", "err", err) return @@ -525,7 +528,7 @@ func (ml *messagesList) openURL(url string) { } func (ml *messagesList) reply(mention bool) { - msg, err := ml.selectedMsg() + msg, err := ml.selectedMessage() if err != nil { slog.Error("failed to get selected message", "err", err) return @@ -558,6 +561,19 @@ func (ml *messagesList) reply(mention bool) { app.SetFocus(app.messageInput) } +func (ml *messagesList) edit() { + message, err := ml.selectedMessage() + if err != nil { + slog.Error("failed to get selected message", "err", err) + return + } + + app.messageInput.SetTitle("Editing") + app.messageInput.edit = true + app.messageInput.SetText(message.Content, true) + app.SetFocus(app.messageInput) +} + func (ml *messagesList) confirmDelete() { onChoice := func(choice string) { if choice == "Yes" { @@ -573,7 +589,7 @@ func (ml *messagesList) confirmDelete() { } func (ml *messagesList) delete() { - msg, err := ml.selectedMsg() + msg, err := ml.selectedMessage() if err != nil { slog.Error("failed to get selected message", "err", err) return diff --git a/internal/config/config.toml b/internal/config/config.toml index 845f93f..ca63fa5 100644 --- a/internal/config/config.toml +++ b/internal/config/config.toml @@ -69,6 +69,7 @@ reply = "Rune[r]" # Reply (with mention) to the selected message. reply_mention = "Rune[R]" cancel = "Esc" +edit = "Rune[e]" delete = "Rune[D]" delete_confirm = "Rune[d]" # Open the selected message's attachments or hyperlinks in the message diff --git a/internal/config/keys.go b/internal/config/keys.go index d7eaac6..c18e016 100644 --- a/internal/config/keys.go +++ b/internal/config/keys.go @@ -28,8 +28,6 @@ type ( SelectCurrent string `toml:"select_current"` YankID string `toml:"yank_id"` - CollapseAll string `toml:"collapse_all"` - ExpandAll string `toml:"expand_all"` CollapseParentNode string `toml:"collapse_parent_node"` MoveToParentNode string `toml:"move_to_parent_node"` } @@ -41,6 +39,7 @@ type ( ReplyMention string `toml:"reply_mention"` Cancel string `toml:"cancel"` + Edit string `toml:"edit"` Delete string `toml:"delete"` DeleteConfirm string `toml:"delete_confirm"` Open string `toml:"open"` diff --git a/internal/notifications/notifications.go b/internal/notifications/notifications.go index 7b590d8..54d0675 100644 --- a/internal/notifications/notifications.go +++ b/internal/notifications/notifications.go @@ -15,29 +15,29 @@ import ( "github.com/diamondburned/ningen/v3" ) -func Notify(state *ningen.State, msg *gateway.MessageCreateEvent, cfg *config.Config) error { +func Notify(state *ningen.State, message *gateway.MessageCreateEvent, cfg *config.Config) error { if !cfg.Notifications.Enabled || cfg.Status == discord.DoNotDisturbStatus { return nil } - mentions := state.MessageMentions(&msg.Message) + mentions := state.MessageMentions(&message.Message) if mentions == 0 { return nil } // Handle sent files - content := msg.Content - if msg.Content == "" && len(msg.Attachments) > 0 { - content = "Uploaded " + msg.Attachments[0].Filename + content := message.Content + if message.Content == "" && len(message.Attachments) > 0 { + content = "Uploaded " + message.Attachments[0].Filename } if content == "" { return nil } - title := msg.Author.DisplayOrUsername() + title := message.Author.DisplayOrUsername() - channel, err := state.Cabinet.Channel(msg.ChannelID) + channel, err := state.Cabinet.Channel(message.ChannelID) if err != nil { return fmt.Errorf("failed to get channel from state: %w", err) } @@ -48,19 +48,19 @@ func Notify(state *ningen.State, msg *gateway.MessageCreateEvent, cfg *config.Co return fmt.Errorf("failed to get guild from state: %w", err) } - if member := msg.Member; member != nil && member.Nick != "" { + if member := message.Member; member != nil && member.Nick != "" { title = member.Nick } title += " (#" + channel.Name + ", " + guild.Name + ")" } - hash := msg.Author.Avatar + hash := message.Author.Avatar if hash == "" { hash = "default" } - imagePath, err := getCachedProfileImage(hash, msg.Author.AvatarURLWithType(discord.PNGImage)) + imagePath, err := getCachedProfileImage(hash, message.Author.AvatarURLWithType(discord.PNGImage)) if err != nil { slog.Info("failed to get profile image from cache for notification", "err", err, "hash", hash) }