Files
digital-pilates/utils/toast.utils.ts
richarjiang 71a8bb9740 feat(toast): 实现原生Toast系统并优化会员购买错误处理
- 新增iOS原生Toast模块(NativeToastManager),提供毛玻璃风格的Toast展示
- 重构ToastContext为原生模块调用,添加错误边界和回退机制
- 优化会员购买流程的错误处理,使用RevenueCat标准错误码
- 调整购买按钮高度和恢复购买按钮字体大小,改善UI体验
- 移除不必要的延迟和注释代码,提升代码质量
2025-10-28 11:04:34 +08:00

123 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Global toast helper backed by native UI.
*
* Usage:
* import { Toast } from '@/utils/toast.utils';
*
* Toast.success('操作成功!');
* Toast.error('操作失败!');
* Toast.warning('注意!');
*/
import { NativeModules, Platform, ToastAndroid } from 'react-native';
export type ToastType = 'default' | 'success' | 'error' | 'warning' | 'info';
export interface ToastConfig {
message?: string;
text1?: string;
duration?: number;
type?: ToastType;
backgroundColor?: string;
textColor?: string;
icon?: string;
}
type NativeToastModule = {
show: (options: {
message?: string;
text1?: string;
duration?: number;
type?: ToastType;
backgroundColor?: string;
textColor?: string;
icon?: string;
}) => void;
};
const DEFAULT_DURATION = 2000;
const MIN_DURATION = 1000;
const MAX_DURATION = 10000;
// 增强类型安全性:添加运行时检查确保模块存在且具有预期的方法
const nativeToast = NativeModules.NativeToastManager &&
typeof NativeModules.NativeToastManager.show === 'function'
? NativeModules.NativeToastManager as NativeToastModule
: undefined;
const clampDuration = (duration?: number) => {
const value = typeof duration === 'number' ? duration : DEFAULT_DURATION;
return Math.min(Math.max(value, MIN_DURATION), MAX_DURATION);
};
const resolveMessage = (config: ToastConfig) => {
const raw = (config.message ?? config.text1 ?? '').toString();
return raw.trim();
};
const showOnAndroid = (message: string, duration: number) => {
const toastDuration = duration >= 3000 ? ToastAndroid.LONG : ToastAndroid.SHORT;
ToastAndroid.showWithGravity(message, toastDuration, ToastAndroid.CENTER);
};
const showNative = (config: ToastConfig) => {
const message = resolveMessage(config);
if (!message) {
console.warn('Toast invoked without message content');
return;
}
const duration = clampDuration(config.duration);
const type = config.type ?? 'default';
if (Platform.OS === 'android') {
showOnAndroid(message, duration);
return;
}
if (nativeToast?.show) {
nativeToast.show({
...config,
message,
duration,
type,
});
return;
}
// 为不支持的平台提供更明显的用户反馈
if (Platform.OS === 'web') {
// 在 Web 环境下使用 console.warn 并尝试使用浏览器原生 alert
console.warn(`Toast: ${message}`);
try {
// 仅在重要消息时使用 alert避免过度打扰用户
if (config.type === 'error' || config.type === 'warning') {
alert(message);
}
} catch (e) {
// 忽略 alert 错误
}
} else {
console.log(`Toast: ${message}`);
}
};
export const Toast = {
show: (config: ToastConfig) => {
showNative(config);
},
success: (message: string, duration?: number) => {
showNative({ message, duration, type: 'success' });
},
error: (message: string, duration?: number) => {
showNative({ message, duration, type: 'error' });
},
warning: (message: string, duration?: number) => {
showNative({ message, duration, type: 'warning' });
},
info: (message: string, duration?: number) => {
showNative({ message, duration, type: 'info' });
},
};