feat: restrict theme changes to authenticated users
This commit is contained in:
@@ -1,32 +1,18 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { DropdownMenuCheckboxItem } from "@/components/ui/dropdown-menu";
|
import { DropdownMenuCheckboxItem } from "@/components/ui/dropdown-menu";
|
||||||
import { useTheme } from "@/components/ThemeProvider";
|
import { useTheme } from "@/components/ThemeProvider";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Moon, Sun } from "lucide-react";
|
import { Moon, Sun } from "lucide-react";
|
||||||
|
|
||||||
type ModeToggleProps = {
|
export default function ModeToggle() {
|
||||||
className?: string;
|
|
||||||
variant?: "icon" | "menu";
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function ModeToggle({ className, variant = "icon" }: ModeToggleProps) {
|
|
||||||
const { theme, resolvedTheme, setTheme } = useTheme();
|
const { theme, resolvedTheme, setTheme } = useTheme();
|
||||||
const isDark = resolvedTheme === "dark" || theme === "dark";
|
const isDark = resolvedTheme === "dark" || theme === "dark";
|
||||||
|
|
||||||
const handleToggle = () => {
|
|
||||||
setTheme(isDark ? "light" : "dark");
|
|
||||||
};
|
|
||||||
|
|
||||||
if (variant === "menu") {
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenuCheckboxItem
|
<DropdownMenuCheckboxItem
|
||||||
checked={isDark}
|
checked={isDark}
|
||||||
onCheckedChange={(checked) => setTheme(checked ? "dark" : "light")}
|
onCheckedChange={(checked) => setTheme(checked ? "dark" : "light")}
|
||||||
onSelect={(event) => event.preventDefault()}
|
onSelect={(event) => event.preventDefault()}
|
||||||
className={cn(
|
className="my-1 justify-between gap-3 rounded-xl px-3 py-3 text-foreground focus:bg-secondary focus:text-foreground [&>span:first-child]:hidden"
|
||||||
"my-1 justify-between gap-3 rounded-xl px-3 py-3 text-foreground focus:bg-secondary focus:text-foreground [&>span:first-child]:hidden",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
aria-label="حالت نمایش"
|
aria-label="حالت نمایش"
|
||||||
>
|
>
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
@@ -51,22 +37,3 @@ export default function ModeToggle({ className, variant = "icon" }: ModeTogglePr
|
|||||||
</DropdownMenuCheckboxItem>
|
</DropdownMenuCheckboxItem>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className={cn(
|
|
||||||
"rounded-full border-0 bg-transparent text-foreground shadow-none transition hover:bg-secondary hover:text-secondary-foreground hover:shadow-sm dark:hover:bg-secondary dark:hover:text-secondary-foreground",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
aria-label="تغییر حالت نمایش"
|
|
||||||
title="تغییر حالت نمایش"
|
|
||||||
onClick={handleToggle}
|
|
||||||
>
|
|
||||||
<Sun className="!size-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
|
||||||
<Moon className="absolute !size-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
|
||||||
<span className="sr-only">Toggle theme</span>
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ function ProfileAvatarMenu() {
|
|||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
) : null}
|
) : null}
|
||||||
<ModeToggle variant="menu" className="md:hidden" />
|
<ModeToggle />
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem asChild className="my-1 flex-row-reverse justify-end gap-2 rounded-xl py-3 text-destructive transition-colors hover:bg-secondary hover:text-destructive focus:bg-secondary focus:text-destructive dark:text-red-300 dark:hover:text-red-200 dark:focus:text-red-200">
|
<DropdownMenuItem asChild className="my-1 flex-row-reverse justify-end gap-2 rounded-xl py-3 text-destructive transition-colors hover:bg-secondary hover:text-destructive focus:bg-secondary focus:text-destructive dark:text-red-300 dark:hover:text-red-200 dark:focus:text-red-200">
|
||||||
<Link to="/logout">
|
<Link to="/logout">
|
||||||
@@ -151,14 +151,12 @@ export default function Navbar({ siteContent }: { siteContent: SiteContent }) {
|
|||||||
<NavItem to="/">{siteContent.navigation.home}</NavItem>
|
<NavItem to="/">{siteContent.navigation.home}</NavItem>
|
||||||
<NavItem to="/events">{siteContent.navigation.events}</NavItem>
|
<NavItem to="/events">{siteContent.navigation.events}</NavItem>
|
||||||
<NavItem to="/blog">{siteContent.navigation.blog}</NavItem>
|
<NavItem to="/blog">{siteContent.navigation.blog}</NavItem>
|
||||||
<ModeToggle />
|
|
||||||
{isAuthenticated ? <NotificationsBell /> : null}
|
{isAuthenticated ? <NotificationsBell /> : null}
|
||||||
<ProfileAvatarMenu />
|
<ProfileAvatarMenu />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 md:hidden">
|
<div className="flex items-center gap-2 md:hidden">
|
||||||
{isAuthenticated ? <NotificationsBell /> : null}
|
{isAuthenticated ? <NotificationsBell /> : null}
|
||||||
{!isAuthenticated ? <ModeToggle /> : null}
|
|
||||||
<ProfileAvatarMenu />
|
<ProfileAvatarMenu />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,21 +7,32 @@ import { Toaster } from "@/components/ui/toaster";
|
|||||||
import { Toaster as Sonner } from "@/components/ui/sonner";
|
import { Toaster as Sonner } from "@/components/ui/sonner";
|
||||||
import { ThemeProvider } from "@/components/ThemeProvider";
|
import { ThemeProvider } from "@/components/ThemeProvider";
|
||||||
import MobileVerificationGate from "@/components/MobileVerificationGate";
|
import MobileVerificationGate from "@/components/MobileVerificationGate";
|
||||||
import { AuthProvider } from "@/contexts/AuthContext";
|
import { AuthProvider, useAuth } from "@/contexts/AuthContext";
|
||||||
import { NotificationsProvider } from "@/contexts/NotificationsContext";
|
import { NotificationsProvider } from "@/contexts/NotificationsContext";
|
||||||
|
|
||||||
|
function AuthAwareThemeProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
const { isAuthenticated } = useAuth();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeProvider
|
||||||
|
attribute="class"
|
||||||
|
defaultTheme="light"
|
||||||
|
enableSystem
|
||||||
|
forcedTheme={isAuthenticated ? undefined : "light"}
|
||||||
|
storageKey="egce-theme"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function Providers({ children }: { children: React.ReactNode }) {
|
export default function Providers({ children }: { children: React.ReactNode }) {
|
||||||
const [queryClient] = React.useState(() => new QueryClient());
|
const [queryClient] = React.useState(() => new QueryClient());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider
|
|
||||||
attribute="class"
|
|
||||||
defaultTheme="system"
|
|
||||||
enableSystem
|
|
||||||
storageKey="egce-theme"
|
|
||||||
>
|
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
|
<AuthAwareThemeProvider>
|
||||||
<NotificationsProvider>
|
<NotificationsProvider>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Toaster />
|
<Toaster />
|
||||||
@@ -30,8 +41,8 @@ export default function Providers({ children }: { children: React.ReactNode }) {
|
|||||||
<MobileVerificationGate />
|
<MobileVerificationGate />
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</NotificationsProvider>
|
</NotificationsProvider>
|
||||||
|
</AuthAwareThemeProvider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</ThemeProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user