diff --git a/src/view/com/util/Toast.tsx b/src/view/com/util/Toast.tsx index 2f8888bef4..56c6780ad3 100644 --- a/src/view/com/util/Toast.tsx +++ b/src/view/com/util/Toast.tsx @@ -6,8 +6,8 @@ import { GestureHandlerRootView, } from 'react-native-gesture-handler' import Animated, { - FadeIn, - FadeOut, + FadeInUp, + FadeOutUp, runOnJS, useAnimatedReaction, useAnimatedStyle, @@ -17,36 +17,37 @@ import Animated, { } from 'react-native-reanimated' import RootSiblings from 'react-native-root-siblings' import {useSafeAreaInsets} from 'react-native-safe-area-context' +import { + FontAwesomeIcon, + type Props as FontAwesomeProps, +} from '@fortawesome/react-native-fontawesome' + import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {atoms as a, useTheme} from '#/alf' import {Text} from '#/components/Typography' -import { - type ToastType, - TOAST_TYPE_TO_ICON, - getToastTypeStyles, - TOAST_ANIMATION_CONFIG, -} from './Toast.style' const TIMEOUT = 2e3 -export function show(message: string, type: ToastType = 'default') { +export function show( + message: string, + icon: FontAwesomeProps['icon'] = 'check', +) { if (process.env.NODE_ENV === 'test') { return } - AccessibilityInfo.announceForAccessibility(message) const item = new RootSiblings( - item.destroy()} />, + item.destroy()} />, ) } function Toast({ message, - type, + icon, destroy, }: { message: string - type: ToastType + icon: FontAwesomeProps['icon'] destroy: () => void }) { const t = useTheme() @@ -55,10 +56,6 @@ function Toast({ 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 @@ -162,52 +159,55 @@ function Toast({ pointerEvents="box-none"> {alive && ( setCardHeight(evt.nativeEvent.layout.height)} - accessibilityRole="alert" - accessible={true} - accessibilityLabel={message} - accessibilityHint="" - onAccessibilityEscape={hideAndDestroyImmediately} - style={[ - a.flex_1, - {backgroundColor: colors.backgroundColor}, - a.shadow_sm, - {borderColor: colors.borderColor, borderWidth: 1}, - a.rounded_sm, - animatedStyle, - ]}> - - - - + entering={FadeInUp} + exiting={FadeOutUp} + style={[a.flex_1]}> + setCardHeight(evt.nativeEvent.layout.height)} + accessibilityRole="alert" + accessible={true} + accessibilityLabel={message} + accessibilityHint="" + onAccessibilityEscape={hideAndDestroyImmediately} + style={[ + a.flex_1, + t.name === 'dark' ? t.atoms.bg_contrast_25 : t.atoms.bg, + a.shadow_lg, + t.atoms.border_contrast_medium, + a.rounded_sm, + a.border, + animatedStyle, + ]}> + + + + + + + + {message} + + - - - {message} - - - - + + )} diff --git a/src/view/com/util/Toast.web.tsx b/src/view/com/util/Toast.web.tsx index 331a8b5398..d3b7bda33e 100644 --- a/src/view/com/util/Toast.web.tsx +++ b/src/view/com/util/Toast.web.tsx @@ -4,20 +4,17 @@ import {useEffect, useState} from 'react' import {Pressable, StyleSheet, Text, View} from 'react-native' -import {atoms as a, useTheme} from '#/alf' import { - type ToastType, - TOAST_TYPE_TO_ICON, - getToastTypeStyles, - getToastWebAnimationStyles, - TOAST_WEB_KEYFRAMES, -} from './Toast.style' + FontAwesomeIcon, + type FontAwesomeIconStyle, + type Props as FontAwesomeProps, +} from '@fortawesome/react-native-fontawesome' const DURATION = 3500 interface ActiveToast { text: string - type: ToastType + icon: FontAwesomeProps['icon'] } type GlobalSetActiveToast = (_activeToast: ActiveToast | undefined) => void @@ -31,82 +28,21 @@ let toastTimeout: NodeJS.Timeout | undefined type ToastContainerProps = {} export const ToastContainer: React.FC = ({}) => { const [activeToast, setActiveToast] = useState() - const [isExiting, setIsExiting] = useState(false) - useEffect(() => { globalSetActiveToast = (t: ActiveToast | undefined) => { - if (!t && activeToast) { - setIsExiting(true) - setTimeout(() => { - setActiveToast(t) - setIsExiting(false) - }, 200) - } else { - setActiveToast(t) - setIsExiting(false) - } + setActiveToast(t) } - }, [activeToast]) - - useEffect(() => { - const styleId = 'toast-animations' - if (!document.getElementById(styleId)) { - const style = document.createElement('style') - style.id = styleId - style.textContent = TOAST_WEB_KEYFRAMES - document.head.appendChild(style) - } - }, []) - - 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 ( <> {activeToast && ( - - - - - - {activeToast.text} - + + + {activeToast.text} = ({}) => { // methods // = -export function show(text: string, type: ToastType = 'default') { +export function show(text: string, icon: FontAwesomeProps['icon'] = 'check') { if (toastTimeout) { clearTimeout(toastTimeout) } - - globalSetActiveToast?.({text, type}) + globalSetActiveToast?.({text, icon}) toastTimeout = setTimeout(() => { globalSetActiveToast?.(undefined) }, DURATION) @@ -143,12 +78,12 @@ const styles = StyleSheet.create({ bottom: 20, // @ts-ignore web only width: 'calc(100% - 40px)', - maxWidth: 380, + maxWidth: 350, padding: 20, flexDirection: 'row', - alignItems: 'flex-start', + alignItems: 'center', + backgroundColor: '#000c', borderRadius: 10, - borderWidth: 1, }, dismissBackdrop: { position: 'absolute', @@ -157,18 +92,13 @@ const styles = StyleSheet.create({ bottom: 0, right: 0, }, - iconContainer: { - width: 32, - height: 32, - borderRadius: 16, - alignItems: 'center', - justifyContent: 'center', - flexShrink: 0, - }, icon: { + color: '#fff', flexShrink: 0, }, text: { + color: '#fff', + fontSize: 18, marginLeft: 10, }, }) diff --git a/src/view/screens/Storybook/Toasts.tsx b/src/view/screens/Storybook/Toasts.tsx deleted file mode 100644 index 5197ec2f4c..0000000000 --- a/src/view/screens/Storybook/Toasts.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import {View, Pressable} from 'react-native' - -import {atoms as a, useTheme} from '#/alf' -import {Text, H1} from '#/components/Typography' -import { - type ToastType, - TOAST_TYPE_TO_ICON, - getToastTypeStyles, -} from '#/view/com/util/Toast.style' -import * as Toast from '#/view/com/util/Toast' - -function ToastPreview({message, type}: {message: string; type: ToastType}) { - const t = useTheme() - const toastStyles = getToastTypeStyles(t) - const colors = toastStyles[type] - const IconComponent = TOAST_TYPE_TO_ICON[type] - - return ( - Toast.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} - - - - ) -} - -export function Toasts() { - return ( - -

Toast Examples

- - - - - - - - - - - - - - - - - - - - - - - - - - -
- ) -} diff --git a/src/view/screens/Storybook/index.tsx b/src/view/screens/Storybook/index.tsx index afcc1c4e73..a6c2ecdde3 100644 --- a/src/view/screens/Storybook/index.tsx +++ b/src/view/screens/Storybook/index.tsx @@ -20,7 +20,6 @@ import {Settings} from './Settings' import {Shadows} from './Shadows' import {Spacing} from './Spacing' import {Theming} from './Theming' -import {Toasts} from './Toasts' import {Typography} from './Typography' export function Storybook() { @@ -123,7 +122,6 @@ function StorybookInner() { -