mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-29 07:05:14 -07:00
fix: clean up scroll effect timers and fix disabled+selected card toggle
- Add cleanup for requestAnimationFrame and setTimeout in scroll effect to prevent stale DOM writes on unmount or rapid selection changes - Fix disabled+selected card click: bounce the selection to re-trigger the engine auto-switch instead of deselecting Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
48cd1f369a
commit
c9e5c5d9a7
@@ -41,6 +41,12 @@ export function ProfileCard({ profile, disabled }: ProfileCardProps) {
|
|||||||
const isSelected = selectedProfileId === profile.id;
|
const isSelected = selectedProfileId === profile.id;
|
||||||
|
|
||||||
const handleSelect = () => {
|
const handleSelect = () => {
|
||||||
|
// If disabled but already selected, bounce the selection to re-trigger engine auto-switch
|
||||||
|
if (disabled && isSelected) {
|
||||||
|
setSelectedProfileId(null);
|
||||||
|
setTimeout(() => setSelectedProfileId(profile.id), 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setSelectedProfileId(isSelected ? null : profile.id);
|
setSelectedProfileId(isSelected ? null : profile.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,17 +20,20 @@ export function ProfileList() {
|
|||||||
// Scroll to the selected profile after engine/sort changes
|
// Scroll to the selected profile after engine/sort changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!selectedProfileId) return;
|
if (!selectedProfileId) return;
|
||||||
// Wait a frame for the DOM to update after re-sort
|
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||||
requestAnimationFrame(() => {
|
const rafId = requestAnimationFrame(() => {
|
||||||
const el = cardRefs.current.get(selectedProfileId);
|
const el = cardRefs.current.get(selectedProfileId);
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
|
|
||||||
// Temporarily apply scroll-margin so it doesn't land flush at the top
|
// Temporarily apply scroll-margin so it doesn't land flush at the top
|
||||||
el.style.scrollMarginTop = '180px';
|
el.style.scrollMarginTop = '180px';
|
||||||
el.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
|
el.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
|
||||||
// Clean up after scroll completes
|
timeoutId = setTimeout(() => { el.style.scrollMarginTop = ''; }, 500);
|
||||||
setTimeout(() => { el.style.scrollMarginTop = ''; }, 500);
|
|
||||||
});
|
});
|
||||||
|
return () => {
|
||||||
|
cancelAnimationFrame(rafId);
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
|
};
|
||||||
}, [selectedProfileId, selectedEngine]);
|
}, [selectedProfileId, selectedEngine]);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|||||||
Reference in New Issue
Block a user