diff --git a/src/components/Toast/Toast.tsx b/src/components/Toast/Toast.tsx new file mode 100644 index 0000000000..d8793dc0c8 --- /dev/null +++ b/src/components/Toast/Toast.tsx @@ -0,0 +1,78 @@ +import {createContext, useContext, useMemo} from 'react' +import {View} from 'react-native' + +import {atoms as a, useTheme} from '#/alf' +import {getToastTypeStyles, TOAST_TYPE_TO_ICON} from '#/components/Toast/style' +import {type ToastType} from '#/components/Toast/types' +import {Text} from '#/components/Typography' + +type ContextType = { + type: ToastType +} + +const Context = createContext({ + type: 'default', +}) + +export function Toast({ + type, + content, +}: { + type: ToastType + content: React.ReactNode +}) { + const t = useTheme() + const toastTypeStyles = getToastTypeStyles(t) + const styles = toastTypeStyles[type] + const Icon = TOAST_TYPE_TO_ICON[type] + + return ( + ({type}), [type])}> + + + + + {typeof content === 'string' ? ( + {content} + ) : ( + content + )} + + + + ) +} + +export function ToastText({children}: {children: React.ReactNode}) { + const t = useTheme() + const toastTypeStyles = getToastTypeStyles(t) + const {type} = useContext(Context) + const styles = toastTypeStyles[type] + return ( + + {children} + + ) +} diff --git a/src/components/Toast/index.tsx b/src/components/Toast/index.tsx index e3c3f6e18c..6d252f8aad 100644 --- a/src/components/Toast/index.tsx +++ b/src/components/Toast/index.tsx @@ -1,5 +1,5 @@ import {useEffect, useMemo, useRef, useState} from 'react' -import {AccessibilityInfo, View} from 'react-native' +import {AccessibilityInfo} from 'react-native' import { Gesture, GestureDetector, @@ -19,14 +19,10 @@ import RootSiblings from 'react-native-root-siblings' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' -import {atoms as a, useTheme} from '#/alf' -import { - getToastTypeStyles, - TOAST_ANIMATION_CONFIG, - TOAST_TYPE_TO_ICON, -} from '#/components/Toast/style' +import {atoms as a} from '#/alf' +import {TOAST_ANIMATION_CONFIG} from '#/components/Toast/style' +import {Toast as BaseToast} from '#/components/Toast/Toast' import {type ToastType} from '#/components/Toast/types' -import {Text} from '#/components/Typography' const TIMEOUT = 2e3 @@ -54,16 +50,11 @@ function Toast({ type: ToastType destroy: () => void }) { - const t = useTheme() const {top} = useSafeAreaInsets() const isPanning = useSharedValue(false) const dismissSwipeTranslateY = useSharedValue(0) const [cardHeight, setCardHeight] = useState(0) - const toastStyles = getToastTypeStyles(t) - const colors = toastStyles[type] - const IconComponent = TOAST_TYPE_TO_ICON[type] - // for the exit animation to work on iOS the animated component // must not be the root component // so we need to wrap it in a view and unmount the toast ahead of time @@ -175,43 +166,9 @@ function Toast({ accessibilityLabel={message} accessibilityHint="" onAccessibilityEscape={hideAndDestroyImmediately} - style={[ - a.flex_1, - {backgroundColor: colors.backgroundColor}, - a.shadow_sm, - {borderColor: colors.borderColor, borderWidth: 1}, - a.rounded_sm, - animatedStyle, - ]}> + style={[a.flex_1, animatedStyle]}> - - - - - - - {message} - - - + )} diff --git a/src/components/Toast/index.web.tsx b/src/components/Toast/index.web.tsx index d5a21b9746..4e78e841ab 100644 --- a/src/components/Toast/index.web.tsx +++ b/src/components/Toast/index.web.tsx @@ -3,14 +3,13 @@ */ import {useEffect, useState} from 'react' -import {Pressable, StyleSheet, Text, View} from 'react-native' +import {Pressable, View} from 'react-native' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' -import {atoms as a, useTheme} from '#/alf' -import { - getToastTypeStyles, - getToastWebAnimationStyles, - TOAST_TYPE_TO_ICON, -} from '#/components/Toast/style' +import {atoms as a, web} from '#/alf' +import {getToastWebAnimationStyles} from '#/components/Toast/style' +import {Toast} from '#/components/Toast/Toast' import {type ToastType} from '#/components/Toast/types' const DURATION = 3500 @@ -20,16 +19,12 @@ interface ActiveToast { type: ToastType } type GlobalSetActiveToast = (_activeToast: ActiveToast | undefined) => void - -// globals -// = let globalSetActiveToast: GlobalSetActiveToast | undefined let toastTimeout: NodeJS.Timeout | undefined - -// components -// = type ToastContainerProps = {} + export const ToastContainer: React.FC = ({}) => { + const {_} = useLingui() const [activeToast, setActiveToast] = useState() const [isExiting, setIsExiting] = useState(false) @@ -48,17 +43,6 @@ export const ToastContainer: React.FC = ({}) => { } }, [activeToast]) - const t = useTheme() - - const toastTypeStyles = getToastTypeStyles(t) - const toastStyles = activeToast - ? toastTypeStyles[activeToast.type] - : toastTypeStyles.default - - const IconComponent = activeToast - ? TOAST_TYPE_TO_ICON[activeToast.type] - : TOAST_TYPE_TO_ICON.default - const animationStyles = getToastWebAnimationStyles() return ( @@ -66,44 +50,23 @@ export const ToastContainer: React.FC = ({}) => { {activeToast && ( - - - - - {activeToast.text} - + { - setActiveToast(undefined) - }} + onPress={() => setActiveToast(undefined)} /> )} @@ -111,9 +74,6 @@ export const ToastContainer: React.FC = ({}) => { ) } -// methods -// = - export function show(text: string, type: ToastType = 'default') { if (toastTimeout) { clearTimeout(toastTimeout) @@ -124,41 +84,3 @@ export function show(text: string, type: ToastType = 'default') { globalSetActiveToast?.(undefined) }, DURATION) } - -const styles = StyleSheet.create({ - container: { - // @ts-ignore web only - position: 'fixed', - left: 20, - bottom: 20, - // @ts-ignore web only - width: 'calc(100% - 40px)', - maxWidth: 380, - padding: 20, - flexDirection: 'row', - alignItems: 'center', - borderRadius: 10, - borderWidth: 1, - }, - dismissBackdrop: { - position: 'absolute', - top: 0, - left: 0, - bottom: 0, - right: 0, - }, - iconContainer: { - width: 32, - height: 32, - borderRadius: 16, - alignItems: 'center', - justifyContent: 'center', - flexShrink: 0, - }, - icon: { - flexShrink: 0, - }, - text: { - marginLeft: 10, - }, -}) diff --git a/src/view/screens/Storybook/Toasts.tsx b/src/view/screens/Storybook/Toasts.tsx index 8dca956779..59be40ef80 100644 --- a/src/view/screens/Storybook/Toasts.tsx +++ b/src/view/screens/Storybook/Toasts.tsx @@ -1,64 +1,11 @@ import {Pressable, View} from 'react-native' import {show as deprecatedShow} from '#/view/com/util/Toast' -import {atoms as a, useTheme} from '#/alf' +import {atoms as a} from '#/alf' import {Button, ButtonText} from '#/components/Button' import {show} from '#/components/Toast' -import {getToastTypeStyles, TOAST_TYPE_TO_ICON} from '#/components/Toast/style' -import {type ToastType} from '#/components/Toast/types' -import {H1, Text} from '#/components/Typography' - -function ToastPreview({message, type}: {message: string; type: ToastType}) { - const t = useTheme() - const toastStyles = getToastTypeStyles(t) - const colors = toastStyles[type as keyof typeof toastStyles] - const IconComponent = - TOAST_TYPE_TO_ICON[type as keyof typeof TOAST_TYPE_TO_ICON] - - return ( - show(message, type)} - style={[ - {backgroundColor: colors.backgroundColor}, - a.shadow_sm, - {borderColor: colors.borderColor}, - a.rounded_sm, - a.border, - a.px_sm, - a.py_sm, - a.flex_row, - a.gap_sm, - a.align_center, - ]}> - - - - - - {message} - - - - ) -} +import {Toast} from '#/components/Toast/Toast' +import {H1} from '#/components/Typography' export function Toasts() { return ( @@ -66,35 +13,44 @@ export function Toasts() {

Toast Examples

- - - - - - show('Default toast', 'default')}> + + + + show( + 'This is a longer message to test how the toast handles multiple lines of text content.', + 'default', + ) + }> + - - - - - - - - - - - - - - - - - + + show('Success toast', 'success')}> + + + show('Info toast', 'info')}> + + + show('Warning toast', 'warning')}> + + + show('Error toast', 'error')}> + +