From 9094aca1ffbab29928a102a4e23c44ed64938e94 Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Thu, 2 Oct 2025 13:52:18 +0200 Subject: [PATCH] feat: enhance keyboard shortcuts and background image handling - Added `GlobalKeyboardShortcuts` component to manage global keyboard shortcuts. - Introduced new keyboard shortcut (Shift + B) for changing the background. - Updated `BackgroundImageSelector` to preserve custom background URLs and allow restoration of previously set backgrounds. - Improved local storage handling for custom backgrounds to enhance user experience. --- src/app/layout.tsx | 2 + src/components/GlobalKeyboardShortcuts.tsx | 14 +++ .../settings/BackgroundImageSelector.tsx | 49 +++++++-- src/contexts/KeyboardShortcutsContext.tsx | 5 + src/hooks/useBackgroundCycle.ts | 103 ++++++++++++++++++ src/hooks/useGlobalKeyboardShortcuts.ts | 5 + 6 files changed, 169 insertions(+), 9 deletions(-) create mode 100644 src/components/GlobalKeyboardShortcuts.tsx create mode 100644 src/hooks/useBackgroundCycle.ts diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 208d79d..38dd45b 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -8,6 +8,7 @@ import { UserPreferencesProvider } from "@/contexts/UserPreferencesContext"; import { KeyboardShortcutsProvider } from "@/contexts/KeyboardShortcutsContext"; import { userPreferencesService } from "@/services/core/user-preferences"; import { KeyboardShortcuts } from "@/components/KeyboardShortcuts"; +import { GlobalKeyboardShortcuts } from "@/components/GlobalKeyboardShortcuts"; import { AuthProvider } from "../components/AuthProvider"; import { getServerSession } from 'next-auth'; import { authOptions } from '@/lib/auth'; @@ -55,6 +56,7 @@ export default async function RootLayout({ + {children} diff --git a/src/components/GlobalKeyboardShortcuts.tsx b/src/components/GlobalKeyboardShortcuts.tsx new file mode 100644 index 0000000..cf007d9 --- /dev/null +++ b/src/components/GlobalKeyboardShortcuts.tsx @@ -0,0 +1,14 @@ +'use client'; + +import { useGlobalKeyboardShortcuts } from '@/hooks/useGlobalKeyboardShortcuts'; +import { useBackgroundCycle } from '@/hooks/useBackgroundCycle'; + +export function GlobalKeyboardShortcuts() { + const { cycleBackground } = useBackgroundCycle(); + + useGlobalKeyboardShortcuts({ + onCycleBackground: cycleBackground + }); + + return null; +} diff --git a/src/components/settings/BackgroundImageSelector.tsx b/src/components/settings/BackgroundImageSelector.tsx index b9cd056..da755be 100644 --- a/src/components/settings/BackgroundImageSelector.tsx +++ b/src/components/settings/BackgroundImageSelector.tsx @@ -87,11 +87,20 @@ export function BackgroundImageSelector() { const [customUrl, setCustomUrl] = useState(''); const [showCustomInput, setShowCustomInput] = useState(false); const [showAdvancedOptions, setShowAdvancedOptions] = useState(false); + const [preservedCustomUrl, setPreservedCustomUrl] = useState(''); const currentBackground = preferences?.viewPreferences?.backgroundImage; const backgroundBlur = preferences?.viewPreferences?.backgroundBlur || 0; const backgroundOpacity = preferences?.viewPreferences?.backgroundOpacity || 100; + // Préserver l'URL personnalisée si elle existe + useEffect(() => { + if (currentBackground && !PRESET_BACKGROUNDS.some(preset => preset.id === currentBackground)) { + setPreservedCustomUrl(currentBackground); + localStorage.setItem('preservedCustomBackground', currentBackground); + } + }, [currentBackground]); + const handlePresetSelect = (presetId: string) => { const backgroundImage = presetId === 'none' ? undefined : presetId; updateViewPreferences({ backgroundImage }); @@ -100,14 +109,24 @@ export function BackgroundImageSelector() { const handleCustomUrlSubmit = () => { if (!customUrl.trim()) return; - updateViewPreferences({ backgroundImage: customUrl.trim() }); + const url = customUrl.trim(); + updateViewPreferences({ backgroundImage: url }); + setPreservedCustomUrl(url); + localStorage.setItem('preservedCustomBackground', url); setCustomUrl(''); setShowCustomInput(false); }; - const handleRemoveCustom = () => { updateViewPreferences({ backgroundImage: undefined }); + setPreservedCustomUrl(''); + localStorage.removeItem('preservedCustomBackground'); + }; + + const handleRestoreCustom = () => { + if (preservedCustomUrl) { + updateViewPreferences({ backgroundImage: preservedCustomUrl }); + } }; const handleBlurChange = (blur: number) => { @@ -212,13 +231,25 @@ export function BackgroundImageSelector() { Ajoutez votre propre image de fond

- +
+ {preservedCustomUrl && ( + + )} + +
{showCustomInput && ( diff --git a/src/contexts/KeyboardShortcutsContext.tsx b/src/contexts/KeyboardShortcutsContext.tsx index 66655fa..45ae910 100644 --- a/src/contexts/KeyboardShortcutsContext.tsx +++ b/src/contexts/KeyboardShortcutsContext.tsx @@ -32,6 +32,11 @@ const PAGE_SHORTCUTS: PageShortcuts = { description: 'Faire tourner les thèmes dark', category: 'Apparence' }, + { + keys: ['Shift', 'B'], + description: 'Changer le background', + category: 'Apparence' + }, { keys: ['Esc'], description: 'Fermer les modales/annuler', diff --git a/src/hooks/useBackgroundCycle.ts b/src/hooks/useBackgroundCycle.ts new file mode 100644 index 0000000..1e93a90 --- /dev/null +++ b/src/hooks/useBackgroundCycle.ts @@ -0,0 +1,103 @@ +'use client'; + +import { useUserPreferences } from '@/contexts/UserPreferencesContext'; + +// Liste des backgrounds prédéfinis pour le cycle +const BACKGROUND_CYCLE = [ + 'none', + 'theme-subtle', + 'theme-primary', + 'theme-accent', + 'theme-success', + 'theme-purple', + 'theme-diagonal', + 'theme-radial', + 'theme-sunset', + 'theme-ocean', + 'theme-forest', + 'theme-galaxy' +]; + +export function useBackgroundCycle() { + const { preferences, updateViewPreferences } = useUserPreferences(); + + const cycleBackground = () => { + const currentBackground = preferences?.viewPreferences?.backgroundImage; + + // Construire la liste complète des backgrounds (prédéfinis + personnalisé) + const allBackgrounds = [...BACKGROUND_CYCLE]; + + // Ajouter l'image personnalisée préservée si elle existe + const preservedCustomUrl = localStorage.getItem('preservedCustomBackground'); + console.log('Debug cycle:', { + currentBackground, + preservedCustomUrl, + localStorageKeys: Object.keys(localStorage) + }); + + if (preservedCustomUrl && !BACKGROUND_CYCLE.includes(preservedCustomUrl)) { + allBackgrounds.push(preservedCustomUrl); + } + + // Ajouter aussi l'image actuelle si elle est personnalisée ET différente de celle préservée + if (currentBackground && + !BACKGROUND_CYCLE.includes(currentBackground) && + currentBackground !== preservedCustomUrl) { + allBackgrounds.push(currentBackground); + } + + const currentIndex = allBackgrounds.findIndex(bg => bg === currentBackground); + + // Si on ne trouve pas l'index, c'est qu'on est sur "none" (undefined) + // Dans ce cas, on commence au début de la liste + let actualCurrentIndex = currentIndex; + if (currentIndex === -1) { + actualCurrentIndex = -1; // On est sur "none", on va commencer à l'index 0 + } + + const nextIndex = (actualCurrentIndex + 1) % allBackgrounds.length; + const nextBackground = allBackgrounds[nextIndex]; + + // Si c'est 'none', on met undefined + const backgroundImage = nextBackground === 'none' ? undefined : nextBackground; + + // Si on est sur "none" (undefined) et qu'on va vers "none", on va vers le suivant + if (currentBackground === undefined && nextBackground === 'none') { + const nextNextIndex = (nextIndex + 1) % allBackgrounds.length; + const nextNextBackground = allBackgrounds[nextNextIndex]; + const finalBackgroundImage = nextNextBackground === 'none' ? undefined : nextNextBackground; + + console.log('Cycle result:', { + allBackgrounds, + currentIndex, + actualCurrentIndex, + nextIndex, + nextBackground, + backgroundImage, + currentBackground, + nextNextIndex, + nextNextBackground, + finalBackgroundImage + }); + + updateViewPreferences({ backgroundImage: finalBackgroundImage }); + return; + } + + console.log('Cycle result:', { + allBackgrounds, + currentIndex, + actualCurrentIndex, + nextIndex, + nextBackground, + backgroundImage, + currentBackground + }); + + updateViewPreferences({ backgroundImage }); + }; + + return { + cycleBackground + }; +} diff --git a/src/hooks/useGlobalKeyboardShortcuts.ts b/src/hooks/useGlobalKeyboardShortcuts.ts index acf657f..2f9b153 100644 --- a/src/hooks/useGlobalKeyboardShortcuts.ts +++ b/src/hooks/useGlobalKeyboardShortcuts.ts @@ -21,6 +21,7 @@ interface KeyboardShortcutsActions { onSave?: () => void; onBackup?: () => void; onOpenSearch?: () => void; + onCycleBackground?: () => void; } export function useGlobalKeyboardShortcuts(actions: KeyboardShortcutsActions = {}) { @@ -56,6 +57,10 @@ export function useGlobalKeyboardShortcuts(actions: KeyboardShortcutsActions = { event.preventDefault(); actions.onOpenSearch?.(); return; + case 'B': + event.preventDefault(); + actions.onCycleBackground?.(); + return; } }