@@ -6,6 +6,7 @@ import { useMutation, useQuery } from "@tanstack/react-query"
66import dayjs from "dayjs"
77import type { SubscriptionProduct } from "expo-iap"
88import { openURL } from "expo-linking"
9+ import type { TFunction } from "i18next"
910import { useCallback , useEffect , useMemo , useRef , useState } from "react"
1011import { useTranslation } from "react-i18next"
1112import type { LayoutChangeEvent } from "react-native"
@@ -39,6 +40,41 @@ type PaymentPlan = NonNullable<StatusConfigs["PAYMENT_PLAN_LIST"]>[number]
3940type PaymentFeature = PaymentPlan [ "limit" ]
4041type BillingPeriod = "monthly" | "yearly"
4142
43+ const AI_MODEL_SELECTION_VALUE_LABELS = {
44+ none : {
45+ translationKey : "plan.featureValues.AI_MODEL_SELECTION.none" ,
46+ fallback : "—" ,
47+ } ,
48+ curated : {
49+ translationKey : "plan.featureValues.AI_MODEL_SELECTION.curated" ,
50+ fallback : "Curated models" ,
51+ } ,
52+ high_performance : {
53+ translationKey : "plan.featureValues.AI_MODEL_SELECTION.high_performance" ,
54+ fallback : "All high-end models" ,
55+ } ,
56+ } as const
57+
58+ const PLAN_FEATURE_ORDER : Array < keyof PaymentFeature > = [
59+ "MAX_SUBSCRIPTIONS" ,
60+ "MAX_LISTS" ,
61+ "MAX_INBOXES" ,
62+ "MAX_ACTIONS" ,
63+ "MAX_AI_ENTRY_SUMMARY_PER_DAY" ,
64+ "MAX_AI_ENTRY_TRANSLATION_PER_DAY" ,
65+ "MAX_AI_TEXT_TO_SPEECH_PER_DAY" ,
66+ "AI_MODEL_SELECTION" ,
67+ "AI_BRING_YOUR_OWN_KEY" ,
68+ "BOOSTS" ,
69+ "PRIORITY_SUPPORT" ,
70+ "PRIVATE_SUBSCRIPTION" ,
71+ "MAX_RSSHUB_SUBSCRIPTIONS" ,
72+ "SECURE_IMAGE_PROXY" ,
73+ "INTEGRATION_SUPPORTED" ,
74+ "AI_CREDIT" ,
75+ "MAX_AI_TASKS" ,
76+ ]
77+
4278const BILLING_SEGMENTS : BillingPeriod [ ] = [ "monthly" , "yearly" ]
4379
4480type SegmentLayout = {
@@ -96,11 +132,21 @@ const formatCurrency = (value: number) => currencyFormatter.format(value)
96132const formatFeatureValue = (
97133 key : keyof PaymentFeature ,
98134 value : PaymentFeature [ keyof PaymentFeature ] | undefined | null ,
135+ t ?: TFunction < "settings" > ,
99136) : string => {
100137 if ( value == null ) {
101138 return "—"
102139 }
103140
141+ if ( key === "AI_MODEL_SELECTION" && typeof value === "string" ) {
142+ const selectionValue =
143+ AI_MODEL_SELECTION_VALUE_LABELS [ value as keyof typeof AI_MODEL_SELECTION_VALUE_LABELS ]
144+
145+ if ( selectionValue ) {
146+ return t ?.( selectionValue . translationKey ) ?? selectionValue . fallback
147+ }
148+ }
149+
104150 if ( typeof value === "boolean" ) {
105151 return value ? "✓" : "—"
106152 }
@@ -337,13 +383,13 @@ export const PlanScreen: NavigationControllerView = () => {
337383 : t ( "subscription.summary.free" )
338384
339385 let summarySubtitle = t ( "subscription.summary.free_description" )
340- if ( role === UserRole . Pro || role === UserRole . Plus ) {
341- summarySubtitle = t ( "subscription.summary.active" )
342- } else if ( daysLeft && daysLeft > 0 && role && role !== UserRole . Free ) {
386+ if ( daysLeft && daysLeft > 0 && role && role !== UserRole . Free ) {
343387 summarySubtitle = t ( "subscription.summary.trial_expiring" , {
344388 date : dayjs ( roleEndAt ) . format ( "MMMM D, YYYY" ) ,
345389 days : daysLeft ,
346390 } )
391+ } else if ( currentPlan && currentPlan . role !== UserRole . Free ) {
392+ summarySubtitle = t ( "subscription.summary.active" )
347393 }
348394
349395 return (
@@ -407,6 +453,7 @@ export const PlanScreen: NavigationControllerView = () => {
407453 isProcessing = { isProcessing || isPurchasing || isProcessingPurchase }
408454 isManaging = { billingPortalMutation . isPending }
409455 activeSubscription = { billingSubscriptionQuery . data }
456+ upgradeButtonText = { plan . upgradeButtonText }
410457 />
411458 )
412459 } ) }
@@ -568,6 +615,7 @@ const PlanCard = ({
568615 isProcessing,
569616 isManaging,
570617 activeSubscription,
618+ upgradeButtonText,
571619} : {
572620 plan : PaymentPlan
573621 billingPeriod : BillingPeriod
@@ -578,6 +626,7 @@ const PlanCard = ({
578626 isProcessing ?: boolean
579627 isManaging ?: boolean
580628 activeSubscription ?: ActiveSubscription
629+ upgradeButtonText ?: string
581630} ) => {
582631 const { t } = useTranslation ( "settings" )
583632
@@ -645,11 +694,14 @@ const PlanCard = ({
645694 const planDescription = t ( `plan.descriptions.${ plan . role } ` as const , { defaultValue : "" } )
646695
647696 const features = useMemo ( ( ) => {
648- return (
649- Object . entries ( plan . limit || { } ) as Array <
650- [ keyof PaymentFeature , PaymentFeature [ keyof PaymentFeature ] ]
651- >
652- )
697+ const fallbackFeatureOrder = Object . keys ( plan . limit || { } ) as Array < keyof PaymentFeature >
698+ const orderedFeatureKeys = [
699+ ...PLAN_FEATURE_ORDER ,
700+ ...fallbackFeatureOrder . filter ( ( featureKey ) => ! PLAN_FEATURE_ORDER . includes ( featureKey ) ) ,
701+ ]
702+
703+ return orderedFeatureKeys
704+ . map ( ( featureKey ) => [ featureKey , plan . limit ?. [ featureKey ] ] as const )
653705 . filter ( ( [ , value ] ) => isFeatureValueVisible ( value ) )
654706 . slice ( 0 , 6 )
655707 } , [ plan . limit ] )
@@ -719,7 +771,7 @@ const PlanCard = ({
719771
720772 < View className = "mt-4 gap-2" >
721773 { features . map ( ( [ featureKey , value ] ) => {
722- const formattedValue = formatFeatureValue ( featureKey , value )
774+ const formattedValue = formatFeatureValue ( featureKey , value , t )
723775 const showValue = ! ( typeof value === "boolean" && value )
724776 return (
725777 < View key = { featureKey as string } className = "flex-row items-center gap-3" >
@@ -755,6 +807,7 @@ const PlanCard = ({
755807 isProcessing = { isProcessing }
756808 isManaging = { isManaging }
757809 activeSubscription = { activeSubscription }
810+ upgradeButtonText = { upgradeButtonText }
758811 />
759812 </ View >
760813 )
@@ -767,13 +820,15 @@ const PlanAction = ({
767820 isProcessing,
768821 isManaging,
769822 activeSubscription,
823+ upgradeButtonText,
770824} : {
771825 actionType : "current" | "upgrade" | "coming-soon" | null
772826 onUpgrade ?: ( ) => void
773827 onManageSubscription ?: ( ) => void
774828 isProcessing ?: boolean
775829 isManaging ?: boolean
776830 activeSubscription ?: ActiveSubscription
831+ upgradeButtonText ?: string
777832} ) => {
778833 const { t } = useTranslation ( "settings" )
779834
@@ -865,7 +920,7 @@ const PlanAction = ({
865920 < ActivityIndicator color = "white" />
866921 ) : (
867922 < Text className = "text-base font-semibold text-white" >
868- { t ( "subscription.actions.upgrade" ) }
923+ { upgradeButtonText || t ( "subscription.actions.upgrade" ) }
869924 </ Text >
870925 ) }
871926 </ Pressable >
0 commit comments