diff --git a/internal/ui/chat/message_input.go b/internal/ui/chat/message_input.go index 243cec9..9c3bdbc 100644 --- a/internal/ui/chat/message_input.go +++ b/internal/ui/chat/message_input.go @@ -122,8 +122,8 @@ func (mi *messageInput) Update(msg tview.Msg) tview.Cmd { mi.attach(imageAttachmentName, bytes.NewReader(msg)) return nil case filesPickedMsg: - selected := mi.chat.SelectedChannel() - if selected == nil || selected.ID != msg.channelID { + selectedChannel, ok := mi.chat.SelectedChannel() + if !ok || selectedChannel.ID != msg.channelID { return closeFiles(msg.files) } for _, file := range msg.files { @@ -198,11 +198,11 @@ type filesPickedMsg struct { } func (mi *messageInput) pickFiles() tview.Cmd { - selected := mi.chat.SelectedChannel() - if selected == nil { + selectedChannel, ok := mi.chat.SelectedChannel() + if !ok { return nil } - channelID := selected.ID + channelID := selectedChannel.ID return func() tview.Msg { paths, err := zenity.SelectFileMultiple() @@ -255,8 +255,8 @@ func (mi *messageInput) sendTyping() tview.Cmd { }) mi.typingTimerMu.Unlock() - selectedChannel := mi.chat.SelectedChannel() - if selectedChannel == nil { + selectedChannel, ok := mi.chat.SelectedChannel() + if !ok { return nil } channelID := selectedChannel.ID @@ -267,8 +267,8 @@ func (mi *messageInput) sendTyping() tview.Cmd { } func (mi *messageInput) send() tview.Cmd { - selected := mi.chat.SelectedChannel() - if selected == nil { + selectedChannel, ok := mi.chat.SelectedChannel() + if !ok { return nil } @@ -277,19 +277,18 @@ func (mi *messageInput) send() tview.Cmd { return nil } - text = mi.processText(selected, []byte(text)) + text = mi.processText(selectedChannel, []byte(text)) data := *mi.sendMessageData data.Files = slices.Clone(data.Files) var editMessage discord.Message edit := mi.edit if edit { - message, err := mi.chat.messagesList.selectedMessage() - if err != nil { - slog.Error("failed to get selected message", "err", err) + selectedMessage, ok := mi.chat.messagesList.selectedMessage() + if !ok { return nil } - editMessage = *message + editMessage = *selectedMessage } mi.stopTypingTimer() @@ -307,8 +306,8 @@ func (mi *messageInput) send() tview.Cmd { return nil } data.Content = text - if _, err := mi.chat.state.SendMessageComplex(selected.ID, data); err != nil { - slog.Error("failed to send message in channel", "channel_id", selected.ID, "err", err) + if _, err := mi.chat.state.SendMessageComplex(selectedChannel.ID, data); err != nil { + slog.Error("failed to send message in channel", "channel_id", selectedChannel.ID, "err", err) } return nil } @@ -396,15 +395,15 @@ func (mi *messageInput) tabComplete() tview.Cmd { } pos := posEnd - (len(name) + 1) - selected := mi.chat.SelectedChannel() - if selected == nil { + selectedChannel, ok := mi.chat.SelectedChannel() + if !ok { return nil } - gID := selected.GuildID + gID := selectedChannel.GuildID if mi.cfg.AutocompleteLimit == 0 { if !gID.IsValid() { - users := selected.DMRecipients + users := selectedChannel.DMRecipients res := fuzzy.FindFrom(name, userList(users)) if len(res) > 0 { mi.Replace(pos, posEnd, "@"+users[res[0].Index].Username+" ") @@ -419,7 +418,7 @@ func (mi *messageInput) tabComplete() tview.Cmd { res := fuzzy.FindFrom(name, memberList(members)) for _, r := range res { - if channelHasUser(mi.chat.state, selected.ID, members[r.Index].User.ID) { + if channelHasUser(mi.chat.state, selectedChannel.ID, members[r.Index].User.ID) { mi.Replace(pos, posEnd, "@"+members[r.Index].User.Username+" ") return cmd } @@ -431,7 +430,7 @@ func (mi *messageInput) tabComplete() tview.Cmd { if mi.mentionsList.itemCount() == 0 { return nil } - name, ok := mi.mentionsList.selectedInsertText() + name, ok = mi.mentionsList.selectedInsertText() if !ok { return nil } @@ -444,12 +443,12 @@ func (mi *messageInput) tabSuggest() tview.Cmd { if r != '@' { return mi.stopTabCompletion() } - selected := mi.chat.SelectedChannel() - if selected == nil { + selectedChannel, ok := mi.chat.SelectedChannel() + if !ok { return nil } - gID := selected.GuildID - cID := selected.ID + gID := selectedChannel.GuildID + cID := selectedChannel.ID mi.mentionsList.clear() var shown map[string]struct{} @@ -476,7 +475,7 @@ func (mi *messageInput) tabSuggest() tview.Cmd { mi.addMentionUser(&m.Author) } } else { - users := selected.DMRecipients + users := selectedChannel.DMRecipients me, _ := mi.chat.state.Cabinet.Me() users = append(users, *me) res := fuzzy.FindFrom(name, userList(users)) @@ -750,8 +749,8 @@ func (mi *messageInput) attach(name string, reader io.Reader) { } func (mi *messageInput) canAttachFiles() bool { - selected := mi.chat.SelectedChannel() - return selected != nil && mi.chat.state.HasPermissions(selected.ID, discord.PermissionAttachFiles) + selectedChannel, ok := mi.chat.SelectedChannel() + return ok && mi.chat.state.HasPermissions(selectedChannel.ID, discord.PermissionAttachFiles) } func (mi *messageInput) ShortHelp() []keybind.Keybind { diff --git a/internal/ui/chat/messages_list.go b/internal/ui/chat/messages_list.go index c882fed..97ee1af 100644 --- a/internal/ui/chat/messages_list.go +++ b/internal/ui/chat/messages_list.go @@ -2,7 +2,6 @@ package chat import ( "context" - "errors" "io" "log/slog" "net/http" @@ -841,17 +840,17 @@ func (ml *messagesList) drawPinnedMessage(builder *tview.LineBuilder, message di builder.Write(" pinned a message.", baseStyle) } -func (ml *messagesList) selectedMessage() (*discord.Message, error) { +func (ml *messagesList) selectedMessage() (*discord.Message, bool) { if len(ml.messages) == 0 { - return nil, errors.New("no messages available") + return nil, false } cursor := ml.Cursor() if cursor == -1 || cursor >= len(ml.messages) { - return nil, errors.New("no message is currently selected") + return nil, false } - return &ml.messages[cursor], nil + return &ml.messages[cursor], true } func (ml *messagesList) Update(msg tview.Msg) tview.Cmd { @@ -898,8 +897,8 @@ func (ml *messagesList) Update(msg tview.Msg) tview.Cmd { return ml.Model.Update(msg) case olderMessagesLoadedMsg: - selectedChannel := ml.chat.SelectedChannel() - if selectedChannel == nil || selectedChannel.ID != msg.ChannelID { + selectedChannel, ok := ml.chat.SelectedChannel() + if !ok || selectedChannel.ID != msg.ChannelID { return nil } prevCursor := ml.Cursor() @@ -999,8 +998,8 @@ func (ml *messagesList) selectReply() { } func (ml *messagesList) fetchOlderMessages() tview.Cmd { - selectedChannel := ml.chat.SelectedChannel() - if selectedChannel == nil { + selectedChannel, ok := ml.chat.SelectedChannel() + if !ok { return nil } @@ -1028,14 +1027,13 @@ func (ml *messagesList) fetchOlderMessages() tview.Cmd { } func (ml *messagesList) yankMessageID() tview.Cmd { - msg, err := ml.selectedMessage() - if err != nil { - slog.Error("failed to get selected message", "err", err) + selectedMessage, ok := ml.selectedMessage() + if !ok { return nil } return func() tview.Msg { - if err := clipboard.Write(clipboard.FmtText, []byte(msg.ID.String())); err != nil { + if err := clipboard.Write(clipboard.FmtText, []byte(selectedMessage.ID.String())); err != nil { slog.Error("failed to copy message id", "err", err) } return nil @@ -1043,14 +1041,13 @@ func (ml *messagesList) yankMessageID() tview.Cmd { } func (ml *messagesList) yankContent() tview.Cmd { - msg, err := ml.selectedMessage() - if err != nil { - slog.Error("failed to get selected message", "err", err) + selectedMessage, ok := ml.selectedMessage() + if !ok { return nil } return func() tview.Msg { - if err := clipboard.Write(clipboard.FmtText, []byte(msg.Content)); err != nil { + if err := clipboard.Write(clipboard.FmtText, []byte(selectedMessage.Content)); err != nil { slog.Error("failed to copy message content", "err", err) } return nil @@ -1058,14 +1055,13 @@ func (ml *messagesList) yankContent() tview.Cmd { } func (ml *messagesList) yankURL() tview.Cmd { - msg, err := ml.selectedMessage() - if err != nil { - slog.Error("failed to get selected message", "err", err) + selectedMessage, ok := ml.selectedMessage() + if !ok { return nil } return func() tview.Msg { - if err := clipboard.Write(clipboard.FmtText, []byte(msg.URL())); err != nil { + if err := clipboard.Write(clipboard.FmtText, []byte(selectedMessage.URL())); err != nil { slog.Error("failed to copy message url", "err", err) } return nil @@ -1073,23 +1069,22 @@ func (ml *messagesList) yankURL() tview.Cmd { } func (ml *messagesList) open() tview.Cmd { - msg, err := ml.selectedMessage() - if err != nil { - slog.Error("failed to get selected message", "err", err) + selectedMessage, ok := ml.selectedMessage() + if !ok { return nil } - urls := messageURLs(*msg) - switch total := len(urls) + len(msg.Attachments); { + urls := messageURLs(*selectedMessage) + switch total := len(urls) + len(selectedMessage.Attachments); { case total == 0: return nil case total > 1: - return ml.showAttachmentsList(urls, msg.Attachments) + return ml.showAttachmentsList(urls, selectedMessage.Attachments) case len(urls) == 1: return ml.openURL(urls[0]) } - attachment := msg.Attachments[0] + attachment := selectedMessage.Attachments[0] if strings.HasPrefix(attachment.ContentType, "image/") { return ml.openAttachment(attachment) } @@ -1247,19 +1242,18 @@ func (ml *messagesList) openURL(url string) tview.Cmd { } func (ml *messagesList) reply(mention bool) tview.Cmd { - message, err := ml.selectedMessage() - if err != nil { - slog.Error("failed to get selected message", "err", err) + selectedMessage, ok := ml.selectedMessage() + if !ok { return nil } - name := message.Author.DisplayOrUsername() - if member := ml.memberForMessage(*message); member != nil && member.Nick != "" { + name := selectedMessage.Author.DisplayOrUsername() + if member := ml.memberForMessage(*selectedMessage); member != nil && member.Nick != "" { name = member.Nick } data := ml.chat.messageInput.sendMessageData - data.Reference = &discord.MessageReference{MessageID: message.ID} + data.Reference = &discord.MessageReference{MessageID: selectedMessage.ID} data.AllowedMentions = &api.AllowedMentions{RepliedUser: option.False} title := "Replying to " @@ -1274,21 +1268,20 @@ func (ml *messagesList) reply(mention bool) tview.Cmd { } func (ml *messagesList) editSelectedMessage() tview.Cmd { - message, err := ml.selectedMessage() - if err != nil { - slog.Error("failed to get selected message", "err", err) + selectedMessage, ok := ml.selectedMessage() + if !ok { return nil } me, _ := ml.chat.state.Cabinet.Me() - if message.Author.ID != me.ID { - slog.Error("failed to edit message; not the author", "channel_id", message.ChannelID, "message_id", message.ID) + if selectedMessage.Author.ID != me.ID { + slog.Error("failed to edit message; not the author", "channel_id", selectedMessage.ChannelID, "message_id", selectedMessage.ID) return nil } ml.chat.messageInput.SetTitle("Editing") ml.chat.messageInput.edit = true - ml.chat.messageInput.SetText(message.Content, true) + ml.chat.messageInput.SetText(selectedMessage.Content, true) return tview.SetFocus(ml.chat.messageInput) } @@ -1309,9 +1302,8 @@ func (ml *messagesList) confirmDelete() { } func (ml *messagesList) deleteSelectedMessage() tview.Cmd { - selectedMessage, err := ml.selectedMessage() - if err != nil { - slog.Error("failed to get selected message", "err", err) + selectedMessage, ok := ml.selectedMessage() + if !ok { return nil } @@ -1409,9 +1401,9 @@ func (ml *messagesList) ShortHelp() []keybind.Keybind { cfg.Cancel.Keybind, } - if msg, err := ml.selectedMessage(); err == nil { + if selectedMessage, ok := ml.selectedMessage(); ok { me, _ := ml.chat.state.Cabinet.Me() - if msg.Author.ID != me.ID { + if selectedMessage.Author.ID != me.ID { help = append(help, cfg.Reply.Keybind) } } @@ -1427,17 +1419,17 @@ func (ml *messagesList) FullHelp() [][]keybind.Keybind { canEdit := false canDelete := false canOpen := false - if msg, err := ml.selectedMessage(); err == nil { - canSelectReply = msg.ReferencedMessage != nil - canOpen = len(messageURLs(*msg)) != 0 || len(msg.Attachments) != 0 + if selectedMessage, ok := ml.selectedMessage(); ok { + canSelectReply = selectedMessage.ReferencedMessage != nil + canOpen = len(messageURLs(*selectedMessage)) != 0 || len(selectedMessage.Attachments) != 0 me, _ := ml.chat.state.Cabinet.Me() - canReply = msg.Author.ID != me.ID - canEdit = msg.Author.ID == me.ID + canReply = selectedMessage.Author.ID != me.ID + canEdit = selectedMessage.Author.ID == me.ID canDelete = canEdit if !canDelete { - selected := ml.chat.SelectedChannel() - canDelete = selected != nil && ml.chat.state.HasPermissions(selected.ID, discord.PermissionManageMessages) + selectedChannel, ok := ml.chat.SelectedChannel() + canDelete = ok && ml.chat.state.HasPermissions(selectedChannel.ID, discord.PermissionManageMessages) } } diff --git a/internal/ui/chat/model.go b/internal/ui/chat/model.go index bd7ad41..bdd3c74 100644 --- a/internal/ui/chat/model.go +++ b/internal/ui/chat/model.go @@ -110,10 +110,10 @@ func NewModel(app *tview.Application, cfg *config.Config, token string) *Model { return m } -func (m *Model) SelectedChannel() *discord.Channel { +func (m *Model) SelectedChannel() (*discord.Channel, bool) { m.selectedChannelMu.RLock() defer m.selectedChannelMu.RUnlock() - return m.selectedChannel + return m.selectedChannel, m.selectedChannel != nil } func (m *Model) SetSelectedChannel(channel *discord.Channel) { @@ -415,8 +415,8 @@ func (m *Model) removeTyper(userID discord.UserID) { } func (m *Model) updateFooter() { - selectedChannel := m.SelectedChannel() - if selectedChannel == nil { + selectedChannel, ok := m.SelectedChannel() + if !ok { return } guildID := selectedChannel.GuildID diff --git a/internal/ui/chat/state.go b/internal/ui/chat/state.go index 423fc6a..a9dce9c 100644 --- a/internal/ui/chat/state.go +++ b/internal/ui/chat/state.go @@ -95,8 +95,8 @@ func (m *Model) onReady(event *gateway.ReadyEvent) tview.Cmd { } func (m *Model) onMessageCreate(message *gateway.MessageCreateEvent) tview.Cmd { - selectedChannel := m.SelectedChannel() - if selectedChannel != nil && selectedChannel.ID == message.ChannelID { + selectedChannel, ok := m.SelectedChannel() + if ok && selectedChannel.ID == message.ChannelID { m.removeTyper(message.Author.ID) m.messagesList.addMessage(message.Message) return nil @@ -115,8 +115,8 @@ func (m *Model) notify(message gateway.MessageCreateEvent) tview.Cmd { } func (m *Model) onMessageUpdate(message *gateway.MessageUpdateEvent) { - selectedChannel := m.SelectedChannel() - if selectedChannel == nil || selectedChannel.ID != message.ChannelID { + selectedChannel, ok := m.SelectedChannel() + if !ok || selectedChannel.ID != message.ChannelID { return } @@ -131,8 +131,8 @@ func (m *Model) onMessageUpdate(message *gateway.MessageUpdateEvent) { } func (m *Model) onMessageDelete(message *gateway.MessageDeleteEvent) { - selectedChannel := m.SelectedChannel() - if selectedChannel == nil || selectedChannel.ID != message.ChannelID { + selectedChannel, ok := m.SelectedChannel() + if !ok || selectedChannel.ID != message.ChannelID { return } @@ -175,8 +175,8 @@ func (m *Model) onGuildMemberRemove(event *gateway.GuildMemberRemoveEvent) { } func (m *Model) onTypingStart(event *gateway.TypingStartEvent) { - selectedChannel := m.SelectedChannel() - if selectedChannel == nil || selectedChannel.ID != event.ChannelID { + selectedChannel, ok := m.SelectedChannel() + if !ok || selectedChannel.ID != event.ChannelID { return }