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:
@@ -87,19 +87,11 @@ export function BackgroundImageSelector() {
|
|||||||
const [customUrl, setCustomUrl] = useState('');
|
const [customUrl, setCustomUrl] = useState('');
|
||||||
const [showCustomInput, setShowCustomInput] = useState(false);
|
const [showCustomInput, setShowCustomInput] = useState(false);
|
||||||
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
|
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
|
||||||
const [preservedCustomUrl, setPreservedCustomUrl] = useState<string>('');
|
|
||||||
|
|
||||||
const currentBackground = preferences?.viewPreferences?.backgroundImage;
|
const currentBackground = preferences?.viewPreferences?.backgroundImage;
|
||||||
const backgroundBlur = preferences?.viewPreferences?.backgroundBlur || 0;
|
const backgroundBlur = preferences?.viewPreferences?.backgroundBlur || 0;
|
||||||
const backgroundOpacity = preferences?.viewPreferences?.backgroundOpacity || 100;
|
const backgroundOpacity = preferences?.viewPreferences?.backgroundOpacity || 100;
|
||||||
|
const customImages = preferences?.viewPreferences?.customImages || [];
|
||||||
// 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 handlePresetSelect = (presetId: string) => {
|
||||||
const backgroundImage = presetId === 'none' ? undefined : presetId;
|
const backgroundImage = presetId === 'none' ? undefined : presetId;
|
||||||
@@ -110,23 +102,33 @@ export function BackgroundImageSelector() {
|
|||||||
if (!customUrl.trim()) return;
|
if (!customUrl.trim()) return;
|
||||||
|
|
||||||
const url = customUrl.trim();
|
const url = customUrl.trim();
|
||||||
updateViewPreferences({ backgroundImage: url });
|
|
||||||
setPreservedCustomUrl(url);
|
// Ajouter l'image aux images personnalisées si elle n'existe pas déjà
|
||||||
localStorage.setItem('preservedCustomBackground', url);
|
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 });
|
||||||
|
}
|
||||||
|
|
||||||
setCustomUrl('');
|
setCustomUrl('');
|
||||||
setShowCustomInput(false);
|
setShowCustomInput(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveCustom = () => {
|
const handleRemoveCustomImage = (urlToRemove: string) => {
|
||||||
updateViewPreferences({ backgroundImage: undefined });
|
const newCustomImages = customImages.filter(url => url !== urlToRemove);
|
||||||
setPreservedCustomUrl('');
|
updateViewPreferences({
|
||||||
localStorage.removeItem('preservedCustomBackground');
|
customImages: newCustomImages,
|
||||||
|
backgroundImage: currentBackground === urlToRemove ? undefined : currentBackground
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRestoreCustom = () => {
|
const handleSelectCustomImage = (url: string) => {
|
||||||
if (preservedCustomUrl) {
|
updateViewPreferences({ backgroundImage: url });
|
||||||
updateViewPreferences({ backgroundImage: preservedCustomUrl });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBlurChange = (blur: number) => {
|
const handleBlurChange = (blur: number) => {
|
||||||
@@ -219,6 +221,60 @@ export function BackgroundImageSelector() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</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 */}
|
{/* URL personnalisée */}
|
||||||
<Card>
|
<Card>
|
||||||
@@ -231,25 +287,13 @@ export function BackgroundImageSelector() {
|
|||||||
Ajoutez votre propre image de fond
|
Ajoutez votre propre image de fond
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<Button
|
||||||
{preservedCustomUrl && (
|
onClick={() => setShowCustomInput(!showCustomInput)}
|
||||||
<Button
|
variant="secondary"
|
||||||
onClick={handleRestoreCustom}
|
size="sm"
|
||||||
variant="secondary"
|
>
|
||||||
size="sm"
|
{showCustomInput ? 'Masquer' : 'Ajouter'}
|
||||||
className="text-xs"
|
</Button>
|
||||||
>
|
|
||||||
Restaurer
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Button
|
|
||||||
onClick={() => setShowCustomInput(!showCustomInput)}
|
|
||||||
variant="secondary"
|
|
||||||
size="sm"
|
|
||||||
>
|
|
||||||
{showCustomInput ? 'Masquer' : 'Ajouter'}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showCustomInput && (
|
{showCustomInput && (
|
||||||
@@ -282,17 +326,6 @@ export function BackgroundImageSelector() {
|
|||||||
</div>
|
</div>
|
||||||
</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'image personnalisée
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -23,23 +23,17 @@ export function useBackgroundCycle() {
|
|||||||
|
|
||||||
const cycleBackground = () => {
|
const cycleBackground = () => {
|
||||||
const currentBackground = preferences?.viewPreferences?.backgroundImage;
|
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];
|
const allBackgrounds = [...BACKGROUND_CYCLE];
|
||||||
|
|
||||||
// Ajouter l'image personnalisée préservée si elle existe
|
// Ajouter toutes les images personnalisées
|
||||||
const preservedCustomUrl = localStorage.getItem('preservedCustomBackground');
|
customImages.forEach(url => {
|
||||||
|
if (!allBackgrounds.includes(url)) {
|
||||||
if (preservedCustomUrl && !BACKGROUND_CYCLE.includes(preservedCustomUrl)) {
|
allBackgrounds.push(url);
|
||||||
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);
|
const currentIndex = allBackgrounds.findIndex(bg => bg === currentBackground);
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ export interface ViewPreferences {
|
|||||||
backgroundImage?: string;
|
backgroundImage?: string;
|
||||||
backgroundBlur?: number;
|
backgroundBlur?: number;
|
||||||
backgroundOpacity?: number;
|
backgroundOpacity?: number;
|
||||||
|
customImages?: string[];
|
||||||
[key: string]:
|
[key: string]:
|
||||||
| boolean
|
| boolean
|
||||||
| 'tags'
|
| 'tags'
|
||||||
@@ -117,6 +118,7 @@ export interface ViewPreferences {
|
|||||||
| 'large'
|
| 'large'
|
||||||
| string
|
| string
|
||||||
| number
|
| number
|
||||||
|
| string[]
|
||||||
| undefined;
|
| undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user