feat(toast): 实现原生Toast系统并优化会员购买错误处理
- 新增iOS原生Toast模块(NativeToastManager),提供毛玻璃风格的Toast展示 - 重构ToastContext为原生模块调用,添加错误边界和回退机制 - 优化会员购买流程的错误处理,使用RevenueCat标准错误码 - 调整购买按钮高度和恢复购买按钮字体大小,改善UI体验 - 移除不必要的延迟和注释代码,提升代码质量
This commit is contained in:
@@ -1,58 +1,122 @@
|
||||
/**
|
||||
* 全局Toast工具函数
|
||||
*
|
||||
* 使用方式:
|
||||
* Global toast helper backed by native UI.
|
||||
*
|
||||
* Usage:
|
||||
* import { Toast } from '@/utils/toast.utils';
|
||||
*
|
||||
*
|
||||
* Toast.success('操作成功!');
|
||||
* Toast.error('操作失败!');
|
||||
* Toast.warning('注意!');
|
||||
*/
|
||||
|
||||
import { ToastContextType } from '@/contexts/ToastContext';
|
||||
import { NativeModules, Platform, ToastAndroid } from 'react-native';
|
||||
|
||||
let toastRef: ToastContextType | null = null;
|
||||
export type ToastType = 'default' | 'success' | 'error' | 'warning' | 'info';
|
||||
|
||||
export const setToastRef = (ref: ToastContextType) => {
|
||||
toastRef = ref;
|
||||
};
|
||||
export interface ToastConfig {
|
||||
message?: string;
|
||||
text1?: string;
|
||||
duration?: number;
|
||||
type?: ToastType;
|
||||
backgroundColor?: string;
|
||||
textColor?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export const Toast = {
|
||||
success: (message: string, duration?: number) => {
|
||||
if (toastRef) {
|
||||
toastRef.showSuccess(message, duration);
|
||||
} else {
|
||||
console.warn('Toast not initialized. Please wrap your app with ToastProvider');
|
||||
}
|
||||
},
|
||||
|
||||
error: (message: string, duration?: number) => {
|
||||
if (toastRef) {
|
||||
toastRef.showError(message, duration);
|
||||
} else {
|
||||
console.warn('Toast not initialized. Please wrap your app with ToastProvider');
|
||||
}
|
||||
},
|
||||
|
||||
warning: (message: string, duration?: number) => {
|
||||
if (toastRef) {
|
||||
toastRef.showWarning(message, duration);
|
||||
} else {
|
||||
console.warn('Toast not initialized. Please wrap your app with ToastProvider');
|
||||
}
|
||||
},
|
||||
|
||||
show: (config: {
|
||||
message: string;
|
||||
type NativeToastModule = {
|
||||
show: (options: {
|
||||
message?: string;
|
||||
text1?: string;
|
||||
duration?: number;
|
||||
type?: ToastType;
|
||||
backgroundColor?: string;
|
||||
textColor?: string;
|
||||
icon?: string;
|
||||
}) => {
|
||||
if (toastRef) {
|
||||
toastRef.showToast(config);
|
||||
} else {
|
||||
console.warn('Toast not initialized. Please wrap your app with ToastProvider');
|
||||
}) => 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' });
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user