fix: bug
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
StatusBar as RNStatusBar,
|
||||
Platform,
|
||||
useWindowDimensions,
|
||||
ActivityIndicator,
|
||||
} from 'react-native'
|
||||
import { StatusBar } from 'expo-status-bar'
|
||||
import { LinearGradient } from 'expo-linear-gradient'
|
||||
@@ -22,7 +23,7 @@ import { CheckMarkIcon } from '@/components/icon/checkMark'
|
||||
import PointsDrawer from '@/components/drawer/PointsDrawer'
|
||||
import Dropdown from '@/components/ui/dropdown'
|
||||
import GradientText from '@/components/GradientText'
|
||||
import { useUserBalance } from '@/hooks/use-user-balance'
|
||||
import { useMembership } from '@/hooks/use-membership'
|
||||
|
||||
// 使用唯一 id 的 PointsIcon,避免与其他页面的图标 id 冲突
|
||||
const MembershipPointsIcon = () => {
|
||||
@@ -65,63 +66,53 @@ export default function MembershipScreen() {
|
||||
const router = useRouter()
|
||||
const insets = useSafeAreaInsets()
|
||||
const { width: screenWidth } = useWindowDimensions()
|
||||
const [selectedPlan, setSelectedPlan] = useState<PlanType>('pro')
|
||||
const [agreed, setAgreed] = useState(false)
|
||||
const [pointsDrawerVisible, setPointsDrawerVisible] = useState(false)
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
// 获取积分余额
|
||||
const { balance } = useUserBalance()
|
||||
|
||||
// 订阅计划数据(使用国际化)
|
||||
const plans: Plan[] = [
|
||||
{
|
||||
id: 'plus',
|
||||
name: 'Plus',
|
||||
price: 9,
|
||||
points: 750,
|
||||
features: [
|
||||
t('membership.features.points750'),
|
||||
t('membership.features.textToVideo'),
|
||||
t('membership.features.superClearImage'),
|
||||
t('membership.features.superDiscount'),
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'pro',
|
||||
name: 'Pro',
|
||||
price: 29,
|
||||
recommended: true,
|
||||
points: 1080,
|
||||
features: [
|
||||
t('membership.features.points1080'),
|
||||
t('membership.features.textToVideo'),
|
||||
t('membership.features.superClearImage'),
|
||||
t('membership.features.superDiscount'),
|
||||
t('membership.features.sora2ProTemplate'),
|
||||
t('membership.features.removeWatermark'),
|
||||
t('membership.features.allTemplates'),
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'plus-premium',
|
||||
name: 'Plus',
|
||||
price: 49,
|
||||
points: 1500,
|
||||
features: [
|
||||
t('membership.features.points1500'),
|
||||
t('membership.features.textToVideo'),
|
||||
t('membership.features.superClearImage'),
|
||||
t('membership.features.superDiscount'),
|
||||
// 使用 useMembership hook
|
||||
const {
|
||||
creditPlans,
|
||||
creditBalance,
|
||||
selectedPlanIndex,
|
||||
setSelectedPlanIndex,
|
||||
isLoadingSubscriptions,
|
||||
isStripePricingLoading,
|
||||
createSubscription,
|
||||
upgradeSubscription,
|
||||
restoreSubscription,
|
||||
rechargeToken,
|
||||
activeAuthSubscription,
|
||||
hasActiveSubscription,
|
||||
stripePricingData,
|
||||
} = useMembership()
|
||||
|
||||
// 映射 API 数据到 UI 格式
|
||||
const plans: Plan[] = creditPlans.map((plan, index) => ({
|
||||
id: `plan-${index}` as PlanType,
|
||||
name: index === 0 ? 'Plus' : index === 1 ? 'Pro' : 'Plus',
|
||||
price: plan.amountInCents / 100,
|
||||
recommended: plan.popular,
|
||||
points: plan.credits,
|
||||
features: [
|
||||
t('membership.features.points750'),
|
||||
t('membership.features.textToVideo'),
|
||||
t('membership.features.superClearImage'),
|
||||
t('membership.features.superDiscount'),
|
||||
...(index > 0 ? [
|
||||
t('membership.features.sora2ProTemplate'),
|
||||
t('membership.features.removeWatermark'),
|
||||
t('membership.features.allTemplates'),
|
||||
] : []),
|
||||
...(index > 1 ? [
|
||||
t('membership.features.higherQuality'),
|
||||
t('membership.features.prioritySupport'),
|
||||
]
|
||||
},
|
||||
]
|
||||
] : []),
|
||||
],
|
||||
}))
|
||||
|
||||
const selectedPlan = plans[selectedPlanIndex] || plans[0]
|
||||
|
||||
// 下拉菜单选项
|
||||
const menuOptions = [
|
||||
@@ -146,18 +137,52 @@ export default function MembershipScreen() {
|
||||
]
|
||||
const [currentImageIndex, setCurrentImageIndex] = useState(0)
|
||||
|
||||
// 处理订阅按钮点击
|
||||
const handleSubscribe = () => {
|
||||
if (!agreed || !selectedPlan) return
|
||||
|
||||
const planData = creditPlans[selectedPlanIndex]
|
||||
if (!planData) return
|
||||
|
||||
if (hasActiveSubscription) {
|
||||
upgradeSubscription({ credits: planData.credits })
|
||||
} else if (activeAuthSubscription?.cancelAtPeriodEnd) {
|
||||
restoreSubscription()
|
||||
} else {
|
||||
const pricingItem = stripePricingData?.pricing_table_items?.[selectedPlanIndex]
|
||||
if (pricingItem) {
|
||||
createSubscription({
|
||||
priceId: pricingItem.price_id,
|
||||
productId: pricingItem.product_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前选中计划的信息
|
||||
const currentPlan = plans.find(plan => plan.id === selectedPlan) || plans[1]
|
||||
|
||||
const currentPlan = selectedPlan
|
||||
|
||||
// 计算进度条百分比:当前计划积分 / 最高计划积分
|
||||
const maxPoints = Math.max(...plans.map(plan => plan.points))
|
||||
const progressPercentage = (currentPlan.points / maxPoints) * 100
|
||||
const progressPercentage = (currentPlan?.points / maxPoints) * 100
|
||||
|
||||
// 加载状态
|
||||
if (isStripePricingLoading || creditPlans.length === 0) {
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
<StatusBar style="light" />
|
||||
<View style={[styles.container, styles.loadingContainer]}>
|
||||
<ActivityIndicator size="large" color="#FF9966" />
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
<StatusBar style="light" />
|
||||
<RNStatusBar
|
||||
barStyle="light-content"
|
||||
<RNStatusBar
|
||||
barStyle="light-content"
|
||||
backgroundColor="#090A0B"
|
||||
translucent={Platform.OS === 'android'}
|
||||
/>
|
||||
@@ -172,13 +197,13 @@ export default function MembershipScreen() {
|
||||
</Pressable>
|
||||
<View style={styles.headerSpacer} />
|
||||
<View style={styles.pointsContainer}>
|
||||
<Pressable
|
||||
<Pressable
|
||||
style={styles.pointsPill}
|
||||
onPress={() => setPointsDrawerVisible(true)}
|
||||
>
|
||||
<Text style={styles.pointsLabel}>{t('membership.myPoints')}</Text>
|
||||
<MembershipPointsIcon />
|
||||
<Text style={styles.pointsValue}>{balance}</Text>
|
||||
<Text style={styles.pointsValue}>{creditBalance}</Text>
|
||||
</Pressable>
|
||||
<View style={styles.settingsButtonContainer}>
|
||||
<Dropdown
|
||||
@@ -253,7 +278,7 @@ export default function MembershipScreen() {
|
||||
{/* 订阅计划卡片 */}
|
||||
<View style={styles.plansContainer}>
|
||||
{plans.map((plan, index) => {
|
||||
const isSelected = selectedPlan === plan.id
|
||||
const isSelected = selectedPlanIndex === index
|
||||
return (
|
||||
<Pressable
|
||||
key={plan.id}
|
||||
@@ -261,7 +286,7 @@ export default function MembershipScreen() {
|
||||
styles.planCardWrapper,
|
||||
index > 0 && styles.planCardSpacing,
|
||||
]}
|
||||
onPress={() => setSelectedPlan(plan.id)}
|
||||
onPress={() => setSelectedPlanIndex(index)}
|
||||
>
|
||||
{isSelected ? (
|
||||
<LinearGradient
|
||||
@@ -365,15 +390,16 @@ export default function MembershipScreen() {
|
||||
<View style={[styles.subscribeContainer, { paddingBottom: Math.max(insets.bottom, 3) }]}>
|
||||
{/* 立即开通按钮 */}
|
||||
<Pressable
|
||||
disabled={!agreed}
|
||||
disabled={!agreed || isLoadingSubscriptions || isStripePricingLoading}
|
||||
style={styles.subscribeButtonPressable}
|
||||
onPress={handleSubscribe}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={currentPlan.recommended
|
||||
colors={currentPlan.recommended
|
||||
? ['#9966FF', '#FF6699', '#FF9966']
|
||||
: ['#FFE7F8', '#C6A7FA', '#ADBCFF', '#E6E3FF']
|
||||
}
|
||||
locations={currentPlan.recommended
|
||||
locations={currentPlan.recommended
|
||||
? [0.0015, 0.4985, 0.9956]
|
||||
: [0, 0.33, 0.66, 1]
|
||||
}
|
||||
@@ -381,12 +407,14 @@ export default function MembershipScreen() {
|
||||
end={{ x: 0, y: 0 }}
|
||||
style={[
|
||||
styles.subscribeButton,
|
||||
!agreed && styles.subscribeButtonDisabled,
|
||||
(!agreed || isLoadingSubscriptions || isStripePricingLoading) && styles.subscribeButtonDisabled,
|
||||
]}
|
||||
>
|
||||
|
||||
|
||||
{isLoadingSubscriptions || isStripePricingLoading ? (
|
||||
<ActivityIndicator color="#F5F5F5" />
|
||||
) : (
|
||||
<Text style={currentPlan.recommended ? styles.subscribeButtonText : styles.subscribeButtonText1}>{t('membership.subscribeNow')}</Text>
|
||||
)}
|
||||
</LinearGradient>
|
||||
</Pressable>
|
||||
{/* 协议复选框 */}
|
||||
@@ -423,9 +451,10 @@ export default function MembershipScreen() {
|
||||
<PointsDrawer
|
||||
visible={pointsDrawerVisible}
|
||||
onClose={() => setPointsDrawerVisible(false)}
|
||||
totalPoints={balance}
|
||||
totalPoints={creditBalance}
|
||||
subscriptionPoints={0}
|
||||
topUpPoints={0}
|
||||
onRecharge={(amount) => rechargeToken(amount)}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
)
|
||||
@@ -770,5 +799,9 @@ const styles = StyleSheet.create({
|
||||
fontWeight: '400',
|
||||
flex: 1,
|
||||
},
|
||||
loadingContainer: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user