mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 08:53:36 +00:00
feat(ui): enhance BoardBackgroundModal with local state management for opacity sliders
- Implemented local state for card, column, and card border opacity during slider dragging to improve user experience. - Added useEffect to sync local state with store settings when not dragging. - Updated handlers to commit changes to the store and persist settings upon slider release. - Adjusted UI to reflect local state values for opacity sliders, ensuring immediate feedback during adjustments.
This commit is contained in:
@@ -45,6 +45,8 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
|
|||||||
setCardBorderOpacity,
|
setCardBorderOpacity,
|
||||||
setHideScrollbar,
|
setHideScrollbar,
|
||||||
clearBoardBackground,
|
clearBoardBackground,
|
||||||
|
persistSettings,
|
||||||
|
getCurrentSettings,
|
||||||
} = useBoardBackgroundSettings();
|
} = useBoardBackgroundSettings();
|
||||||
const [isDragOver, setIsDragOver] = useState(false);
|
const [isDragOver, setIsDragOver] = useState(false);
|
||||||
const [isProcessing, setIsProcessing] = useState(false);
|
const [isProcessing, setIsProcessing] = useState(false);
|
||||||
@@ -55,12 +57,31 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
|
|||||||
const backgroundSettings =
|
const backgroundSettings =
|
||||||
(currentProject && boardBackgroundByProject[currentProject.path]) || defaultBackgroundSettings;
|
(currentProject && boardBackgroundByProject[currentProject.path]) || defaultBackgroundSettings;
|
||||||
|
|
||||||
const cardOpacity = backgroundSettings.cardOpacity;
|
// Local state for sliders during dragging (avoids store updates during drag)
|
||||||
const columnOpacity = backgroundSettings.columnOpacity;
|
const [localCardOpacity, setLocalCardOpacity] = useState(backgroundSettings.cardOpacity);
|
||||||
|
const [localColumnOpacity, setLocalColumnOpacity] = useState(backgroundSettings.columnOpacity);
|
||||||
|
const [localCardBorderOpacity, setLocalCardBorderOpacity] = useState(
|
||||||
|
backgroundSettings.cardBorderOpacity
|
||||||
|
);
|
||||||
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
|
|
||||||
|
// Sync local state with store when not dragging (e.g., on modal open or external changes)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isDragging) {
|
||||||
|
setLocalCardOpacity(backgroundSettings.cardOpacity);
|
||||||
|
setLocalColumnOpacity(backgroundSettings.columnOpacity);
|
||||||
|
setLocalCardBorderOpacity(backgroundSettings.cardBorderOpacity);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
isDragging,
|
||||||
|
backgroundSettings.cardOpacity,
|
||||||
|
backgroundSettings.columnOpacity,
|
||||||
|
backgroundSettings.cardBorderOpacity,
|
||||||
|
]);
|
||||||
|
|
||||||
const columnBorderEnabled = backgroundSettings.columnBorderEnabled;
|
const columnBorderEnabled = backgroundSettings.columnBorderEnabled;
|
||||||
const cardGlassmorphism = backgroundSettings.cardGlassmorphism;
|
const cardGlassmorphism = backgroundSettings.cardGlassmorphism;
|
||||||
const cardBorderEnabled = backgroundSettings.cardBorderEnabled;
|
const cardBorderEnabled = backgroundSettings.cardBorderEnabled;
|
||||||
const cardBorderOpacity = backgroundSettings.cardBorderOpacity;
|
|
||||||
const hideScrollbar = backgroundSettings.hideScrollbar;
|
const hideScrollbar = backgroundSettings.hideScrollbar;
|
||||||
const imageVersion = backgroundSettings.imageVersion;
|
const imageVersion = backgroundSettings.imageVersion;
|
||||||
|
|
||||||
@@ -198,21 +219,40 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
|
|||||||
}
|
}
|
||||||
}, [currentProject, clearBoardBackground]);
|
}, [currentProject, clearBoardBackground]);
|
||||||
|
|
||||||
// Live update opacity when sliders change (with persistence)
|
// Live update local state during drag (modal-only, no store update)
|
||||||
const handleCardOpacityChange = useCallback(
|
const handleCardOpacityChange = useCallback((value: number[]) => {
|
||||||
async (value: number[]) => {
|
setIsDragging(true);
|
||||||
|
setLocalCardOpacity(value[0]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Update store and persist when slider is released
|
||||||
|
const handleCardOpacityCommit = useCallback(
|
||||||
|
(value: number[]) => {
|
||||||
if (!currentProject) return;
|
if (!currentProject) return;
|
||||||
await setCardOpacity(currentProject.path, value[0]);
|
setIsDragging(false);
|
||||||
|
setCardOpacity(currentProject.path, value[0]);
|
||||||
|
const current = getCurrentSettings(currentProject.path);
|
||||||
|
persistSettings(currentProject.path, { ...current, cardOpacity: value[0] });
|
||||||
},
|
},
|
||||||
[currentProject, setCardOpacity]
|
[currentProject, setCardOpacity, getCurrentSettings, persistSettings]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleColumnOpacityChange = useCallback(
|
// Live update local state during drag (modal-only, no store update)
|
||||||
async (value: number[]) => {
|
const handleColumnOpacityChange = useCallback((value: number[]) => {
|
||||||
|
setIsDragging(true);
|
||||||
|
setLocalColumnOpacity(value[0]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Update store and persist when slider is released
|
||||||
|
const handleColumnOpacityCommit = useCallback(
|
||||||
|
(value: number[]) => {
|
||||||
if (!currentProject) return;
|
if (!currentProject) return;
|
||||||
await setColumnOpacity(currentProject.path, value[0]);
|
setIsDragging(false);
|
||||||
|
setColumnOpacity(currentProject.path, value[0]);
|
||||||
|
const current = getCurrentSettings(currentProject.path);
|
||||||
|
persistSettings(currentProject.path, { ...current, columnOpacity: value[0] });
|
||||||
},
|
},
|
||||||
[currentProject, setColumnOpacity]
|
[currentProject, setColumnOpacity, getCurrentSettings, persistSettings]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleColumnBorderToggle = useCallback(
|
const handleColumnBorderToggle = useCallback(
|
||||||
@@ -239,12 +279,22 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
|
|||||||
[currentProject, setCardBorderEnabled]
|
[currentProject, setCardBorderEnabled]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCardBorderOpacityChange = useCallback(
|
// Live update local state during drag (modal-only, no store update)
|
||||||
async (value: number[]) => {
|
const handleCardBorderOpacityChange = useCallback((value: number[]) => {
|
||||||
|
setIsDragging(true);
|
||||||
|
setLocalCardBorderOpacity(value[0]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Update store and persist when slider is released
|
||||||
|
const handleCardBorderOpacityCommit = useCallback(
|
||||||
|
(value: number[]) => {
|
||||||
if (!currentProject) return;
|
if (!currentProject) return;
|
||||||
await setCardBorderOpacity(currentProject.path, value[0]);
|
setIsDragging(false);
|
||||||
|
setCardBorderOpacity(currentProject.path, value[0]);
|
||||||
|
const current = getCurrentSettings(currentProject.path);
|
||||||
|
persistSettings(currentProject.path, { ...current, cardBorderOpacity: value[0] });
|
||||||
},
|
},
|
||||||
[currentProject, setCardBorderOpacity]
|
[currentProject, setCardBorderOpacity, getCurrentSettings, persistSettings]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleHideScrollbarToggle = useCallback(
|
const handleHideScrollbarToggle = useCallback(
|
||||||
@@ -378,11 +428,12 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Label>Card Opacity</Label>
|
<Label>Card Opacity</Label>
|
||||||
<span className="text-sm text-muted-foreground">{cardOpacity}%</span>
|
<span className="text-sm text-muted-foreground">{localCardOpacity}%</span>
|
||||||
</div>
|
</div>
|
||||||
<Slider
|
<Slider
|
||||||
value={[cardOpacity]}
|
value={[localCardOpacity]}
|
||||||
onValueChange={handleCardOpacityChange}
|
onValueChange={handleCardOpacityChange}
|
||||||
|
onValueCommit={handleCardOpacityCommit}
|
||||||
min={0}
|
min={0}
|
||||||
max={100}
|
max={100}
|
||||||
step={1}
|
step={1}
|
||||||
@@ -393,11 +444,12 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Label>Column Opacity</Label>
|
<Label>Column Opacity</Label>
|
||||||
<span className="text-sm text-muted-foreground">{columnOpacity}%</span>
|
<span className="text-sm text-muted-foreground">{localColumnOpacity}%</span>
|
||||||
</div>
|
</div>
|
||||||
<Slider
|
<Slider
|
||||||
value={[columnOpacity]}
|
value={[localColumnOpacity]}
|
||||||
onValueChange={handleColumnOpacityChange}
|
onValueChange={handleColumnOpacityChange}
|
||||||
|
onValueCommit={handleColumnOpacityCommit}
|
||||||
min={0}
|
min={0}
|
||||||
max={100}
|
max={100}
|
||||||
step={1}
|
step={1}
|
||||||
@@ -446,11 +498,12 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Label>Card Border Opacity</Label>
|
<Label>Card Border Opacity</Label>
|
||||||
<span className="text-sm text-muted-foreground">{cardBorderOpacity}%</span>
|
<span className="text-sm text-muted-foreground">{localCardBorderOpacity}%</span>
|
||||||
</div>
|
</div>
|
||||||
<Slider
|
<Slider
|
||||||
value={[cardBorderOpacity]}
|
value={[localCardBorderOpacity]}
|
||||||
onValueChange={handleCardBorderOpacityChange}
|
onValueChange={handleCardBorderOpacityChange}
|
||||||
|
onValueCommit={handleCardBorderOpacityCommit}
|
||||||
min={0}
|
min={0}
|
||||||
max={100}
|
max={100}
|
||||||
step={1}
|
step={1}
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ export function useUpdateGlobalSettings(options: UpdateGlobalSettingsOptions = {
|
|||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async (settings: Record<string, unknown>) => {
|
mutationFn: async (settings: Record<string, unknown>) => {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
|
if (!api.settings) {
|
||||||
|
throw new Error('Settings API not available');
|
||||||
|
}
|
||||||
// Use updateGlobal for partial updates
|
// Use updateGlobal for partial updates
|
||||||
const result = await api.settings.updateGlobal(settings);
|
const result = await api.settings.updateGlobal(settings);
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
@@ -66,33 +69,43 @@ export function useUpdateGlobalSettings(options: UpdateGlobalSettingsOptions = {
|
|||||||
* @param projectPath - Optional path to the project (can also pass via mutation variables)
|
* @param projectPath - Optional path to the project (can also pass via mutation variables)
|
||||||
* @returns Mutation for updating project settings
|
* @returns Mutation for updating project settings
|
||||||
*/
|
*/
|
||||||
|
interface ProjectSettingsWithPath {
|
||||||
|
projectPath: string;
|
||||||
|
settings: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
export function useUpdateProjectSettings(projectPath?: string) {
|
export function useUpdateProjectSettings(projectPath?: string) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async (
|
mutationFn: async (variables: Record<string, unknown> | ProjectSettingsWithPath) => {
|
||||||
variables:
|
|
||||||
| Record<string, unknown>
|
|
||||||
| { projectPath: string; settings: Record<string, unknown> }
|
|
||||||
) => {
|
|
||||||
// Support both call patterns:
|
// Support both call patterns:
|
||||||
// 1. useUpdateProjectSettings(projectPath) then mutate(settings)
|
// 1. useUpdateProjectSettings(projectPath) then mutate(settings)
|
||||||
// 2. useUpdateProjectSettings() then mutate({ projectPath, settings })
|
// 2. useUpdateProjectSettings() then mutate({ projectPath, settings })
|
||||||
let path: string;
|
let path: string;
|
||||||
let settings: Record<string, unknown>;
|
let settings: Record<string, unknown>;
|
||||||
|
|
||||||
if ('projectPath' in variables && 'settings' in variables) {
|
if (
|
||||||
|
typeof variables === 'object' &&
|
||||||
|
'projectPath' in variables &&
|
||||||
|
'settings' in variables &&
|
||||||
|
typeof variables.projectPath === 'string' &&
|
||||||
|
typeof variables.settings === 'object'
|
||||||
|
) {
|
||||||
path = variables.projectPath;
|
path = variables.projectPath;
|
||||||
settings = variables.settings;
|
settings = variables.settings as Record<string, unknown>;
|
||||||
} else if (projectPath) {
|
} else if (projectPath) {
|
||||||
path = projectPath;
|
path = projectPath;
|
||||||
settings = variables;
|
settings = variables as Record<string, unknown>;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Project path is required');
|
throw new Error('Project path is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
const result = await api.settings.setProject(path, settings);
|
if (!api.settings) {
|
||||||
|
throw new Error('Settings API not available');
|
||||||
|
}
|
||||||
|
const result = await api.settings.updateProject(path, settings);
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
throw new Error(result.error || 'Failed to update project settings');
|
throw new Error(result.error || 'Failed to update project settings');
|
||||||
}
|
}
|
||||||
@@ -122,9 +135,12 @@ export function useSaveCredentials() {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async (credentials: Record<string, string>) => {
|
mutationFn: async (credentials: { anthropic?: string; google?: string; openai?: string }) => {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
const result = await api.settings.setCredentials(credentials);
|
if (!api.settings) {
|
||||||
|
throw new Error('Settings API not available');
|
||||||
|
}
|
||||||
|
const result = await api.settings.updateCredentials({ apiKeys: credentials });
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
throw new Error(result.error || 'Failed to save credentials');
|
throw new Error(result.error || 'Failed to save credentials');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import { useUpdateProjectSettings } from '@/hooks/mutations';
|
|||||||
/**
|
/**
|
||||||
* Hook for managing board background settings with automatic persistence to server.
|
* Hook for managing board background settings with automatic persistence to server.
|
||||||
* Uses React Query mutation for server persistence with automatic error handling.
|
* Uses React Query mutation for server persistence with automatic error handling.
|
||||||
|
*
|
||||||
|
* For sliders, the modal uses local state during dragging and calls:
|
||||||
|
* - setCardOpacity/setColumnOpacity/setCardBorderOpacity to update store on commit
|
||||||
|
* - persistSettings directly to save to server on commit
|
||||||
*/
|
*/
|
||||||
export function useBoardBackgroundSettings() {
|
export function useBoardBackgroundSettings() {
|
||||||
const store = useAppStore();
|
const store = useAppStore();
|
||||||
@@ -65,22 +69,20 @@ export function useBoardBackgroundSettings() {
|
|||||||
[store, persistSettings, getCurrentSettings]
|
[store, persistSettings, getCurrentSettings]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Update store (called on slider commit to update the board view)
|
||||||
const setCardOpacity = useCallback(
|
const setCardOpacity = useCallback(
|
||||||
async (projectPath: string, opacity: number) => {
|
(projectPath: string, opacity: number) => {
|
||||||
const current = getCurrentSettings(projectPath);
|
|
||||||
store.setCardOpacity(projectPath, opacity);
|
store.setCardOpacity(projectPath, opacity);
|
||||||
await persistSettings(projectPath, { ...current, cardOpacity: opacity });
|
|
||||||
},
|
},
|
||||||
[store, persistSettings, getCurrentSettings]
|
[store]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Update store (called on slider commit to update the board view)
|
||||||
const setColumnOpacity = useCallback(
|
const setColumnOpacity = useCallback(
|
||||||
async (projectPath: string, opacity: number) => {
|
(projectPath: string, opacity: number) => {
|
||||||
const current = getCurrentSettings(projectPath);
|
|
||||||
store.setColumnOpacity(projectPath, opacity);
|
store.setColumnOpacity(projectPath, opacity);
|
||||||
await persistSettings(projectPath, { ...current, columnOpacity: opacity });
|
|
||||||
},
|
},
|
||||||
[store, persistSettings, getCurrentSettings]
|
[store]
|
||||||
);
|
);
|
||||||
|
|
||||||
const setColumnBorderEnabled = useCallback(
|
const setColumnBorderEnabled = useCallback(
|
||||||
@@ -119,16 +121,12 @@ export function useBoardBackgroundSettings() {
|
|||||||
[store, persistSettings, getCurrentSettings]
|
[store, persistSettings, getCurrentSettings]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Update store (called on slider commit to update the board view)
|
||||||
const setCardBorderOpacity = useCallback(
|
const setCardBorderOpacity = useCallback(
|
||||||
async (projectPath: string, opacity: number) => {
|
(projectPath: string, opacity: number) => {
|
||||||
const current = getCurrentSettings(projectPath);
|
|
||||||
store.setCardBorderOpacity(projectPath, opacity);
|
store.setCardBorderOpacity(projectPath, opacity);
|
||||||
await persistSettings(projectPath, {
|
|
||||||
...current,
|
|
||||||
cardBorderOpacity: opacity,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[store, persistSettings, getCurrentSettings]
|
[store]
|
||||||
);
|
);
|
||||||
|
|
||||||
const setHideScrollbar = useCallback(
|
const setHideScrollbar = useCallback(
|
||||||
@@ -170,5 +168,6 @@ export function useBoardBackgroundSettings() {
|
|||||||
setHideScrollbar,
|
setHideScrollbar,
|
||||||
clearBoardBackground,
|
clearBoardBackground,
|
||||||
getCurrentSettings,
|
getCurrentSettings,
|
||||||
|
persistSettings,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user