feat: show provider badge in quick actions

This commit is contained in:
Stavros
2026-06-24 12:27:30 +03:00
parent 08af4557fd
commit aaa5f4cb2f
3 changed files with 92 additions and 7 deletions
@@ -0,0 +1,22 @@
import type { SVGProps } from "react";
export function LocalAuthIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 24 24"
{...props}
>
<path
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0-8 0M6 21v-2a4 4 0 0 1 4-4h5m3.5 3.5L15 22l-1.5-1.5m5.054-2.086a2 2 0 1 1 2.828-2.828a2 2 0 0 1-2.828 2.828M16 19l1 1"
></path>
</svg>
);
}
@@ -37,6 +37,13 @@ import { useMutation } from "@tanstack/react-query";
import axios from "axios"; import axios from "axios";
import { toast } from "sonner"; import { toast } from "sonner";
import { useEffect } from "react"; import { useEffect } from "react";
import { GoogleIcon } from "../icons/google";
import { GithubIcon } from "../icons/github";
import { TailscaleIcon } from "../icons/tailscale";
import { MicrosoftIcon } from "../icons/microsoft";
import { PocketIDIcon } from "../icons/pocket-id";
import { LocalAuthIcon } from "../icons/local-auth";
import { OAuthIcon } from "../icons/oauth";
function Avatar({ initial }: { initial: string }) { function Avatar({ initial }: { initial: string }) {
return ( return (
@@ -49,8 +56,18 @@ function Avatar({ initial }: { initial: string }) {
); );
} }
const iconStyles = "size-4";
const iconMap: Record<string, React.ReactNode> = {
google: <GoogleIcon className={iconStyles} />,
github: <GithubIcon className={iconStyles} />,
tailscale: <TailscaleIcon className={iconStyles} />,
microsoft: <MicrosoftIcon className={iconStyles} />,
pocketid: <PocketIDIcon className={iconStyles} />,
};
export const QuickActions = () => { export const QuickActions = () => {
const { auth } = useUserContext(); const { auth, oauth, tailscale } = useUserContext();
const { theme, setTheme } = useTheme(); const { theme, setTheme } = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
const { search } = useLocation(); const { search } = useLocation();
@@ -64,6 +81,41 @@ export const QuickActions = () => {
const screenParams = useScreenParams(searchParams); const screenParams = useScreenParams(searchParams);
const compiledParams = recompileScreenParams(screenParams); const compiledParams = recompileScreenParams(screenParams);
const providerDetails = (():
| { name: string; icon: React.ReactNode }
| undefined => {
if (!auth.authenticated) {
return undefined;
}
if (auth.providerId === "local" || auth.providerId === "ldap") {
return {
name: t(
auth.providerId === "ldap"
? "quickActionsProviderLDAP"
: "quickActionsProviderLocal",
),
icon: <LocalAuthIcon className={iconStyles} />,
};
}
if (oauth.active) {
return {
name: t("quickActionsProviderOAuth", { provider: oauth.displayName }),
icon: iconMap[auth.providerId] || <OAuthIcon className={iconStyles} />,
};
}
if (auth.providerId === "tailscale") {
return {
name: `Tailscale (${tailscale.nodeName})`,
icon: <TailscaleIcon className={iconStyles} />,
};
}
return undefined;
})();
const logoutMutation = useMutation({ const logoutMutation = useMutation({
mutationFn: () => axios.post("/api/user/logout"), mutationFn: () => axios.post("/api/user/logout"),
mutationKey: ["logout"], mutationKey: ["logout"],
@@ -134,11 +186,19 @@ export const QuickActions = () => {
<div className="bg-foreground text-background flex size-9 shrink-0 items-center justify-center rounded-full text-sm font-medium"> <div className="bg-foreground text-background flex size-9 shrink-0 items-center justify-center rounded-full text-sm font-medium">
{initial} {initial}
</div> </div>
<div className="flex min-w-0 flex-col"> <div className="flex min-w-0 flex-col gap-1.5">
<span className="truncate text-sm font-medium"> <div className="flex items-center gap-2">
{auth.name} <span className="truncate text-sm font-medium leading-none">
</span> {auth.name}
<span className="text-muted-foreground truncate text-xs font-normal"> </span>
{providerDetails && (
<span className="text-muted-foreground inline-flex shrink-0 items-center gap-1 rounded-lg bg-secondary px-2 py-1.25 text-[10px] font-medium leading-none [&_svg]:size-3">
{providerDetails.icon}
{providerDetails.name}
</span>
)}
</div>
<span className="text-muted-foreground truncate text-xs leading-none">
{auth.email} {auth.email}
</span> </span>
</div> </div>
+4 -1
View File
@@ -99,5 +99,8 @@
"quickActionsThemeDark": "Dark", "quickActionsThemeDark": "Dark",
"quickActionsThemeSystem": "System", "quickActionsThemeSystem": "System",
"quickActionsLogout": "Logout", "quickActionsLogout": "Logout",
"quickActionsTitle": "Quick Actions" "quickActionsTitle": "Quick Actions",
"quickActionsProviderLocal": "Local",
"quickActionsProviderLDAP": "LDAP",
"quickActionsProviderOAuth": "{{provider}} OAuth"
} }