From b41b5647aa10d22ca83cfd3ba97146681e9f28a3 Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Mon, 9 Feb 2026 00:25:45 +0000 Subject: feat(mobile): Add animated UI feedback to sharing modal (#2427) * feat(mobile): Add animated UI feedback to sharing modal --------- Co-authored-by: Claude --- apps/mobile/app/sharing.tsx | 165 +++++++++++++++++++++++++++++++------------- 1 file changed, 116 insertions(+), 49 deletions(-) (limited to 'apps/mobile/app/sharing.tsx') diff --git a/apps/mobile/app/sharing.tsx b/apps/mobile/app/sharing.tsx index 25c9d793..355f32ef 100644 --- a/apps/mobile/app/sharing.tsx +++ b/apps/mobile/app/sharing.tsx @@ -1,7 +1,11 @@ import { useEffect, useRef, useState } from "react"; -import { ActivityIndicator, Pressable, View } from "react-native"; +import { Pressable, View } from "react-native"; +import Animated, { FadeIn } from "react-native-reanimated"; import { useRouter } from "expo-router"; import { useShareIntentContext } from "expo-share-intent"; +import ErrorAnimation from "@/components/sharing/ErrorAnimation"; +import LoadingAnimation from "@/components/sharing/LoadingAnimation"; +import SuccessAnimation from "@/components/sharing/SuccessAnimation"; import { Button } from "@/components/ui/Button"; import { Text } from "@/components/ui/Text"; import useAppSettings from "@/lib/settings"; @@ -87,55 +91,14 @@ function SaveBookmark({ setMode }: { setMode: (mode: Mode) => void }) { }), ); - return ( - - Hoarding - - - ); + return null; } export default function Sharing() { const router = useRouter(); const [mode, setMode] = useState({ type: "idle" }); - const autoCloseTimeoutId = useRef(null); - - let comp; - switch (mode.type) { - case "idle": { - comp = ; - break; - } - case "alreadyExists": - case "success": { - comp = ( - - - {mode.type === "alreadyExists" ? "Already Hoarded!" : "Hoarded!"} - - - router.replace("dashboard")}> - Dismiss - - - ); - break; - } - case "error": { - comp = Error!; - break; - } - } + const autoCloseTimeoutId = useRef | null>(null); // Auto dismiss the modal after saving. useEffect(() => { @@ -143,14 +106,118 @@ export default function Sharing() { return; } - autoCloseTimeoutId.current = setTimeout(() => { - router.replace("dashboard"); - }, 2000); + autoCloseTimeoutId.current = setTimeout( + () => { + router.replace("dashboard"); + }, + mode.type === "error" ? 3000 : 2500, + ); - return () => clearTimeout(autoCloseTimeoutId.current!); + return () => { + if (autoCloseTimeoutId.current) { + clearTimeout(autoCloseTimeoutId.current); + } + }; }, [mode.type]); + const handleManage = () => { + if (mode.type === "success" || mode.type === "alreadyExists") { + router.replace(`/dashboard/bookmarks/${mode.bookmarkId}/info`); + if (autoCloseTimeoutId.current) { + clearTimeout(autoCloseTimeoutId.current); + } + } + }; + + const handleDismiss = () => { + if (autoCloseTimeoutId.current) { + clearTimeout(autoCloseTimeoutId.current); + } + router.replace("dashboard"); + }; + return ( - {comp} + + {/* Hidden component that handles the save logic */} + {mode.type === "idle" && } + + {/* Loading State */} + {mode.type === "idle" && } + + {/* Success State */} + {(mode.type === "success" || mode.type === "alreadyExists") && ( + + + + + + {mode.type === "alreadyExists" ? "Already Hoarded!" : "Hoarded!"} + + + {mode.type === "alreadyExists" + ? "This item was saved before" + : "Saved to your collection"} + + + + + + + Dismiss + + + + )} + + {/* Error State */} + {mode.type === "error" && ( + + + + + + Oops! + + + Something went wrong + + + + + + Dismiss + + + + )} + ); } -- cgit v1.2.3-70-g09d2