diff options
Diffstat (limited to 'src/view/com/util/Toast.web.tsx')
-rw-r--r-- | src/view/com/util/Toast.web.tsx | 100 |
1 files changed, 90 insertions, 10 deletions
diff --git a/src/view/com/util/Toast.web.tsx b/src/view/com/util/Toast.web.tsx index d3b7bda33..949dce7ef 100644 --- a/src/view/com/util/Toast.web.tsx +++ b/src/view/com/util/Toast.web.tsx @@ -9,12 +9,24 @@ import { type FontAwesomeIconStyle, type Props as FontAwesomeProps, } from '@fortawesome/react-native-fontawesome' +import {atoms as a, useBreakpoints, useTheme} from '#/alf' -const DURATION = 3500 +const DURATION = 60000 + +export type ToastType = 'default' | 'success' | 'error' | 'warning' | 'info' + +const TOAST_TYPE_TO_ICON: Record<ToastType, FontAwesomeProps['icon']> = { + default: 'check', + success: 'check', + error: 'exclamation', + warning: 'circle-exclamation', + info: 'info', +} interface ActiveToast { text: string icon: FontAwesomeProps['icon'] + type: ToastType } type GlobalSetActiveToast = (_activeToast: ActiveToast | undefined) => void @@ -33,16 +45,76 @@ export const ToastContainer: React.FC<ToastContainerProps> = ({}) => { setActiveToast(t) } }) + + const t = useTheme() + + const TOAST_TYPE_TO_STYLES = { + default: { + backgroundColor: t.atoms.text_contrast_low.color, + borderColor: t.atoms.border_contrast_medium.borderColor, + iconColor: '#fff', + textColor: '#fff', + }, + success: { + backgroundColor: '#059669', + borderColor: '#047857', + iconColor: '#fff', + textColor: '#fff', + }, + error: { + backgroundColor: t.palette.negative_100, + borderColor: t.palette.negative_400, + iconColor: t.palette.negative_600, + textColor: t.palette.negative_600, + }, + warning: { + backgroundColor: t.palette.negative_500, + borderColor: t.palette.negative_600, + iconColor: '#fff', + textColor: '#fff', + }, + info: { + backgroundColor: t.atoms.text_contrast_low.color, + borderColor: t.atoms.border_contrast_medium.borderColor, + iconColor: '#fff', + textColor: '#fff', + }, + } + + const toastStyles = activeToast + ? TOAST_TYPE_TO_STYLES[activeToast.type] + : TOAST_TYPE_TO_STYLES.default + return ( <> {activeToast && ( - <View style={styles.container}> + <View + style={[ + styles.container, + { + backgroundColor: toastStyles.backgroundColor, + borderColor: toastStyles.borderColor, + }, + ]}> <FontAwesomeIcon icon={activeToast.icon} size={20} - style={styles.icon as FontAwesomeIconStyle} + style={ + [ + styles.icon, + {color: toastStyles.iconColor}, + ] as FontAwesomeIconStyle + } /> - <Text style={styles.text}>{activeToast.text}</Text> + <Text + style={[ + styles.text, + a.text_sm, + a.font_bold, + {color: toastStyles.textColor}, + ]}> + {activeToast.text} + </Text> <Pressable style={styles.dismissBackdrop} accessibilityLabel="Dismiss" @@ -60,11 +132,22 @@ export const ToastContainer: React.FC<ToastContainerProps> = ({}) => { // methods // = -export function show(text: string, icon: FontAwesomeProps['icon'] = 'check') { +export function show( + text: string, + type: ToastType | FontAwesomeProps['icon'] = 'default', +) { if (toastTimeout) { clearTimeout(toastTimeout) } - globalSetActiveToast?.({text, icon}) + + // Determine if type is a semantic type or direct icon + const isSemanticType = typeof type === 'string' && type in TOAST_TYPE_TO_ICON + const icon = isSemanticType + ? TOAST_TYPE_TO_ICON[type as ToastType] + : (type as FontAwesomeProps['icon']) + const toastType = isSemanticType ? (type as ToastType) : 'default' + + globalSetActiveToast?.({text, icon, type: toastType}) toastTimeout = setTimeout(() => { globalSetActiveToast?.(undefined) }, DURATION) @@ -82,8 +165,8 @@ const styles = StyleSheet.create({ padding: 20, flexDirection: 'row', alignItems: 'center', - backgroundColor: '#000c', borderRadius: 10, + borderWidth: 1, }, dismissBackdrop: { position: 'absolute', @@ -93,12 +176,9 @@ const styles = StyleSheet.create({ right: 0, }, icon: { - color: '#fff', flexShrink: 0, }, text: { - color: '#fff', - fontSize: 18, marginLeft: 10, }, }) |