Fix username display issues (#6471)

# Description of Changes
Main fixes:
- Fix the display of the username in the bottom left
- Now displays as "User" when not logged in on self-hosted (desktop) and
"Guest" on SaaS when logged in anonymously
- Now updates properly when the user logs in/out in SaaS, desktop and
self-hosted
- Fix incremental build issues in the desktop app that have been here
since the start (I hope at least - I think the issue is that the JLink
is built read-only and then on subsequent builds you get OS errors when
trying to override the JLink with the new version. There's no real need
for it to be read-only that I know of, so we might as well just make it
R/W and ship like that)
This commit is contained in:
James Brunton
2026-05-29 14:35:47 +00:00
committed by GitHub
parent 83ea07ed6a
commit 4d5eeb103f
17 changed files with 561 additions and 238 deletions
+39 -1
View File
@@ -6,6 +6,8 @@ import {
ReactNode,
useCallback,
} from "react";
import { useTranslation } from "react-i18next";
import type { TFunction } from "i18next";
import { supabase } from "@app/auth/supabase";
import type {
Session,
@@ -31,6 +33,29 @@ import {
// Extend Supabase User to include optional username for compatibility
export type User = SupabaseUser & { username?: string };
/**
* Derive a display name from the Supabase user. Prefers the OAuth-provided
* full_name / name, then the email. Anonymous users get the localised
* "Guest" placeholder (SaaS's chosen label for guest sessions); returns
* null only when there is no user object at all so consumers can pick
* their own fallback.
*
* Exported for unit testing.
*/
export function deriveDisplayName(
user: User | null | undefined,
t: TFunction,
): string | null {
if (!user) return null;
if (user.is_anonymous) return t("auth.displayName.guest", "Guest");
const metadata = user.user_metadata as
| { full_name?: string; name?: string }
| undefined;
return (
user.username || metadata?.full_name || metadata?.name || user.email || null
);
}
export interface TrialStatus {
isTrialing: boolean;
trialEnd: string;
@@ -43,6 +68,15 @@ export interface TrialStatus {
interface AuthContextType {
session: Session | null;
user: User | null;
/**
* Human-readable name to show in the UI for the current session.
* - A real identity (full_name / name / email) when the user is signed in.
* - The localised "Guest" placeholder for anonymous (Supabase
* `is_anonymous`) sessions - SaaS's chosen label, see deriveDisplayName.
* - null only when there is no user object at all (signed-out), so
* consumers can fall back to whatever makes sense.
*/
displayName: string | null;
loading: boolean;
error: AuthError | null;
creditBalance: number | null;
@@ -66,6 +100,7 @@ interface AuthContextType {
const AuthContext = createContext<AuthContextType>({
session: null,
user: null,
displayName: null,
loading: true,
error: null,
creditBalance: null,
@@ -660,9 +695,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
};
}, []);
const { t } = useTranslation();
const user = session?.user ?? null;
const value: AuthContextType = {
session,
user: session?.user ?? null,
user,
displayName: deriveDisplayName(user, t),
loading,
error,
creditBalance,