feat(config): add fine-grained controls for showing {display/nick/user}names (#573)

Co-authored-by: Ayyan <ayn2op@gmail.com>
This commit is contained in:
xqrs 2025-07-06 00:47:36 +03:00 • committed by GitHub
parent d4c770eb49
commit 0a5e566608
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 83 additions and 44 deletions

View file

@ -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, ", ")

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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