mirror of
https://github.com/better-auth/better-hub
synced 2026-08-09 12:26:04 +00:00
fix: settings dialog closes on theme change (#211)
https://github.com/better-auth/better-hub/issues/201
This commit is contained in:
@@ -27,9 +27,15 @@ interface SettingsContentProps {
|
||||
initialSettings: UserSettings;
|
||||
user: { name: string; email: string; image: string | null };
|
||||
githubProfile: GitHubProfile;
|
||||
onThemeTransition?: () => void;
|
||||
}
|
||||
|
||||
export function SettingsContent({ initialSettings, user, githubProfile }: SettingsContentProps) {
|
||||
export function SettingsContent({
|
||||
initialSettings,
|
||||
user,
|
||||
githubProfile,
|
||||
onThemeTransition,
|
||||
}: SettingsContentProps) {
|
||||
const [activeTab, setActiveTab] = useState<TabId>("general");
|
||||
const [settings, setSettings] = useState(initialSettings);
|
||||
const { emit } = useMutationEvents();
|
||||
@@ -126,7 +132,11 @@ export function SettingsContent({ initialSettings, user, githubProfile }: Settin
|
||||
{/* Content — only this area scrolls */}
|
||||
<div className="flex-1 min-h-0 overflow-y-auto border border-t-0 border-border mx-6 mb-6">
|
||||
{activeTab === "general" && (
|
||||
<GeneralTab settings={settings} onUpdate={handleUpdate} />
|
||||
<GeneralTab
|
||||
settings={settings}
|
||||
onUpdate={handleUpdate}
|
||||
onThemeTransition={onThemeTransition}
|
||||
/>
|
||||
)}
|
||||
{activeTab === "editor" && <EditorTab />}
|
||||
{activeTab === "ai" && (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
|
||||
import * as VisuallyHidden from "@radix-ui/react-visually-hidden";
|
||||
@@ -53,11 +54,35 @@ export function SettingsDialog({ open, onOpenChange, user, githubProfile }: Sett
|
||||
gcTime: 15 * 60 * 1000,
|
||||
});
|
||||
|
||||
const [isThemeTransitioning, setIsThemeTransitioning] = useState(false);
|
||||
const transitionTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
const onThemeTransition = useCallback(() => {
|
||||
if (transitionTimeoutRef.current) {
|
||||
clearTimeout(transitionTimeoutRef.current);
|
||||
}
|
||||
setIsThemeTransitioning(true);
|
||||
transitionTimeoutRef.current = setTimeout(() => {
|
||||
setIsThemeTransitioning(false);
|
||||
}, 1000);
|
||||
}, []);
|
||||
|
||||
const handleInteractOutside = useCallback(
|
||||
(e: Event) => {
|
||||
if (isThemeTransitioning) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
[isThemeTransitioning],
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent
|
||||
className="sm:max-w-2xl p-0 gap-0 overflow-hidden max-h-[85vh] outline-none"
|
||||
showCloseButton={false}
|
||||
onPointerDownOutside={handleInteractOutside}
|
||||
onInteractOutside={handleInteractOutside}
|
||||
>
|
||||
<VisuallyHidden.Root>
|
||||
<DialogTitle>Settings</DialogTitle>
|
||||
@@ -68,6 +93,7 @@ export function SettingsDialog({ open, onOpenChange, user, githubProfile }: Sett
|
||||
initialSettings={settings}
|
||||
user={user}
|
||||
githubProfile={githubProfile}
|
||||
onThemeTransition={onThemeTransition}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex-1 flex items-center justify-center px-6">
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { UserSettings } from "@/lib/user-settings-store";
|
||||
interface GeneralTabProps {
|
||||
settings: UserSettings;
|
||||
onUpdate: (updates: Partial<UserSettings>) => Promise<void>;
|
||||
onThemeTransition?: () => void;
|
||||
}
|
||||
|
||||
function ThemeGrid({
|
||||
@@ -84,10 +85,24 @@ const RADIUS_OPTIONS: { id: BorderRadiusPreset; label: string; description: stri
|
||||
{ id: "large", label: "Large", description: "Soft & rounded" },
|
||||
];
|
||||
|
||||
export function GeneralTab({ settings: _settings, onUpdate: _onUpdate }: GeneralTabProps) {
|
||||
export function GeneralTab({
|
||||
settings: _settings,
|
||||
onUpdate: _onUpdate,
|
||||
onThemeTransition,
|
||||
}: GeneralTabProps) {
|
||||
const { themeId, mode, borderRadius, setTheme, toggleMode, setBorderRadius, themes } =
|
||||
useColorTheme();
|
||||
|
||||
const handleSetTheme = (id: string) => {
|
||||
onThemeTransition?.();
|
||||
setTheme(id);
|
||||
};
|
||||
|
||||
const handleToggleMode = () => {
|
||||
onThemeTransition?.();
|
||||
toggleMode();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="divide-y divide-border">
|
||||
{/* Mode toggle */}
|
||||
@@ -105,7 +120,9 @@ export function GeneralTab({ settings: _settings, onUpdate: _onUpdate }: General
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => mode === "light" && toggleMode()}
|
||||
onClick={() =>
|
||||
mode === "light" && handleToggleMode()
|
||||
}
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-3 py-2 border text-sm transition-colors",
|
||||
mode === "dark"
|
||||
@@ -120,7 +137,9 @@ export function GeneralTab({ settings: _settings, onUpdate: _onUpdate }: General
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => mode === "dark" && toggleMode()}
|
||||
onClick={() =>
|
||||
mode === "dark" && handleToggleMode()
|
||||
}
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-3 py-2 border text-sm transition-colors",
|
||||
mode === "light"
|
||||
@@ -150,7 +169,7 @@ export function GeneralTab({ settings: _settings, onUpdate: _onUpdate }: General
|
||||
themes={themes}
|
||||
activeId={themeId}
|
||||
mode={mode}
|
||||
onSelect={setTheme}
|
||||
onSelect={handleSetTheme}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user