diff options
Diffstat (limited to 'apps/mobile')
| -rw-r--r-- | apps/mobile/components/ui/Toast.tsx | 215 | ||||
| -rw-r--r-- | apps/mobile/lib/providers.tsx | 5 | ||||
| -rw-r--r-- | apps/mobile/package.json | 1 |
3 files changed, 38 insertions, 183 deletions
diff --git a/apps/mobile/components/ui/Toast.tsx b/apps/mobile/components/ui/Toast.tsx index 96323263..722c93ab 100644 --- a/apps/mobile/components/ui/Toast.tsx +++ b/apps/mobile/components/ui/Toast.tsx @@ -1,8 +1,4 @@ -import { createContext, useContext, useEffect, useRef, useState } from "react"; -import { Animated, Platform, View } from "react-native"; -import { FullWindowOverlay } from "react-native-screens"; -import { Text } from "@/components/ui/Text"; -import { cn } from "@/lib/utils"; +import { toast as sonnerToast } from "sonner-native"; const toastVariants = { default: "bg-foreground", @@ -11,184 +7,41 @@ const toastVariants = { info: "bg-blue-500", }; -interface ToastProps { - id: number; - message: string; - onHide: (id: number) => void; - variant?: keyof typeof toastVariants; - duration?: number; - showProgress?: boolean; -} -function Toast({ - id, - message, - onHide, - variant = "default", - duration = 3000, - showProgress = true, -}: ToastProps) { - const opacity = useRef(new Animated.Value(0)).current; - const progress = useRef(new Animated.Value(0)).current; - - useEffect(() => { - Animated.sequence([ - Animated.timing(opacity, { - toValue: 1, - duration: 500, - useNativeDriver: true, - }), - Animated.timing(progress, { - toValue: 1, - duration: duration - 1000, - useNativeDriver: false, - }), - Animated.timing(opacity, { - toValue: 0, - duration: 500, - useNativeDriver: true, - }), - ]).start(() => onHide(id)); - }, [duration]); - - return ( - <Animated.View - className={` - ${toastVariants[variant]} - m-2 mb-1 transform rounded-lg p-4 transition-all - `} - style={{ - opacity, - transform: [ - { - translateY: opacity.interpolate({ - inputRange: [0, 1], - outputRange: [-20, 0], - }), - }, - ], - }} - > - <Text className="text-left font-semibold text-background">{message}</Text> - {showProgress && ( - <View className="mt-2 rounded"> - <Animated.View - className="h-2 rounded bg-white opacity-30 dark:bg-black" - style={{ - width: progress.interpolate({ - inputRange: [0, 1], - outputRange: ["0%", "100%"], - }), - }} - /> - </View> - )} - </Animated.View> - ); -} - type ToastVariant = keyof typeof toastVariants; -interface ToastMessage { - id: number; - text: string; - variant: ToastVariant; - duration?: number; - position?: string; - showProgress?: boolean; -} -interface ToastContextProps { - toast: (t: { - message: string; - variant?: keyof typeof toastVariants; - duration?: number; - position?: "top" | "bottom"; - showProgress?: boolean; - }) => void; - removeToast: (id: number) => void; -} -const ToastContext = createContext<ToastContextProps | undefined>(undefined); - -// TODO: refactor to pass position to Toast instead of ToastProvider -function ToastProvider({ - children, - position = "top", -}: { - children: React.ReactNode; - position?: "top" | "bottom"; -}) { - const [messages, setMessages] = useState<ToastMessage[]>([]); - - const toast: ToastContextProps["toast"] = ({ - message, - variant = "default", - duration = 3000, - position = "top", - showProgress = true, - }: { - message: string; - variant?: ToastVariant; - duration?: number; - position?: "top" | "bottom"; - showProgress?: boolean; - }) => { - setMessages((prev) => [ - ...prev, - { - id: Date.now(), - text: message, - variant, - duration, - position, - showProgress, - }, - ]); - }; - - const removeToast = (id: number) => { - setMessages((prev) => prev.filter((message) => message.id !== id)); - }; - - const content = ( - <View - className={cn("absolute left-0 right-0", { - "top-[45px]": position === "top", - "bottom-0": position === "bottom", - })} - > - {messages.map((message) => ( - <Toast - key={message.id} - id={message.id} - message={message.text} - variant={message.variant} - duration={message.duration} - showProgress={message.showProgress} - onHide={removeToast} - /> - ))} - </View> - ); - - return ( - <ToastContext.Provider value={{ toast, removeToast }}> - {children} - {/* Use FullWindowOverlay on iOS to ensure toasts appear above all content */} - {/* Platform specific implementation due to FullWindowOverlay being iOS-only */} - {Platform.OS === "ios" ? ( - <FullWindowOverlay>{content}</FullWindowOverlay> - ) : ( - content - )} - </ToastContext.Provider> - ); -} - +// Compatibility wrapper for sonner-native function useToast() { - const context = useContext(ToastContext); - if (!context) { - throw new Error("useToast must be used within ToastProvider"); - } - return context; + return { + toast: ({ + message, + variant = "default", + duration = 3000, + }: { + message: string; + variant?: ToastVariant; + duration?: number; + position?: "top" | "bottom"; + showProgress?: boolean; + }) => { + // Map variants to sonner-native methods + switch (variant) { + case "success": + sonnerToast.success(message, { duration }); + break; + case "destructive": + sonnerToast.error(message, { duration }); + break; + case "info": + sonnerToast.info(message, { duration }); + break; + default: + sonnerToast(message, { duration }); + } + }, + removeToast: () => { + // sonner-native handles dismissal automatically + }, + }; } -export { ToastProvider, ToastVariant, Toast, toastVariants, useToast }; +export { ToastVariant, toastVariants, useToast }; diff --git a/apps/mobile/lib/providers.tsx b/apps/mobile/lib/providers.tsx index 36ed7e71..01d2d5b5 100644 --- a/apps/mobile/lib/providers.tsx +++ b/apps/mobile/lib/providers.tsx @@ -1,6 +1,6 @@ import { useEffect } from "react"; import FullPageSpinner from "@/components/ui/FullPageSpinner"; -import { ToastProvider } from "@/components/ui/Toast"; +import { Toaster } from "sonner-native"; import { TRPCProvider } from "@karakeep/shared-react/providers/trpc-provider"; @@ -22,7 +22,8 @@ export function Providers({ children }: { children: React.ReactNode }) { return ( <TRPCProvider settings={settings}> <ReaderSettingsProvider> - <ToastProvider>{children}</ToastProvider> + {children} + <Toaster /> </ReaderSettingsProvider> </TRPCProvider> ); diff --git a/apps/mobile/package.json b/apps/mobile/package.json index f826300d..d1525a48 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -61,6 +61,7 @@ "react-native-screens": "~4.11.1", "react-native-svg": "^15.11.2", "react-native-webview": "^13.13.5", + "sonner-native": "^0.22.2", "tailwind-merge": "^2.2.1", "zod": "^3.24.2", "zustand": "^5.0.5" |
