diff --git a/cmd/guilds_tree.go b/cmd/guilds_tree.go index fc92a6c..b56b362 100644 --- a/cmd/guilds_tree.go +++ b/cmd/guilds_tree.go @@ -79,7 +79,7 @@ func (gt *guildsTree) channelToString(channel discord.Channel) string { recipients := make([]string, len(channel.DMRecipients)) for i, r := range channel.DMRecipients { - recipients[i] = r.DisplayOrUsername() + recipients[i] = ui.PreferredName(r, gt.cfg.Theme) } return strings.Join(recipients, ", ") diff --git a/cmd/message_input.go b/cmd/message_input.go index 59a44a5..4ee8f46 100644 --- a/cmd/message_input.go +++ b/cmd/message_input.go @@ -264,7 +264,7 @@ func (mi *messageInput) tabComplete(isAuto bool) { shown[m.Author.Username] = true discordState.MemberState.RequestMember(gID, m.Author.ID) if mem, err := discordState.Cabinet.Member(gID, m.Author.ID); err == nil { - if mi.addAutocompleteItem(gID, mem) { + if mi.addMentionItem(gID, mem) { break } } @@ -283,7 +283,7 @@ func (mi *messageInput) tabComplete(isAuto bool) { } for _, r := range res { if channelHasUser(cID, mems[r.Index].User.ID) && - mi.addAutocompleteItem(gID, &mems[r.Index]) { + mi.addMentionItem(gID, &mems[r.Index]) { break } } @@ -379,42 +379,48 @@ func (mi *messageInput) showMentionList(col int) { app.SetFocus(mi) } -func (mi *messageInput) addAutocompleteItem(gID discord.GuildID, m *discord.Member) bool { +func (mi *messageInput) addMentionItem(gID discord.GuildID, m *discord.Member) bool { username := m.User.Username if username == "" { return false } - var dname string - if mi.cfg.Theme.MentionsList.ShowNicknames && m.Nick != "" { + dname := "" + if mi.cfg.Theme.MentionsList.PreferNicknames { dname = m.Nick - } else { + } + if dname == "" { dname = m.User.DisplayName } if dname != "" { dname = tview.Escape(dname) } // this is WAY faster than discordState.MemberColor - if mi.cfg.Theme.MentionsList.ShowUsernameColors { + if mi.cfg.Theme.MentionsList.ShowUserColors { if c, ok := state.MemberColor(m, func(id discord.RoleID) *discord.Role { r, _ := discordState.Cabinet.Role(gID, id) return r }); ok { if dname != "" { - dname = "[" + c.String() + "]" + dname + "[-]" + dname = "[" + c.String() + "]" + dname + "[-:]" } else { - username = "[" + c.String() + "]" + username + "[-]" + username = "[" + c.String() + "]" + username + "[-:]" } } } - // The username overwrite in the case of dname == "" is intended if presence, _ := discordState.Cabinet.Presence(gID, m.User.ID); presence == nil || presence.Status == discord.OfflineStatus { - username = "[::d]" + username + "[::D]" + if mi.cfg.Theme.MentionsList.ShowUsernames || dname == "" { + username = "[::d]" + username + "[::D]" + } else { + dname = "[::d]" + dname + "[::D]" + } } if dname != "" { - mi.mentionsList.AddItem(dname+" ("+username+")", m.User.Username, 0, nil) - } else { - mi.mentionsList.AddItem(username, m.User.Username, 0, nil) + if mi.cfg.Theme.MentionsList.ShowUsernames { + dname += " (" + username + ")" + } + username = dname } + mi.mentionsList.AddItem(username, m.User.Username, 0, nil) return mi.mentionsList.GetItemCount() > int(mi.cfg.AutocompleteLimit) } diff --git a/cmd/messages_list.go b/cmd/messages_list.go index df51dbf..db6aa98 100644 --- a/cmd/messages_list.go +++ b/cmd/messages_list.go @@ -57,7 +57,7 @@ func newMessagesList(cfg *config.Config) *messagesList { SetTitle("Messages"). SetInputCapture(ml.onInputCapture) - markdown.DefaultRenderer.AddOptions(renderer.WithOption("theme", cfg.Theme.MessagesList)) + markdown.DefaultRenderer.AddOptions(renderer.WithOption("theme", cfg.Theme)) return ml } @@ -68,7 +68,7 @@ func (ml *messagesList) drawMsgs(cID discord.ChannelID) { return } - if app.cfg.Theme.MessagesList.ShowNicknames || app.cfg.Theme.MessagesList.ShowUsernameColors { + if app.cfg.Theme.PreferNicknames || app.cfg.Theme.MessagesList.ShowUserColors { if ch, _ := discordState.Cabinet.Channel(cID); ch.GuildID.IsValid() { ml.requestGuildMembers(ch.GuildID, msgs) } @@ -143,7 +143,7 @@ func (ml *messagesList) drawTimestamps(ts discord.Timestamp) { } func (ml *messagesList) drawAuthor(msg discord.Message) { - name := msg.Author.DisplayOrUsername() + name := "" style := ml.cfg.Theme.MessagesList.AuthorStyle if msg.GuildID.IsValid() { @@ -153,11 +153,9 @@ func (ml *messagesList) drawAuthor(msg discord.Message) { return } - if app.cfg.Theme.MessagesList.ShowNicknames && member.Nick != "" { - name = member.Nick - } + name = ui.PreferredMemberName(member, ml.cfg.Theme) - if app.cfg.Theme.MessagesList.ShowUsernameColors { + if app.cfg.Theme.MessagesList.ShowUserColors { color, ok := state.MemberColor(member, func(id discord.RoleID) *discord.Role { r, _ := discordState.Cabinet.Role(msg.GuildID, id) return r @@ -167,6 +165,8 @@ func (ml *messagesList) drawAuthor(msg discord.Message) { style = config.NewStyleWrapper(tcell.StyleDefault.Foreground(c)) } } + } else { + name = ui.PreferredName(msg.Author, ml.cfg.Theme) } fg, bg, _ := style.Decompose() @@ -506,18 +506,16 @@ func (ml *messagesList) reply(mention bool) { return } - name := msg.Author.DisplayOrUsername() - + name := "" if msg.GuildID.IsValid() { member, err := discordState.Cabinet.Member(msg.GuildID, msg.Author.ID) if err != nil { slog.Error("failed to get member from state", "guild_id", msg.GuildID, "member_id", msg.Author.ID, "err", err) return } - - if app.cfg.Theme.MessagesList.ShowNicknames && member.Nick != "" { - name = member.Nick - } + name = ui.PreferredMemberName(member, ml.cfg.Theme) + } else { + name = ui.PreferredName(msg.Author, ml.cfg.Theme) } title += name diff --git a/internal/config/config.toml b/internal/config/config.toml index de97d4e..1ee2df7 100644 --- a/internal/config/config.toml +++ b/internal/config/config.toml @@ -102,6 +102,12 @@ down = "Down" # Applies to all # style = { foreground = "", background = "", attributes = "" or [""] } [theme] +# prefer_nicknames will show nicknames if possible +# This has higher priority than prefer_display_names. +prefer_nicknames = true +# prefer_display_names will show display names if possible. +# This has lower priority than prefer_nicknames. +prefer_display_names = false background_color = "default" [theme.title] @@ -129,8 +135,6 @@ graphics = true # channel_style = { foreground = "default" } [theme.messages_list] -# Set to false to show messages with usernames instead of nicknames -show_user_nicks = true show_user_colors = true reply_indicator = ">" @@ -143,7 +147,12 @@ url_style = { foreground = "blue" } attachment_style = { foreground = "yellow" } [theme.mentions_list] -show_user_nicks = true +# prefer_nicknames will show the user's nickname instead of displayname (if +# the user has one). +prefer_nicknames = true +# Show usernames next to display/nickname (username will always be shown if the +# user did not set a display/nickname in their profile). +show_usernames = true show_user_colors = true # Note: width and height are capped to the avaliable space diff --git a/internal/config/theme.go b/internal/config/theme.go index 3d3ee94..47885e9 100644 --- a/internal/config/theme.go +++ b/internal/config/theme.go @@ -137,8 +137,7 @@ type ( } MessagesListTheme struct { - ShowNicknames bool `toml:"show_user_nicks"` - ShowUsernameColors bool `toml:"show_user_colors"` + ShowUserColors bool `toml:"show_user_colors"` ReplyIndicator string `toml:"reply_indicator"` ForwardedIndicator string `toml:"forwarded_indicator"` @@ -151,14 +150,17 @@ type ( } MentionsListTheme struct { - ShowNicknames bool `toml:"show_user_nicks"` - ShowUsernameColors bool `toml:"show_user_colors"` + PreferNicknames bool `toml:"prefer_nicknames"` + ShowUsernames bool `toml:"show_usernames"` + ShowUserColors bool `toml:"show_user_colors"` MinWidth uint `toml:"min_width"` MaxHeight uint `toml:"max_height"` } Theme struct { + PreferNicknames bool `toml:"prefer_nicknames"` + PreferDisplayNames bool `toml:"prefer_display_names"` BackgroundColor string `toml:"background_color"` Title TitleTheme `toml:"title"` diff --git a/internal/markdown/renderer.go b/internal/markdown/renderer.go index ebd3172..958e240 100644 --- a/internal/markdown/renderer.go +++ b/internal/markdown/renderer.go @@ -36,7 +36,7 @@ func (r *renderer) AddOptions(opts ...gmr.Option) { } func (r *renderer) Render(w io.Writer, source []byte, node ast.Node) error { - theme := r.config.Options["theme"].(config.MessagesListTheme) + theme := r.config.Options["theme"].(config.Theme) return ast.Walk(node, func(node ast.Node, entering bool) (ast.WalkStatus, error) { switch node := node.(type) { case *ast.Document: @@ -48,9 +48,9 @@ func (r *renderer) Render(w io.Writer, source []byte, node ast.Node) error { case *ast.FencedCodeBlock: r.renderFencedCodeBlock(w, node, entering, source) case *ast.AutoLink: - r.renderAutoLink(w, node, entering, source, theme.URLStyle.Style) + r.renderAutoLink(w, node, entering, source, theme.MessagesList.URLStyle.Style) case *ast.Link: - r.renderLink(w, node, entering, theme.URLStyle.Style) + r.renderLink(w, node, entering, theme.MessagesList.URLStyle.Style) case *ast.List: r.renderList(w, node, entering) case *ast.ListItem: @@ -59,9 +59,9 @@ func (r *renderer) Render(w io.Writer, source []byte, node ast.Node) error { case *discordmd.Inline: r.renderInline(w, node, entering) case *discordmd.Mention: - r.renderMention(w, node, entering, theme.ShowNicknames, theme.MentionStyle.Style) + r.renderMention(w, node, entering, theme.PreferNicknames, theme.PreferDisplayNames, theme.MessagesList.MentionStyle.Style) case *discordmd.Emoji: - r.renderEmoji(w, node, entering, theme.EmojiStyle.Style) + r.renderEmoji(w, node, entering, theme.MessagesList.EmojiStyle.Style) } return ast.WalkContinue, nil @@ -190,19 +190,25 @@ func (r *renderer) renderInline(w io.Writer, node *discordmd.Inline, entering bo } } -func (r *renderer) renderMention(w io.Writer, node *discordmd.Mention, entering bool, showNicknames bool, mentionStyle tcell.Style) { +func (r *renderer) renderMention(w io.Writer, node *discordmd.Mention, entering, preferNicknames, preferDisplayNames bool, style tcell.Style) { if entering { - fg, bg, _ := mentionStyle.Decompose() + fg, bg, _ := style.Decompose() _, _ = fmt.Fprintf(w, "[%s:%s:b]", fg, bg) switch { case node.Channel != nil: io.WriteString(w, "#"+node.Channel.Name) case node.GuildUser != nil: - username := node.GuildUser.DisplayOrUsername() - if showNicknames && node.GuildUser.Member != nil && node.GuildUser.Member.Nick != "" { + username := "" + if preferNicknames && node.GuildUser.Member != nil { username = node.GuildUser.Member.Nick } + if username == "" && !preferDisplayNames { + username = node.GuildUser.DisplayName + } + if username == "" { + username = node.GuildUser.Username + } io.WriteString(w, "@"+username) case node.GuildRole != nil: io.WriteString(w, "@"+node.GuildRole.Name) diff --git a/internal/ui/util.go b/internal/ui/util.go index 0e01656..d6caf82 100644 --- a/internal/ui/util.go +++ b/internal/ui/util.go @@ -3,6 +3,7 @@ package ui import ( "github.com/ayn2op/discordo/internal/config" "github.com/ayn2op/tview" + "github.com/diamondburned/arikawa/v3/discord" ) // ConfigureBox configures the provided box according to the provided theme. @@ -41,3 +42,20 @@ func Centered(p tview.Primitive, width, height int) tview.Primitive { SetRows(0, height, 0). AddItem(p, 1, 1, 1, 1, 0, 0, true) } + +func PreferredMemberName(m *discord.Member, theme config.Theme) string { + if theme.PreferNicknames && m.Nick != "" { + return m.Nick + } + if theme.PreferDisplayNames && m.User.DisplayName != "" { + return m.User.DisplayName + } + return m.User.Username +} + +func PreferredName(u discord.User, theme config.Theme) string { + if theme.PreferDisplayNames && u.DisplayName != "" { + return u.DisplayName + } + return u.Username +}