feat(discord): show track title in Discord member list (configurable name template) (#885)

* feat(discord): override activity name in member list with track title (configurable template)

The Discord member list and the collapsed Rich Presence card display the
activity's `name` field next to the music icon. Without an override
Discord falls back to the registered application name ("Psysonic"), so
the line reads "Psysonic" while every track plays instead of the actual
track title that comparable players show.

Add a fourth user-configurable template `discordTemplateName` (Settings ->
Integrations -> Discord Rich Presence). The Rust side passes it through
`Activity::new().name(...)` when the rendered template is non-empty; an
empty template falls back to Discord's default application name. Default
template is "{title}".

Tauri boundary: additive optional `nameTemplate` field on the existing
`discord_update_presence` command. Existing call sites that omit it keep
working -- the Rust handler applies the same "{title}" fallback.

* i18n(settings): discord name template label in all locales

Adds the discordTemplateName label across en, de, fr, es, nl, nb, ro,
zh, ru -- shown next to the new "User list line (name)" template input
under Discord Rich Presence.

* docs(release): CHANGELOG and credits for discord name template (PR #885)
This commit is contained in:
Frank Stellmacher
2026-05-28 20:32:41 +02:00
committed by GitHub
parent 403979b35d
commit 455aec4def
18 changed files with 70 additions and 6 deletions
@@ -18,6 +18,7 @@ export function setupDiscordPresence(): () => void {
let discordPrevTemplateDetails: string | null = null;
let discordPrevTemplateState: string | null = null;
let discordPrevTemplateLargeText: string | null = null;
let discordPrevTemplateName: string | null = null;
let discordPrevCoverSource: string | null = null;
const discordServerCoverCache = new Map<string, string | null>();
@@ -30,6 +31,7 @@ export function setupDiscordPresence(): () => void {
discordTemplateDetails,
discordTemplateState,
discordTemplateLargeText,
discordTemplateName,
} = useAuthStore.getState();
if (!discordRichPresence || !currentTrack) {
@@ -41,6 +43,7 @@ export function setupDiscordPresence(): () => void {
discordPrevTemplateDetails = null;
discordPrevTemplateState = null;
discordPrevTemplateLargeText = null;
discordPrevTemplateName = null;
invoke('discord_clear_presence').catch(() => {});
}
return;
@@ -52,7 +55,8 @@ export function setupDiscordPresence(): () => void {
const detailsTemplateChanged = discordTemplateDetails !== discordPrevTemplateDetails;
const stateTemplateChanged = discordTemplateState !== discordPrevTemplateState;
const largeTextTemplateChanged = discordTemplateLargeText !== discordPrevTemplateLargeText;
if (!trackChanged && !playingChanged && !coverSourceChanged && !detailsTemplateChanged && !stateTemplateChanged && !largeTextTemplateChanged) return;
const nameTemplateChanged = discordTemplateName !== discordPrevTemplateName;
if (!trackChanged && !playingChanged && !coverSourceChanged && !detailsTemplateChanged && !stateTemplateChanged && !largeTextTemplateChanged && !nameTemplateChanged) return;
discordPrevTrackId = currentTrack.id;
discordPrevIsPlaying = isPlaying;
@@ -61,6 +65,7 @@ export function setupDiscordPresence(): () => void {
discordPrevTemplateDetails = discordTemplateDetails;
discordPrevTemplateState = discordTemplateState;
discordPrevTemplateLargeText = discordTemplateLargeText;
discordPrevTemplateName = discordTemplateName;
const sendPresence = (coverArtUrl: string | null) => {
invoke('discord_update_presence', {
@@ -74,6 +79,7 @@ export function setupDiscordPresence(): () => void {
detailsTemplate: discordTemplateDetails,
stateTemplate: discordTemplateState,
largeTextTemplate: discordTemplateLargeText,
nameTemplate: discordTemplateName,
}).catch(() => {});
};
+2
View File
@@ -12,6 +12,7 @@ export function createDiscordSettingsActions(set: SetState): Pick<
| 'setDiscordTemplateDetails'
| 'setDiscordTemplateState'
| 'setDiscordTemplateLargeText'
| 'setDiscordTemplateName'
> {
return {
setDiscordRichPresence: (v) => set({ discordRichPresence: v }),
@@ -20,5 +21,6 @@ export function createDiscordSettingsActions(set: SetState): Pick<
setDiscordTemplateDetails: (v) => set({ discordTemplateDetails: v }),
setDiscordTemplateState: (v) => set({ discordTemplateState: v }),
setDiscordTemplateLargeText: (v) => set({ discordTemplateLargeText: v }),
setDiscordTemplateName: (v) => set({ discordTemplateName: v }),
};
}
+1
View File
@@ -98,6 +98,7 @@ describe('trivial pass-through setters', () => {
['setDiscordTemplateDetails', 'discordTemplateDetails', '{artist} — {title}'],
['setDiscordTemplateState', 'discordTemplateState', '{album}'],
['setDiscordTemplateLargeText', 'discordTemplateLargeText', 'Hi'],
['setDiscordTemplateName', 'discordTemplateName', '{title} — {artist}'],
])('%s stores a string value', (setter, key, value) => {
(useAuthStore.getState() as unknown as Record<string, (v: unknown) => void>)[setter](value);
expect((useAuthStore.getState() as unknown as Record<string, unknown>)[key]).toBe(value);
+1
View File
@@ -75,6 +75,7 @@ export const useAuthStore = create<AuthState>()(
discordTemplateDetails: '{artist}',
discordTemplateState: '{title}',
discordTemplateLargeText: '{album}',
discordTemplateName: '{title}',
useCustomTitlebar: false,
preloadMiniPlayer: false,
linuxWebkitKineticScroll: true,
+5
View File
@@ -128,6 +128,10 @@ export interface AuthState {
discordTemplateDetails: string;
discordTemplateState: string;
discordTemplateLargeText: string;
/** Template for Discord activity name (overrides the registered application
* name in the user list / collapsed presence). Default "{title}".
* Empty string falls back to "Psysonic". */
discordTemplateName: string;
useCustomTitlebar: boolean;
/** Pre-build the mini-player webview at app start on Linux/macOS so content is available instantly
* on first open. Ignored on Windows — that platform always pre-creates as a hang workaround. */
@@ -311,6 +315,7 @@ export interface AuthState {
setDiscordTemplateDetails: (v: string) => void;
setDiscordTemplateState: (v: string) => void;
setDiscordTemplateLargeText: (v: string) => void;
setDiscordTemplateName: (v: string) => void;
setUseCustomTitlebar: (v: boolean) => void;
setPreloadMiniPlayer: (v: boolean) => void;
setLinuxWebkitKineticScroll: (v: boolean) => void;