From dfa56173b972054dafe98f66582e8037c03d262e Mon Sep 17 00:00:00 2001 From: R0m1k3 Date: Thu, 12 Feb 2026 13:18:58 +0100 Subject: [PATCH] Fix double swipe/validation in Focus Mode & Improve Button Layout --- web/src/components/focus/FocusCardStack.tsx | 70 +++++++++++---------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/web/src/components/focus/FocusCardStack.tsx b/web/src/components/focus/FocusCardStack.tsx index 2109218..ed54185 100644 --- a/web/src/components/focus/FocusCardStack.tsx +++ b/web/src/components/focus/FocusCardStack.tsx @@ -27,6 +27,8 @@ export function FocusCardStack({ articles, onMarkRead, onKeep, onToggleFavorite, const [exitX, setExitX] = useState(0); const isMobile = useIsMobile(); + const [isProcessing, setIsProcessing] = useState(false); + // Visible stack size const visibleArticles = articles.slice(currentIndex, currentIndex + 3); const topArticle = visibleArticles[0]; @@ -44,8 +46,31 @@ export function FocusCardStack({ articles, onMarkRead, onKeep, onToggleFavorite, const xSpring = useSpring(x, { stiffness: 1000, damping: 50 }); const rotateSpring = useSpring(rotate, { stiffness: 1000, damping: 50 }); + const handleAction = (action: 'read' | 'keep' | 'undo') => { + if (isProcessing) return; + setIsProcessing(true); + + if (action === 'undo') { + if (currentIndex > 0) { + setCurrentIndex(prev => Math.max(0, prev - 1)); + x.set(0); + } + } else if (action === 'read') { + onMarkRead(topArticle.id); + setCurrentIndex(prev => prev + 1); + x.set(0); + } else { + onKeep(topArticle.id); + setCurrentIndex(prev => prev + 1); + x.set(0); + } + + // Lock for animation duration + setTimeout(() => setIsProcessing(false), 500); + }; + const bind = useDrag(({ active, movement: [mx], velocity: [vx] }) => { - if (readingArticle) return; // Disable drag when reading + if (readingArticle || isProcessing) return; // Disable drag when reading or processing const trigger = Math.abs(mx) > 200 || (Math.abs(vx) > 0.5 && Math.abs(mx) > 50); @@ -54,20 +79,8 @@ export function FocusCardStack({ articles, onMarkRead, onKeep, onToggleFavorite, const isRight = mx > 0; setExitX(isRight ? 500 : -500); // Trigger exit animation - // Logic handled after animation completes via AnimatePresence onExitComplete - // But here we need to trigger state change. - // Better approach: State change triggers exit animation naturally? - // No, libraries like Framer Motion handle exit animations when components are removed from DOM. - // So we just need to advance index. - - if (isRight) { - onMarkRead(topArticle.id); - } else { - onKeep(topArticle.id); - } - - setCurrentIndex(prev => prev + 1); - x.set(0); // Reset for next card + // Trigger action via handler + handleAction(isRight ? 'read' : 'keep'); } else { // Dragging or Snap Back x.set(active ? mx : 0); @@ -188,13 +201,11 @@ export function FocusCardStack({ articles, onMarkRead, onKeep, onToggleFavorite, -
+