fix(ui): stuck viewer when spamming toggle

There are a number of bugs with `framer-motion` that can result in sync issues with AnimatePresence and the conditionally rendered component.

You can see this if you rapidly click an accordion, occasionally it gets out of sync and is closed when it should be open.

This is a bigger problem with the viewer where the user may hold down the `z` key. It's trivial to get it to lock up.

For now, just remove the animation entirely.

Upstream issues for reference:
https://github.com/framer/motion/issues/2023
https://github.com/framer/motion/issues/2618
https://github.com/framer/motion/issues/2554
This commit is contained in:
psychedelicious 2024-05-07 05:57:21 +10:00 committed by Kent Keirsey
parent 6b98dba71d
commit 6249982d82

View File

@ -5,8 +5,6 @@ import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/To
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer'; import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import type { InvokeTabName } from 'features/ui/store/tabMap'; import type { InvokeTabName } from 'features/ui/store/tabMap';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import type { AnimationProps } from 'framer-motion';
import { AnimatePresence, motion } from 'framer-motion';
import { memo, useMemo } from 'react'; import { memo, useMemo } from 'react';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
@ -14,18 +12,6 @@ import CurrentImageButtons from './CurrentImageButtons';
import CurrentImagePreview from './CurrentImagePreview'; import CurrentImagePreview from './CurrentImagePreview';
import { EditorButton } from './EditorButton'; import { EditorButton } from './EditorButton';
const initial: AnimationProps['initial'] = {
opacity: 0,
};
const animate: AnimationProps['animate'] = {
opacity: 1,
transition: { duration: 0.07 },
};
const exit: AnimationProps['exit'] = {
opacity: 0,
transition: { duration: 0.07 },
};
const VIEWER_ENABLED_TABS: InvokeTabName[] = ['canvas', 'generation', 'workflows']; const VIEWER_ENABLED_TABS: InvokeTabName[] = ['canvas', 'generation', 'workflows'];
export const ImageViewer = memo(() => { export const ImageViewer = memo(() => {
@ -42,50 +28,44 @@ export const ImageViewer = memo(() => {
useHotkeys('z', onToggle, { enabled: isViewerEnabled }, [isViewerEnabled, onToggle]); useHotkeys('z', onToggle, { enabled: isViewerEnabled }, [isViewerEnabled, onToggle]);
useHotkeys('esc', onClose, { enabled: isViewerEnabled }, [isViewerEnabled, onClose]); useHotkeys('esc', onClose, { enabled: isViewerEnabled }, [isViewerEnabled, onClose]);
// The AnimatePresence mode must be wait - else framer can get confused if you spam the toggle button if (!shouldShowViewer) {
return null;
}
return ( return (
<AnimatePresence mode="wait"> <Flex
{shouldShowViewer && ( layerStyle="first"
<Flex borderRadius="base"
key="imageViewer" position="absolute"
as={motion.div} flexDirection="column"
initial={initial} top={0}
animate={animate} right={0}
exit={exit} bottom={0}
layerStyle="first" left={0}
borderRadius="base" p={2}
position="absolute" rowGap={4}
flexDirection="column" alignItems="center"
top={0} justifyContent="center"
right={0} zIndex={10} // reactflow puts its minimap at 5, so we need to be above that
bottom={0} >
left={0} <Flex w="full" gap={2}>
p={2} <Flex flex={1} justifyContent="center">
rowGap={4} <Flex gap={2} marginInlineEnd="auto">
alignItems="center" <ToggleProgressButton />
justifyContent="center" <ToggleMetadataViewerButton />
zIndex={10} // reactflow puts its minimap at 5, so we need to be above that
>
<Flex w="full" gap={2}>
<Flex flex={1} justifyContent="center">
<Flex gap={2} marginInlineEnd="auto">
<ToggleProgressButton />
<ToggleMetadataViewerButton />
</Flex>
</Flex>
<Flex flex={1} gap={2} justifyContent="center">
<CurrentImageButtons />
</Flex>
<Flex flex={1} justifyContent="center">
<Flex gap={2} marginInlineStart="auto">
<EditorButton />
</Flex>
</Flex>
</Flex> </Flex>
<CurrentImagePreview />
</Flex> </Flex>
)} <Flex flex={1} gap={2} justifyContent="center">
</AnimatePresence> <CurrentImageButtons />
</Flex>
<Flex flex={1} justifyContent="center">
<Flex gap={2} marginInlineStart="auto">
<EditorButton />
</Flex>
</Flex>
</Flex>
<CurrentImagePreview />
</Flex>
); );
}); });