feat: enhance BackgroundImageSelector with custom image management

- Removed preserved custom URL handling and replaced it with a custom images array for better management of user-added backgrounds.
- Updated the component to allow adding, selecting, and removing custom images, improving user experience and flexibility.
- Adjusted background cycling logic to include custom images, ensuring a seamless integration with existing backgrounds.
This commit is contained in:
Julien Froidefond
2025-10-02 14:40:50 +02:00
parent fbb9311f9e
commit 99377ee38d
3 changed files with 93 additions and 64 deletions

View File

@@ -87,19 +87,11 @@ export function BackgroundImageSelector() {
const [customUrl, setCustomUrl] = useState('');
const [showCustomInput, setShowCustomInput] = useState(false);
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
const [preservedCustomUrl, setPreservedCustomUrl] = useState<string>('');
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 customImages = preferences?.viewPreferences?.customImages || [];
const handlePresetSelect = (presetId: string) => {
const backgroundImage = presetId === 'none' ? undefined : presetId;
@@ -110,23 +102,33 @@ export function BackgroundImageSelector() {
if (!customUrl.trim()) return;
const url = customUrl.trim();
// Ajouter l'image aux images personnalisées si elle n'existe pas déjà
if (!customImages.includes(url)) {
const newCustomImages = [...customImages, url];
updateViewPreferences({
backgroundImage: url,
customImages: newCustomImages
});
} else {
// Si elle existe déjà, juste la sélectionner
updateViewPreferences({ backgroundImage: url });
setPreservedCustomUrl(url);
localStorage.setItem('preservedCustomBackground', url);
}
setCustomUrl('');
setShowCustomInput(false);
};
const handleRemoveCustom = () => {
updateViewPreferences({ backgroundImage: undefined });
setPreservedCustomUrl('');
localStorage.removeItem('preservedCustomBackground');
const handleRemoveCustomImage = (urlToRemove: string) => {
const newCustomImages = customImages.filter(url => url !== urlToRemove);
updateViewPreferences({
customImages: newCustomImages,
backgroundImage: currentBackground === urlToRemove ? undefined : currentBackground
});
};
const handleRestoreCustom = () => {
if (preservedCustomUrl) {
updateViewPreferences({ backgroundImage: preservedCustomUrl });
}
const handleSelectCustomImage = (url: string) => {
updateViewPreferences({ backgroundImage: url });
};
const handleBlurChange = (blur: number) => {
@@ -219,6 +221,60 @@ export function BackgroundImageSelector() {
</CardContent>
</Card>
{/* Images personnalisées */}
{customImages.length > 0 && (
<Card variant="glass">
<CardContent className="p-4">
<div className="text-sm text-[var(--muted-foreground)] mb-4">Images personnalisées :</div>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
{customImages.map((url, index) => (
<div key={url} className="relative">
<Button
onClick={() => handleSelectCustomImage(url)}
variant={currentBackground === url ? 'selected' : 'secondary'}
className="p-3 h-auto text-left justify-start w-full"
>
<div className="flex items-start gap-3 w-full">
{/* Aperçu */}
<div
className="w-12 h-8 rounded border border-[var(--border)]/30 flex-shrink-0 bg-cover bg-center"
style={{ backgroundImage: `url("${url}")` }}
/>
<div className="flex-1 min-w-0 pr-8">
<div className="font-medium text-[var(--foreground)] mb-1">
Image {index + 1}
</div>
<div className="text-xs text-[var(--muted-foreground)] leading-relaxed truncate">
{url.length > 25 ? `${url.substring(0, 25)}...` : url}
</div>
{currentBackground === url && (
<div className="mt-1 text-xs text-[var(--primary)] font-medium">
Sélectionné
</div>
)}
</div>
</div>
</Button>
{/* Bouton supprimer - positionné absolument */}
<Button
onClick={(e) => {
e.stopPropagation();
handleRemoveCustomImage(url);
}}
variant="destructive"
size="sm"
className="absolute top-2 right-2 px-2 py-1 text-xs h-6 w-6 min-w-0"
>
×
</Button>
</div>
))}
</div>
</CardContent>
</Card>
)}
{/* URL personnalisée */}
<Card>
@@ -231,17 +287,6 @@ export function BackgroundImageSelector() {
Ajoutez votre propre image de fond
</p>
</div>
<div className="flex gap-2">
{preservedCustomUrl && (
<Button
onClick={handleRestoreCustom}
variant="secondary"
size="sm"
className="text-xs"
>
Restaurer
</Button>
)}
<Button
onClick={() => setShowCustomInput(!showCustomInput)}
variant="secondary"
@@ -250,7 +295,6 @@ export function BackgroundImageSelector() {
{showCustomInput ? 'Masquer' : 'Ajouter'}
</Button>
</div>
</div>
{showCustomInput && (
<div className="space-y-3">
@@ -282,17 +326,6 @@ export function BackgroundImageSelector() {
</div>
</div>
)}
{/* Bouton pour supprimer l'image personnalisée */}
{currentBackground && !PRESET_BACKGROUNDS.find(p => p.id === currentBackground) && (
<Button
onClick={handleRemoveCustom}
variant="destructive"
className="w-full"
>
Supprimer l&apos;image personnalisée
</Button>
)}
</div>
</CardContent>
</Card>

View File

@@ -23,23 +23,17 @@ export function useBackgroundCycle() {
const cycleBackground = () => {
const currentBackground = preferences?.viewPreferences?.backgroundImage;
const customImages = preferences?.viewPreferences?.customImages || [];
// Construire la liste complète des backgrounds (prédéfinis + personnalisé)
// Construire la liste complète des backgrounds (prédéfinis + personnalisés)
const allBackgrounds = [...BACKGROUND_CYCLE];
// Ajouter l'image personnalisée préservée si elle existe
const preservedCustomUrl = localStorage.getItem('preservedCustomBackground');
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);
// Ajouter toutes les images personnalisées
customImages.forEach(url => {
if (!allBackgrounds.includes(url)) {
allBackgrounds.push(url);
}
});
const currentIndex = allBackgrounds.findIndex(bg => bg === currentBackground);

View File

@@ -107,6 +107,7 @@ export interface ViewPreferences {
backgroundImage?: string;
backgroundBlur?: number;
backgroundOpacity?: number;
customImages?: string[];
[key: string]:
| boolean
| 'tags'
@@ -117,6 +118,7 @@ export interface ViewPreferences {
| 'large'
| string
| number
| string[]
| undefined;
}