mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 11:23:10 +02:00
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:
@@ -1,6 +1,19 @@
|
||||
export interface AuthContextType {
|
||||
session: null;
|
||||
user: { id?: string; email?: string; [key: string]: unknown } | null;
|
||||
/**
|
||||
* Human-readable name to show in the UI for the current session.
|
||||
* - A real identity (username/email/full_name) when the user is signed in.
|
||||
* - A layer-specific placeholder (e.g. "Guest" in SaaS, "User" in
|
||||
* proprietary) for anonymous sessions.
|
||||
* - null only when there is no user object at all (signed-out, or core
|
||||
* OSS with no auth context) - consumers can fall back to whatever
|
||||
* makes sense in their build.
|
||||
*
|
||||
* Each layer derives this from its own native user shape - consumers
|
||||
* should treat the resulting string as opaque display text.
|
||||
*/
|
||||
displayName: string | null;
|
||||
loading: boolean;
|
||||
error: Error | null;
|
||||
signOut: () => Promise<void>;
|
||||
@@ -15,6 +28,7 @@ export function useAuth(): AuthContextType {
|
||||
return {
|
||||
session: null,
|
||||
user: null,
|
||||
displayName: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
signOut: async () => {},
|
||||
|
||||
@@ -22,11 +22,9 @@ export default function AppConfigModalLazy({
|
||||
if (opened) setShouldMount(true);
|
||||
}, [opened]);
|
||||
|
||||
if (!shouldMount) return null;
|
||||
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<AppConfigModal opened={opened} onClose={onClose} />
|
||||
{shouldMount && <AppConfigModal opened={opened} onClose={onClose} />}
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
} from "@app/contexts/NavigationContext";
|
||||
import { useViewer } from "@app/contexts/ViewerContext";
|
||||
import { useFileHandler } from "@app/hooks/useFileHandler";
|
||||
import { useAuth } from "@app/auth/UseSession";
|
||||
import {
|
||||
useIndexedDB,
|
||||
useIndexedDBRevision,
|
||||
@@ -107,19 +108,39 @@ const FileSidebar = forwardRef<HTMLDivElement, FileSidebarProps>(
|
||||
const { activeFileId, setActiveFileId } = useViewer();
|
||||
const { addFiles } = useFileHandler();
|
||||
const indexedDB = useIndexedDB();
|
||||
const [displayName, setDisplayName] = useState<string>("Guest");
|
||||
|
||||
// Each auth layer derives its own displayName from its native user shape.
|
||||
// Fall back to the proprietary REST endpoint only when the auth
|
||||
// context yields nothing - then to "User" as a generic last resort.
|
||||
const { displayName: authDisplayName } = useAuth();
|
||||
const [accountUsername, setAccountUsername] = useState<string | null>(null);
|
||||
const displayName =
|
||||
authDisplayName ?? accountUsername ?? t("auth.displayName.user", "User");
|
||||
|
||||
useEffect(() => {
|
||||
if (!config?.enableLogin) return;
|
||||
if (!config?.enableLogin) {
|
||||
setAccountUsername(null);
|
||||
return;
|
||||
}
|
||||
if (authDisplayName) {
|
||||
// The auth context has a name; don't bother hitting the REST
|
||||
// endpoint, but clear any stale cached value from a prior call.
|
||||
setAccountUsername(null);
|
||||
return;
|
||||
}
|
||||
accountService
|
||||
.getAccountData()
|
||||
.then((data) => {
|
||||
if (data?.username) setDisplayName(data.username);
|
||||
// Always reflect the latest result - including clearing it on
|
||||
// sign-out, when the endpoint returns no username (or 401s into
|
||||
// the catch branch below). Without this, signing out would leave
|
||||
// the old username on screen.
|
||||
setAccountUsername(data?.username ?? null);
|
||||
})
|
||||
.catch(() => {
|
||||
/* not logged in or security disabled */
|
||||
setAccountUsername(null);
|
||||
});
|
||||
}, [config?.enableLogin]);
|
||||
}, [config?.enableLogin, authDisplayName]);
|
||||
|
||||
// Leaf files = user-visible files (excludes intermediate tool outputs)
|
||||
const [allFileStubs, setAllFileStubs] = useState<StirlingFileStub[]>([]);
|
||||
|
||||
@@ -31,7 +31,11 @@ const toneStyles: Record<
|
||||
};
|
||||
|
||||
interface InfoBannerProps {
|
||||
icon: string;
|
||||
/**
|
||||
* Either a LocalIcon name (string) for the standard sized icon slot, or a
|
||||
* pre-rendered ReactNode (e.g. a logo image) which is dropped in as-is.
|
||||
*/
|
||||
icon?: string | ReactNode;
|
||||
title?: ReactNode;
|
||||
message: ReactNode;
|
||||
buttonText?: string;
|
||||
@@ -48,6 +52,8 @@ interface InfoBannerProps {
|
||||
iconColor?: string;
|
||||
buttonColor?: string;
|
||||
buttonVariant?: "light" | "filled" | "white" | "outline" | "subtle";
|
||||
/** Override the button label colour (for dark/custom theme variants). */
|
||||
buttonTextColor?: string;
|
||||
minHeight?: number | string;
|
||||
closeIconColor?: string;
|
||||
compact?: boolean;
|
||||
@@ -74,6 +80,7 @@ export const InfoBanner: React.FC<InfoBannerProps> = ({
|
||||
iconColor,
|
||||
buttonColor,
|
||||
buttonVariant = "light",
|
||||
buttonTextColor,
|
||||
minHeight = 56,
|
||||
closeIconColor,
|
||||
compact = false,
|
||||
@@ -120,12 +127,21 @@ export const InfoBanner: React.FC<InfoBannerProps> = ({
|
||||
wrap="nowrap"
|
||||
style={{ flex: 1, minWidth: 0 }}
|
||||
>
|
||||
<LocalIcon
|
||||
icon={icon}
|
||||
width={iconSize}
|
||||
height={iconSize}
|
||||
style={{ color: iconColor ?? toneStyle.icon, flexShrink: 0 }}
|
||||
/>
|
||||
{icon != null &&
|
||||
(typeof icon === "string" ? (
|
||||
<LocalIcon
|
||||
icon={icon}
|
||||
width={iconSize}
|
||||
height={iconSize}
|
||||
style={{ color: iconColor ?? toneStyle.icon, flexShrink: 0 }}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{ flexShrink: 0, display: "flex", alignItems: "center" }}
|
||||
>
|
||||
{icon}
|
||||
</div>
|
||||
))}
|
||||
<Stack gap={compact ? 1 : 2} style={{ flex: 1, minWidth: 0 }}>
|
||||
{title && (
|
||||
<Text
|
||||
@@ -161,6 +177,11 @@ export const InfoBanner: React.FC<InfoBannerProps> = ({
|
||||
height={compact ? "0.75rem" : "0.9rem"}
|
||||
/>
|
||||
}
|
||||
styles={
|
||||
buttonTextColor
|
||||
? { label: { color: buttonTextColor } }
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{buttonText}
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user