Ver Fonte

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

Co-authored-by: Ayyan <ayn2op@gmail.com>
xqrs há 10 meses atrás
pai
commit
0a5e566608

+ 1 - 1
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, ", ")

+ 20 - 14
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)
 }
 

+ 11 - 13
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

+ 12 - 3
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

+ 6 - 4
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"`

+ 15 - 9
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)

+ 18 - 0
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
+}