refactor(chat): return comma-ok from selectedMessage and SelectedChannel

This commit is contained in:
ayn2op 2026-05-24 19:10:19 -04:00
parent b9152f8b21
commit 83f9c049cf
4 changed files with 84 additions and 93 deletions

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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

View file

@ -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
}