Files
dotcom-arcade/frontend/src/views/SettingsView.tsx
T
branderson a6adc61627 feat(auth): sign in through a trusted proxy and let those users set a password
On load, a signed-out visitor first asks the backend to sign them in as the
user an authenticating proxy vouches for, before guest mode or a login
redirect takes over; an explicit logout skips this for the rest of the tab.
Settings gains a password card for signed-in users, where a proxy-signed-in
user can set a password without the current one to log in without the
proxy.
2026-10-12 01:05:40 -07:00

137 lines
5.1 KiB
TypeScript

import { useState } from 'react';
import { Settings } from 'lucide-react';
import { AnimatePresence } from 'framer-motion';
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { AccountPasswordCard } from '@/components/settings/AccountPasswordCard';
import { useAuthStore } from '@/store/auth';
import { useQuery } from '@tanstack/react-query';
import { ruffleApi } from '@/lib/api';
import { usePublicSettings } from '@/hooks/usePublicSettings';
import { APP_VERSION } from '@/lib/version';
import { FormattedDate } from '@/components/common/FormattedDate';
import { GeneralSettingsTab } from '@/components/settings/GeneralSettingsTab';
import { AppSettingsTab } from '@/components/settings/AppSettingsTab';
import { UpdateSettingsTab } from '@/components/settings/UpdateSettingsTab';
import { FeaturesSettingsTab } from '@/components/settings/FeaturesSettingsTab';
const tabContentVariants = {
initial: {
opacity: 0,
y: 10,
},
animate: {
opacity: 1,
y: 0,
transition: {
duration: 0.25,
ease: 'easeOut' as const,
},
},
exit: {
opacity: 0,
y: -10,
transition: {
duration: 0.2,
ease: 'easeIn' as const,
},
},
};
export function SettingsView() {
const [activeTab, setActiveTab] = useState('general');
const { user } = useAuthStore();
const isAdmin = user?.permissions.includes('settings.update') ?? false;
const { data: ruffleVersion, isLoading: isLoadingRuffleVersion } = useQuery({
queryKey: ['ruffleVersion'],
queryFn: () => ruffleApi.getVersion(),
staleTime: 5 * 60 * 1000,
enabled: !isAdmin, // Only fetch for non-admin users (admin users get it from GeneralSettingsTab)
});
const { data: publicSettings } = usePublicSettings();
const flashpointVersion = publicSettings?.metadata?.flashpointVersion ?? 'Unknown';
const packagedAt = publicSettings?.metadata?.flashpointPackagedAt;
const lastUpdate = publicSettings?.metadata?.flashpointLastUpdate;
return (
<div className="max-w-5xl mx-auto space-y-6">
<div className="flex items-center gap-3">
<Settings size={32} className="text-primary" />
<h1 className="text-3xl font-bold">Settings</h1>
</div>
<AccountPasswordCard />
{isAdmin ? (
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<TabsList className="grid w-full grid-cols-4 border-2 border-primary/60 h-full">
<TabsTrigger value="general">General</TabsTrigger>
<TabsTrigger value="app">App</TabsTrigger>
<TabsTrigger value="update">Update</TabsTrigger>
<TabsTrigger value="features">Features</TabsTrigger>
</TabsList>
<div className="mt-6">
<AnimatePresence mode="wait">
{activeTab === 'general' ? (
<GeneralSettingsTab tabContentVariants={tabContentVariants} />
) : null}
{activeTab === 'app' ? (
<AppSettingsTab tabContentVariants={tabContentVariants} />
) : null}
{activeTab === 'update' ? (
<UpdateSettingsTab tabContentVariants={tabContentVariants} />
) : null}
{activeTab === 'features' ? (
<FeaturesSettingsTab tabContentVariants={tabContentVariants} />
) : null}
</AnimatePresence>
</div>
</Tabs>
) : (
<div className="space-y-6">
<div className="bg-card rounded-lg p-6 border border-border shadow-md">
<h2 className="text-xl font-semibold mb-4">Version Information</h2>
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground">Flashpoint Version:</span>
<span className="font-medium">{flashpointVersion}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Package Date:</span>
<span className="font-medium">
{packagedAt ? <FormattedDate date={packagedAt} type="date" /> : 'Unknown'}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Last Update:</span>
<span className="font-medium">
{lastUpdate ? <FormattedDate date={lastUpdate} type="datetime" /> : 'Never'}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Web App Version:</span>
<span className="font-medium">{APP_VERSION}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Ruffle Emulator:</span>
<span className="font-medium">
{isLoadingRuffleVersion
? 'Loading...'
: ruffleVersion?.currentVersion || 'Not installed'}
</span>
</div>
</div>
</div>
</div>
)}
</div>
);
}