mirror of
https://github.com/TautvydasDerzinskas/Thingport.git
synced 2026-10-21 22:22:24 -05:00
fix: profile menu chips
This commit is contained in:
1 parent
6df320f106
commit
f903abcd29
5 files changed
+53
-16
No files matched your search
@@ -108,13 +108,14 @@ router.post(
|
||||
}),
|
||||
);
|
||||
|
||||
// Admin-only end to end, like storage settings: the token is a shared credential for the whole
|
||||
// instance's Thingiverse imports, not a per-user preference. GET never echoes the token itself
|
||||
// back (write-only, like any other API secret) -- only whether one is currently configured, so
|
||||
// the admin UI can show "configured" / "not configured" without re-displaying the value.
|
||||
// Read is open to every user -- UserMenu's Thingiverse connection chip needs to know whether
|
||||
// imports will actually work, same as /settings/previews above. Only admins can change it: the
|
||||
// token is a shared credential for the whole instance's Thingiverse imports, not a per-user
|
||||
// preference. GET never echoes the token itself back (write-only, like any other API secret) --
|
||||
// only whether one is currently configured, so the admin UI can show "configured" / "not
|
||||
// configured" without re-displaying the value.
|
||||
router.get(
|
||||
"/settings/thingiverse",
|
||||
requireAdmin,
|
||||
asyncHandler(async (_req, res) => {
|
||||
res.json({ configured: Boolean(await getThingiverseAccessToken()) });
|
||||
}),
|
||||
|
||||
@@ -74,7 +74,6 @@ export default function TopBar({
|
||||
onThemeChange={onThemeChange}
|
||||
onOpenProfile={onOpenProfile}
|
||||
onLogout={onLogout}
|
||||
makerworldCookie={makerworldCookie}
|
||||
/>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
@@ -18,14 +18,18 @@ import type { AuthUser } from "../../api/auth";
|
||||
import type { ThemeSelection } from "../../constants/settingsOptions";
|
||||
import { IMPORT_PROVIDER_INFO } from "../../constants/importProviders";
|
||||
import { useGravatarUrl } from "../../hooks/useGravatarUrl";
|
||||
import { settingsApi } from "../../api/settings";
|
||||
|
||||
const THEME_MODES: ThemeSelection[] = ["light", "dark"];
|
||||
|
||||
type ServiceChipDef = {
|
||||
key: "makerworld";
|
||||
key: "makerworld" | "thingiverse" | "printables";
|
||||
label: string;
|
||||
color: string;
|
||||
connected: boolean;
|
||||
/** Shown in the tooltip only when connected is false -- explains what's missing (a per-user
|
||||
* credential the viewer can fix themselves, vs. an instance-wide one only an admin can). */
|
||||
disabledReason?: string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
@@ -34,19 +38,30 @@ type Props = {
|
||||
onThemeChange: (theme: ThemeSelection) => void;
|
||||
onOpenProfile: () => void;
|
||||
onLogout: () => void;
|
||||
makerworldCookie: string;
|
||||
};
|
||||
|
||||
/** Avatar (Gravatar, from the account email) that opens identity + quick settings, mirroring
|
||||
* the youtube-mp3-vault UserMenu: name/email/logout up top, Profile and a light/dark Theme
|
||||
* submenu below, then at-a-glance MakerWorld cookie status. Thingiverse import auth is an
|
||||
* admin-configured, instance-wide Access Token now (see AdminSettingsPage/ThingiverseSection),
|
||||
* not a per-user credential, so it has no chip here. */
|
||||
export function UserMenu({ user, theme, onThemeChange, onOpenProfile, onLogout, makerworldCookie }: Props) {
|
||||
* submenu below, then at-a-glance import-provider connection chips (MakerWorld, Thingiverse,
|
||||
* Printables). Each provider's "configured" status is fetched from the backend once on mount --
|
||||
* never from localStorage, which only mirrors what this one browser last saved and can easily
|
||||
* disagree with the DB (e.g. the cookie was set from a different device, or localStorage was
|
||||
* cleared) -- see MakerworldCookieSection, which had the same DB-truth fetch already for its
|
||||
* own chip; this just brings UserMenu's chip in line with it. */
|
||||
export function UserMenu({ user, theme, onThemeChange, onOpenProfile, onLogout }: Props) {
|
||||
const { t } = useTranslation(["app", "common"]);
|
||||
const avatarUrl = useGravatarUrl(user?.email, 128);
|
||||
const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null);
|
||||
const [themeAnchorEl, setThemeAnchorEl] = React.useState<HTMLElement | null>(null);
|
||||
const [makerworldConfigured, setMakerworldConfigured] = React.useState(false);
|
||||
const [thingiverseConfigured, setThingiverseConfigured] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
let active = true;
|
||||
settingsApi.getMakerworld().then(res => { if (active) setMakerworldConfigured(res.configured); }).catch(() => undefined);
|
||||
settingsApi.getThingiverse().then(res => { if (active) setThingiverseConfigured(res.configured); }).catch(() => undefined);
|
||||
return () => { active = false; };
|
||||
}, []);
|
||||
|
||||
const closeMenu = () => setAnchorEl(null);
|
||||
const closeThemeMenu = () => setThemeAnchorEl(null);
|
||||
@@ -58,7 +73,21 @@ export function UserMenu({ user, theme, onThemeChange, onOpenProfile, onLogout,
|
||||
};
|
||||
|
||||
const services: ServiceChipDef[] = [
|
||||
{ key: "makerworld", ...IMPORT_PROVIDER_INFO.makerworld, connected: Boolean(makerworldCookie.trim()) },
|
||||
{
|
||||
key: "makerworld",
|
||||
...IMPORT_PROVIDER_INFO.makerworld,
|
||||
connected: makerworldConfigured,
|
||||
disabledReason: t("userMenu.makerworldDisabledReason"),
|
||||
},
|
||||
{
|
||||
key: "thingiverse",
|
||||
...IMPORT_PROVIDER_INFO.thingiverse,
|
||||
connected: thingiverseConfigured,
|
||||
disabledReason: t("userMenu.thingiverseDisabledReason"),
|
||||
},
|
||||
// No configuration to check -- Printables' public API answers unauthenticated requests
|
||||
// directly (see backend's printablesApi.ts), so imports from it always work.
|
||||
{ key: "printables", ...IMPORT_PROVIDER_INFO.printables, connected: true },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -104,7 +133,11 @@ export function UserMenu({ user, theme, onThemeChange, onOpenProfile, onLogout,
|
||||
{services.map(svc => (
|
||||
<Box
|
||||
key={svc.key}
|
||||
title={svc.connected ? t("userMenu.serviceConnected", { service: svc.label }) : t("userMenu.serviceNotConnected", { service: svc.label })}
|
||||
title={
|
||||
svc.connected
|
||||
? t("userMenu.serviceConnected", { service: svc.label })
|
||||
: svc.disabledReason ?? t("userMenu.serviceNotConnected", { service: svc.label })
|
||||
}
|
||||
sx={{
|
||||
px: 1, py: 0.375, borderRadius: 1, fontSize: 11, fontWeight: 600, lineHeight: 1.4,
|
||||
whiteSpace: "nowrap", color: "#fff",
|
||||
|
||||
@@ -203,7 +203,9 @@
|
||||
"light": "Light",
|
||||
"dark": "Dark",
|
||||
"serviceConnected": "{{service}}: connected",
|
||||
"serviceNotConnected": "{{service}}: not connected"
|
||||
"serviceNotConnected": "{{service}}: not connected",
|
||||
"makerworldDisabledReason": "MakerWorld: you haven't added a cookie in your profile yet",
|
||||
"thingiverseDisabledReason": "Thingiverse: the admin hasn't configured an API key yet"
|
||||
},
|
||||
"notifications": {
|
||||
"label": "Notifications",
|
||||
|
||||
@@ -203,7 +203,9 @@
|
||||
"light": "Šviesi",
|
||||
"dark": "Tamsi",
|
||||
"serviceConnected": "{{service}}: prijungta",
|
||||
"serviceNotConnected": "{{service}}: neprijungta"
|
||||
"serviceNotConnected": "{{service}}: neprijungta",
|
||||
"makerworldDisabledReason": "MakerWorld: profilyje dar nepridėjote slapuko (cookie)",
|
||||
"thingiverseDisabledReason": "Thingiverse: administratorius dar nesukonfigūravo API rakto"
|
||||
},
|
||||
"notifications": {
|
||||
"label": "Pranešimai",
|
||||
|
||||
Reference in new issue
Block a user