feat: add onChange handlers to ChipSelect and Text components; clear imageQueue on background type and category changes

This commit is contained in:
alexsparkes
2025-10-31 16:42:15 +00:00
parent ef22e91b07
commit b303d02492
6 changed files with 71 additions and 14 deletions

View File

@@ -1,11 +1,11 @@
import { memo } from 'react';
import { memo, useMemo } from 'react';
/**
* BackgroundVideo component for rendering video backgrounds
*/
function BackgroundVideo({ url, filterStyle }) {
const isMuted = localStorage.getItem('backgroundVideoMute') === 'true';
const shouldLoop = localStorage.getItem('backgroundVideoLoop') === 'true';
const isMuted = useMemo(() => localStorage.getItem('backgroundVideoMute') === 'true', []);
const shouldLoop = useMemo(() => localStorage.getItem('backgroundVideoLoop') === 'true', []);
return (
<video

View File

@@ -1,13 +1,17 @@
import { useEffect, useRef } from 'react';
import { useEffect, useRef, useMemo } from 'react';
import { createBlobUrl } from '../api/blobUrl';
import { generateBlurHashDataUrl } from '../api/blurHash';
import { getBackgroundFilterStyle, getBackgroundOverlayStyle } from '../api/backgroundFilters';
// Constants
const TRANSITION_DURATION = 1200; // milliseconds
/**
* Hook for rendering backgrounds to the DOM
*/
export function useBackgroundRenderer(backgroundData) {
const blobRef = useRef(null);
const abortControllerRef = useRef(null);
useEffect(() => {
if (backgroundData.video) return;
@@ -15,11 +19,18 @@ export function useBackgroundRenderer(backgroundData) {
const element = document.getElementById('backgroundImage');
if (!element) return;
// Abort any pending image loads
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
abortControllerRef.current = new AbortController();
const applyBackground = async () => {
if (backgroundData.url) {
const hasTransition = localStorage.getItem('bgtransition') !== 'false';
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!hasTransition) {
if (!hasTransition || prefersReducedMotion) {
element.style.background = `url(${backgroundData.url})`;
document.querySelector('.photoInformation')?.style.setProperty('display', 'flex');
return;
@@ -82,7 +93,7 @@ export function useBackgroundRenderer(backgroundData) {
void overlay.offsetHeight;
// Re-enable transition
overlay.style.transition = 'opacity 1.2s ease-in-out';
overlay.style.transition = `opacity ${TRANSITION_DURATION / 1000}s ease-in-out`;
// Force another reflow before changing opacity
void overlay.offsetHeight;
@@ -95,7 +106,7 @@ export function useBackgroundRenderer(backgroundData) {
element.style.backgroundImage = `url(${finalUrl})`;
overlay.style.opacity = '0';
overlay.style.backgroundImage = '';
}, 1300);
}, TRANSITION_DURATION + 100);
} else if (backgroundData.style) {
element.setAttribute('style', backgroundData.style);
}
@@ -104,7 +115,21 @@ export function useBackgroundRenderer(backgroundData) {
applyBackground();
return () => {
if (blobRef.current) URL.revokeObjectURL(blobRef.current);
// Cleanup blob URL
if (blobRef.current) {
URL.revokeObjectURL(blobRef.current);
}
// Abort pending image loads
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
// Remove overlay element to prevent memory leak
const overlay = document.getElementById('backgroundOverlay');
if (overlay) {
overlay.remove();
}
};
}, [backgroundData.url, backgroundData.style, backgroundData.video, backgroundData.photoInfo]);
}
@@ -113,12 +138,12 @@ export function useBackgroundRenderer(backgroundData) {
* Hook to get computed filter styles (legacy - for video backgrounds)
*/
export function useBackgroundFilters() {
return { WebkitFilter: getBackgroundFilterStyle() };
return useMemo(() => ({ WebkitFilter: getBackgroundFilterStyle() }), []);
}
/**
* Hook to get computed overlay filter styles
*/
export function useBackgroundOverlayFilters() {
return getBackgroundOverlayStyle();
return useMemo(() => getBackgroundOverlayStyle(), []);
}

View File

@@ -57,6 +57,8 @@ const BackgroundOptions = memo(() => {
const updateAPI = useCallback((e) => {
localStorage.setItem('nextImage', null);
// Clear prefetch queue when API changes to prevent showing cached images from old API
localStorage.removeItem('imageQueue');
if (e === 'mue') {
setBackgroundCategories(backgroundCategoriesOG);
setBackgroundAPI('mue');
@@ -177,7 +179,11 @@ const BackgroundOptions = memo(() => {
<Dropdown
label={variables.getMessage('modals.main.settings.sections.background.type.title')}
name="backgroundType"
onChange={(value) => setBackgroundType(value)}
onChange={(value) => {
// Clear prefetch queue when changing background type
localStorage.removeItem('imageQueue');
setBackgroundType(value);
}}
category="background"
items={getBackgroundOptionItems(marketplaceEnabled)}
/>
@@ -208,7 +214,11 @@ const BackgroundOptions = memo(() => {
<SourceSection
backgroundType={backgroundType}
marketplaceEnabled={marketplaceEnabled}
onTypeChange={(value) => setBackgroundType(value)}
onTypeChange={(value) => {
// Clear prefetch queue when changing background type
localStorage.removeItem('imageQueue');
setBackgroundType(value);
}}
/>
{getBackgroundSettings()}
</>

View File

@@ -34,6 +34,10 @@ const APISettings = ({ backgroundAPI, backgroundCategories, onUpdateAPI }) => {
label={variables.getMessage('modals.main.settings.sections.background.categories')}
options={backgroundCategories}
name="apiCategories"
onChange={() => {
// Clear prefetch queue when categories change
localStorage.removeItem('imageQueue');
}}
/>
)}
<Dropdown
@@ -43,6 +47,10 @@ const APISettings = ({ backgroundAPI, backgroundCategories, onUpdateAPI }) => {
name="apiQuality"
element=".other"
items={APIQualityOptions}
onChange={() => {
// Clear prefetch queue when quality changes
localStorage.removeItem('imageQueue');
}}
/>
<Radio
title="API"
@@ -81,6 +89,10 @@ const APISettings = ({ backgroundAPI, backgroundCategories, onUpdateAPI }) => {
name="unsplashCollections"
category="background"
element="#backgroundImage"
onChange={() => {
// Clear prefetch queue when Unsplash collections change
localStorage.removeItem('imageQueue');
}}
/>
</Action>
</Row>