feat(toast): 实现原生Toast系统并优化会员购买错误处理

- 新增iOS原生Toast模块(NativeToastManager),提供毛玻璃风格的Toast展示
- 重构ToastContext为原生模块调用,添加错误边界和回退机制
- 优化会员购买流程的错误处理,使用RevenueCat标准错误码
- 调整购买按钮高度和恢复购买按钮字体大小,改善UI体验
- 移除不必要的延迟和注释代码,提升代码质量
This commit is contained in:
richarjiang
2025-10-28 11:04:34 +08:00
parent db8b50f6d7
commit 71a8bb9740
6 changed files with 707 additions and 140 deletions

View File

@@ -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;
}
}