feat(toast): 实现原生Toast系统并优化会员购买错误处理
- 新增iOS原生Toast模块(NativeToastManager),提供毛玻璃风格的Toast展示 - 重构ToastContext为原生模块调用,添加错误边界和回退机制 - 优化会员购买流程的错误处理,使用RevenueCat标准错误码 - 调整购买按钮高度和恢复购买按钮字体大小,改善UI体验 - 移除不必要的延迟和注释代码,提升代码质量
This commit is contained in:
@@ -1,15 +1,6 @@
|
||||
import SuccessToast from '@/components/ui/SuccessToast';
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { setToastRef } from '@/utils/toast.utils';
|
||||
import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
|
||||
|
||||
interface ToastConfig {
|
||||
message: string;
|
||||
duration?: number;
|
||||
backgroundColor?: string;
|
||||
textColor?: string;
|
||||
icon?: string;
|
||||
}
|
||||
import { Toast, ToastConfig } from '@/utils/toast.utils';
|
||||
import React, { createContext, useContext, useMemo } from 'react';
|
||||
import { Alert, Platform } from 'react-native';
|
||||
|
||||
export interface ToastContextType {
|
||||
showToast: (config: ToastConfig) => void;
|
||||
@@ -21,88 +12,76 @@ export interface ToastContextType {
|
||||
const ToastContext = createContext<ToastContextType | undefined>(undefined);
|
||||
|
||||
export function ToastProvider({ children }: { children: React.ReactNode }) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [config, setConfig] = useState<ToastConfig>({ message: '' });
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const showToast = (toastConfig: ToastConfig) => {
|
||||
// 如果已有Toast显示,先隐藏
|
||||
if (visible) {
|
||||
setVisible(false);
|
||||
// 短暂延迟后显示新Toast
|
||||
setTimeout(() => {
|
||||
setConfig(toastConfig);
|
||||
setVisible(true);
|
||||
}, 100);
|
||||
} else {
|
||||
setConfig(toastConfig);
|
||||
setVisible(true);
|
||||
}
|
||||
};
|
||||
|
||||
const showSuccess = (message: string, duration?: number) => {
|
||||
showToast({
|
||||
message,
|
||||
duration,
|
||||
backgroundColor: Colors.light.primary, // 主题色
|
||||
icon: '✓',
|
||||
});
|
||||
};
|
||||
|
||||
const showError = (message: string, duration?: number) => {
|
||||
showToast({
|
||||
message,
|
||||
duration,
|
||||
backgroundColor: '#f44336', // 红色
|
||||
icon: '✕',
|
||||
});
|
||||
};
|
||||
|
||||
const showWarning = (message: string, duration?: number) => {
|
||||
showToast({
|
||||
message,
|
||||
duration,
|
||||
backgroundColor: '#ff9800', // 橙色
|
||||
icon: '⚠',
|
||||
});
|
||||
};
|
||||
|
||||
const handleHide = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
const value: ToastContextType = {
|
||||
showToast,
|
||||
showSuccess,
|
||||
showError,
|
||||
showWarning,
|
||||
};
|
||||
|
||||
// 设置全局引用
|
||||
useEffect(() => {
|
||||
setToastRef(value);
|
||||
}, [value]);
|
||||
const value = useMemo<ToastContextType>(
|
||||
() => ({
|
||||
showToast: (config: ToastConfig) => {
|
||||
try {
|
||||
Toast.show(config);
|
||||
} catch (error) {
|
||||
console.error('Toast.show failed:', error);
|
||||
// 错误边界:在原生模块失败时提供回退方案
|
||||
handleToastFallback(config.message || config.text1 || '提示', config.type);
|
||||
}
|
||||
},
|
||||
showSuccess: (message: string, duration?: number) => {
|
||||
try {
|
||||
Toast.success(message, duration);
|
||||
} catch (error) {
|
||||
console.error('Toast.success failed:', error);
|
||||
handleToastFallback(message, 'success');
|
||||
}
|
||||
},
|
||||
showError: (message: string, duration?: number) => {
|
||||
try {
|
||||
Toast.error(message, duration);
|
||||
} catch (error) {
|
||||
console.error('Toast.error failed:', error);
|
||||
handleToastFallback(message, 'error');
|
||||
}
|
||||
},
|
||||
showWarning: (message: string, duration?: number) => {
|
||||
try {
|
||||
Toast.warning(message, duration);
|
||||
} catch (error) {
|
||||
console.error('Toast.warning failed:', error);
|
||||
handleToastFallback(message, 'warning');
|
||||
}
|
||||
},
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<ToastContext.Provider value={value}>
|
||||
{children}
|
||||
<SuccessToast
|
||||
visible={visible}
|
||||
message={config.message}
|
||||
duration={config.duration}
|
||||
backgroundColor={config.backgroundColor}
|
||||
textColor={config.textColor}
|
||||
icon={config.icon}
|
||||
onHide={handleHide}
|
||||
/>
|
||||
</ToastContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误边界处理:在原生 Toast 模块失败时提供回退方案
|
||||
*/
|
||||
function handleToastFallback(message: string, type?: string) {
|
||||
if (!message) return;
|
||||
|
||||
// 在 iOS 上使用 Alert 作为回退方案,避免过度打扰用户
|
||||
if (Platform.OS === 'ios') {
|
||||
// 只对错误和警告类型使用 Alert,其他类型仅记录到控制台
|
||||
if (type === 'error' || type === 'warning') {
|
||||
Alert.alert('', message, [{ text: '确定', style: 'default' }]);
|
||||
} else {
|
||||
console.log(`Toast (${type}): ${message}`);
|
||||
}
|
||||
} else {
|
||||
// 其他平台仅记录到控制台
|
||||
console.log(`Toast (${type}): ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function useToast(): ToastContextType {
|
||||
const context = useContext(ToastContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useToast must be used within a ToastProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user