diff options
Diffstat (limited to 'apps/mobile/app/sharing.tsx')
| -rw-r--r-- | apps/mobile/app/sharing.tsx | 165 |
1 files changed, 116 insertions, 49 deletions
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 ( - <View className="flex flex-row gap-3"> - <Text variant="largeTitle">Hoarding</Text> - <ActivityIndicator /> - </View> - ); + return null; } export default function Sharing() { const router = useRouter(); const [mode, setMode] = useState<Mode>({ type: "idle" }); - const autoCloseTimeoutId = useRef<number | null>(null); - - let comp; - switch (mode.type) { - case "idle": { - comp = <SaveBookmark setMode={setMode} />; - break; - } - case "alreadyExists": - case "success": { - comp = ( - <View className="items-center gap-4"> - <Text variant="largeTitle"> - {mode.type === "alreadyExists" ? "Already Hoarded!" : "Hoarded!"} - </Text> - <Button - onPress={() => { - router.replace(`/dashboard/bookmarks/${mode.bookmarkId}/info`); - if (autoCloseTimeoutId.current) { - clearTimeout(autoCloseTimeoutId.current); - } - }} - > - <Text>Manage</Text> - </Button> - <Pressable onPress={() => router.replace("dashboard")}> - <Text className="text-muted-foreground">Dismiss</Text> - </Pressable> - </View> - ); - break; - } - case "error": { - comp = <Text variant="largeTitle">Error!</Text>; - break; - } - } + const autoCloseTimeoutId = useRef<ReturnType<typeof setTimeout> | 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 ( - <View className="flex-1 items-center justify-center gap-4">{comp}</View> + <View className="flex-1 items-center justify-center bg-background"> + {/* Hidden component that handles the save logic */} + {mode.type === "idle" && <SaveBookmark setMode={setMode} />} + + {/* Loading State */} + {mode.type === "idle" && <LoadingAnimation />} + + {/* Success State */} + {(mode.type === "success" || mode.type === "alreadyExists") && ( + <Animated.View + entering={FadeIn.duration(200)} + className="items-center gap-6" + > + <SuccessAnimation isAlreadyExists={mode.type === "alreadyExists"} /> + + <Animated.View + entering={FadeIn.delay(400).duration(300)} + className="items-center gap-2" + > + <Text variant="title1" className="font-semibold text-foreground"> + {mode.type === "alreadyExists" ? "Already Hoarded!" : "Hoarded!"} + </Text> + <Text variant="body" className="text-muted-foreground"> + {mode.type === "alreadyExists" + ? "This item was saved before" + : "Saved to your collection"} + </Text> + </Animated.View> + + <Animated.View + entering={FadeIn.delay(600).duration(300)} + className="items-center gap-3 pt-2" + > + <Button onPress={handleManage} variant="primary" size="lg"> + <Text className="font-medium text-primary-foreground"> + Manage + </Text> + </Button> + <Pressable + onPress={handleDismiss} + className="px-4 py-2 active:opacity-60" + > + <Text className="text-muted-foreground">Dismiss</Text> + </Pressable> + </Animated.View> + </Animated.View> + )} + + {/* Error State */} + {mode.type === "error" && ( + <Animated.View + entering={FadeIn.duration(200)} + className="items-center gap-6" + > + <ErrorAnimation /> + + <Animated.View + entering={FadeIn.delay(300).duration(300)} + className="items-center gap-2" + > + <Text variant="title1" className="font-semibold text-foreground"> + Oops! + </Text> + <Text variant="body" className="text-muted-foreground"> + Something went wrong + </Text> + </Animated.View> + + <Animated.View + entering={FadeIn.delay(500).duration(300)} + className="items-center gap-3 pt-2" + > + <Pressable + onPress={handleDismiss} + className="px-4 py-2 active:opacity-60" + > + <Text className="text-muted-foreground">Dismiss</Text> + </Pressable> + </Animated.View> + </Animated.View> + )} + </View> ); } |
