fix: prevent guest link icon from re-animating on WebSocket updates
The external guest link icon was fading in/out on every WebSocket update, creating a distracting visual effect. Now the fade-in animation only plays when the URL first becomes available (initial load or when saving a new URL), rather than re-triggering on every state update. - Add fadeIn keyframe animation to Tailwind config - Track animation state with shouldAnimateIcon signal - Only animate on transition from no URL to having a URL - Clear animation flag after 200ms to prevent re-triggering
This commit is contained in:
parent
f20eccee86
commit
1ae4f5bfd8
2 changed files with 28 additions and 2 deletions
|
|
@ -61,6 +61,7 @@ export function GuestRow(props: GuestRowProps) {
|
||||||
const isEditingUrl = createMemo(() => currentlyEditingGuestId() === guestId());
|
const isEditingUrl = createMemo(() => currentlyEditingGuestId() === guestId());
|
||||||
|
|
||||||
const [customUrl, setCustomUrl] = createSignal<string | undefined>(props.customUrl);
|
const [customUrl, setCustomUrl] = createSignal<string | undefined>(props.customUrl);
|
||||||
|
const [shouldAnimateIcon, setShouldAnimateIcon] = createSignal(false);
|
||||||
const [drawerOpen, setDrawerOpen] = createSignal(drawerState.get(initialGuestId) ?? false);
|
const [drawerOpen, setDrawerOpen] = createSignal(drawerState.get(initialGuestId) ?? false);
|
||||||
const editingUrlValue = createMemo(() => {
|
const editingUrlValue = createMemo(() => {
|
||||||
editingValuesVersion(); // Subscribe to changes
|
editingValuesVersion(); // Subscribe to changes
|
||||||
|
|
@ -82,7 +83,17 @@ export function GuestRow(props: GuestRowProps) {
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
// Don't update customUrl from props if this guest is currently being edited
|
// Don't update customUrl from props if this guest is currently being edited
|
||||||
if (currentlyEditingGuestId() !== guestId()) {
|
if (currentlyEditingGuestId() !== guestId()) {
|
||||||
setCustomUrl(props.customUrl);
|
const prevUrl = customUrl();
|
||||||
|
const newUrl = props.customUrl;
|
||||||
|
|
||||||
|
// Only animate when URL transitions from empty to having a value
|
||||||
|
if (!prevUrl && newUrl) {
|
||||||
|
setShouldAnimateIcon(true);
|
||||||
|
// Remove animation class after it completes
|
||||||
|
setTimeout(() => setShouldAnimateIcon(false), 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCustomUrl(newUrl);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -250,6 +261,14 @@ export function GuestRow(props: GuestRowProps) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await GuestMetadataAPI.updateMetadata(guestId(), { customUrl: newUrl });
|
await GuestMetadataAPI.updateMetadata(guestId(), { customUrl: newUrl });
|
||||||
|
|
||||||
|
// Animate if transitioning from no URL to having a URL
|
||||||
|
const hadUrl = !!customUrl();
|
||||||
|
if (!hadUrl && newUrl) {
|
||||||
|
setShouldAnimateIcon(true);
|
||||||
|
setTimeout(() => setShouldAnimateIcon(false), 200);
|
||||||
|
}
|
||||||
|
|
||||||
setCustomUrl(newUrl || undefined);
|
setCustomUrl(newUrl || undefined);
|
||||||
|
|
||||||
// Notify parent to update metadata
|
// Notify parent to update metadata
|
||||||
|
|
@ -430,7 +449,7 @@ export function GuestRow(props: GuestRowProps) {
|
||||||
href={customUrl()}
|
href={customUrl()}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="flex-shrink-0 text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 transition-colors"
|
class={`flex-shrink-0 text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 transition-colors ${shouldAnimateIcon() ? 'animate-fadeIn' : ''}`}
|
||||||
title="Open in new tab"
|
title="Open in new tab"
|
||||||
onClick={(event) => event.stopPropagation()}
|
onClick={(event) => event.stopPropagation()}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,13 @@ export default {
|
||||||
},
|
},
|
||||||
animation: {
|
animation: {
|
||||||
'spin-slow': 'spin 2s linear infinite',
|
'spin-slow': 'spin 2s linear infinite',
|
||||||
|
'fadeIn': 'fadeIn 0.2s ease-in',
|
||||||
|
},
|
||||||
|
keyframes: {
|
||||||
|
fadeIn: {
|
||||||
|
'0%': { opacity: '0' },
|
||||||
|
'100%': { opacity: '1' },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue