mirror of
https://github.com/R0m1k3/FlowReader.git
synced 2026-10-11 17:28:05 +02:00
Fix double swipe/validation in Focus Mode & Improve Button Layout
This commit is contained in:
1 parent
c350ca4893
commit
dfa56173b9
1 file changed
+37
-33
@@ -27,6 +27,8 @@ export function FocusCardStack({ articles, onMarkRead, onKeep, onToggleFavorite,
|
|||||||
const [exitX, setExitX] = useState(0);
|
const [exitX, setExitX] = useState(0);
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
|
const [isProcessing, setIsProcessing] = useState(false);
|
||||||
|
|
||||||
// Visible stack size
|
// Visible stack size
|
||||||
const visibleArticles = articles.slice(currentIndex, currentIndex + 3);
|
const visibleArticles = articles.slice(currentIndex, currentIndex + 3);
|
||||||
const topArticle = visibleArticles[0];
|
const topArticle = visibleArticles[0];
|
||||||
@@ -44,8 +46,31 @@ export function FocusCardStack({ articles, onMarkRead, onKeep, onToggleFavorite,
|
|||||||
const xSpring = useSpring(x, { stiffness: 1000, damping: 50 });
|
const xSpring = useSpring(x, { stiffness: 1000, damping: 50 });
|
||||||
const rotateSpring = useSpring(rotate, { 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] }) => {
|
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);
|
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;
|
const isRight = mx > 0;
|
||||||
setExitX(isRight ? 500 : -500); // Trigger exit animation
|
setExitX(isRight ? 500 : -500); // Trigger exit animation
|
||||||
|
|
||||||
// Logic handled after animation completes via AnimatePresence onExitComplete
|
// Trigger action via handler
|
||||||
// But here we need to trigger state change.
|
handleAction(isRight ? 'read' : 'keep');
|
||||||
// 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
|
|
||||||
} else {
|
} else {
|
||||||
// Dragging or Snap Back
|
// Dragging or Snap Back
|
||||||
x.set(active ? mx : 0);
|
x.set(active ? mx : 0);
|
||||||
@@ -188,13 +201,11 @@ export function FocusCardStack({ articles, onMarkRead, onKeep, onToggleFavorite,
|
|||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="absolute bottom-10 left-0 right-0 flex justify-center gap-8 z-20 items-center">
|
<div className="absolute bottom-10 left-0 right-0 flex justify-center items-center gap-4 px-4 sm:gap-8 z-20">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => handleAction('keep')}
|
||||||
onKeep(topArticle.id);
|
disabled={isProcessing}
|
||||||
setCurrentIndex(prev => prev + 1);
|
className="p-4 rounded-full bg-white shadow-lg text-blue-500 hover:bg-blue-50 hover:scale-110 transition-all disabled:opacity-50 disabled:scale-100"
|
||||||
}}
|
|
||||||
className="p-4 rounded-full bg-white shadow-lg text-blue-500 hover:bg-blue-50 hover:scale-110 transition-all"
|
|
||||||
title="Garder (Gauche)"
|
title="Garder (Gauche)"
|
||||||
>
|
>
|
||||||
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
@@ -204,13 +215,8 @@ export function FocusCardStack({ articles, onMarkRead, onKeep, onToggleFavorite,
|
|||||||
|
|
||||||
{/* Undo Button - Always rendered for layout, hidden if inactive */}
|
{/* Undo Button - Always rendered for layout, hidden if inactive */}
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => handleAction('undo')}
|
||||||
if (currentIndex > 0) {
|
disabled={currentIndex === 0 || isProcessing}
|
||||||
setCurrentIndex(prev => Math.max(0, prev - 1));
|
|
||||||
x.set(0); // Reset position
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled={currentIndex === 0}
|
|
||||||
className={`p-3 rounded-full backdrop-blur-md text-white shadow-lg border border-white/20 transition-all duration-300 ${currentIndex > 0
|
className={`p-3 rounded-full backdrop-blur-md text-white shadow-lg border border-white/20 transition-all duration-300 ${currentIndex > 0
|
||||||
? 'bg-nature/80 hover:bg-nature hover:scale-105 opacity-100 cursor-pointer'
|
? 'bg-nature/80 hover:bg-nature hover:scale-105 opacity-100 cursor-pointer'
|
||||||
: 'bg-white/5 opacity-0 pointer-events-none scale-90'
|
: 'bg-white/5 opacity-0 pointer-events-none scale-90'
|
||||||
@@ -223,11 +229,9 @@ export function FocusCardStack({ articles, onMarkRead, onKeep, onToggleFavorite,
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => handleAction('read')}
|
||||||
onMarkRead(topArticle.id);
|
disabled={isProcessing}
|
||||||
setCurrentIndex(prev => prev + 1);
|
className="p-4 rounded-full bg-nature shadow-lg text-white hover:bg-nature-light hover:scale-110 transition-all disabled:opacity-50 disabled:scale-100"
|
||||||
}}
|
|
||||||
className="p-4 rounded-full bg-nature shadow-lg text-white hover:bg-nature-light hover:scale-110 transition-all"
|
|
||||||
title="J'ai lu (Droite)"
|
title="J'ai lu (Droite)"
|
||||||
>
|
>
|
||||||
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
|||||||
Reference in new issue
Block a user