feat: fixes and formatter
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React from 'react'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Modal,
|
||||
@@ -7,136 +7,119 @@ import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { BlurView } from "expo-blur";
|
||||
import BorderGradientButton from "../BorderGradientButton";
|
||||
import GradientButton from "../GradientButton";
|
||||
import CreateLyricsHeader from "../../screens/Writing/components/CreateLyricsHeader";
|
||||
import { Palette, gutters } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { isWeb } from "../../hooks/useLayoutType";
|
||||
import CreditAmount from "../CreditAmount";
|
||||
import { useStripe } from "../../providers/StripeProvider";
|
||||
} from 'react-native'
|
||||
import { BlurView } from 'expo-blur'
|
||||
import BorderGradientButton from '../BorderGradientButton'
|
||||
import GradientButton from '../GradientButton'
|
||||
import CreateLyricsHeader from '../../screens/Writing/components/CreateLyricsHeader'
|
||||
import { Palette, gutters } from '../../styles'
|
||||
import { FONT_FAMILY } from '../../styles/Fonts'
|
||||
import { isWeb } from '../../hooks/useLayoutType'
|
||||
import CreditAmount from '../CreditAmount'
|
||||
import { useStripe } from '../../providers/StripeProvider'
|
||||
|
||||
const WEB_MODAL_MAX_WIDTH = 820;
|
||||
const formatCurrency = (amount, currency = "eur") => {
|
||||
if (typeof amount !== "number") {
|
||||
return null;
|
||||
const WEB_MODAL_MAX_WIDTH = 820
|
||||
const formatCurrency = (amount, currency = 'eur') => {
|
||||
if (typeof amount !== 'number') {
|
||||
return null
|
||||
}
|
||||
|
||||
const normalized = amount / 100;
|
||||
const normalized = amount / 100
|
||||
const upperCurrency =
|
||||
typeof currency === "string" && currency.trim()
|
||||
? currency.trim().toUpperCase()
|
||||
: "EUR";
|
||||
typeof currency === 'string' && currency.trim() ? currency.trim().toUpperCase() : 'EUR'
|
||||
|
||||
if (typeof Intl !== "undefined" && Intl.NumberFormat) {
|
||||
if (typeof Intl !== 'undefined' && Intl.NumberFormat) {
|
||||
try {
|
||||
return new Intl.NumberFormat("fr-FR", {
|
||||
style: "currency",
|
||||
return new Intl.NumberFormat('fr-FR', {
|
||||
style: 'currency',
|
||||
currency: upperCurrency,
|
||||
minimumFractionDigits: 2,
|
||||
}).format(normalized);
|
||||
}).format(normalized)
|
||||
} catch (_error) {}
|
||||
}
|
||||
|
||||
return `${normalized.toFixed(2)} ${upperCurrency}`;
|
||||
};
|
||||
return `${normalized.toFixed(2)} ${upperCurrency}`
|
||||
}
|
||||
|
||||
const FALLBACK_BASE_PRICE_PER_COIN = 12; // ~0,12€ par jeton (valeur indicatif pour l'affichage)
|
||||
const FALLBACK_DISCOUNT_STEPS = [0, 12, 18, 26, 32];
|
||||
const STATIC_DISCOUNTS = [0, 30, 50];
|
||||
const FALLBACK_BASE_PRICE_PER_COIN = 12 // ~0,12€ par jeton (valeur indicatif pour l'affichage)
|
||||
const FALLBACK_DISCOUNT_STEPS = [0, 12, 18, 26, 32]
|
||||
const STATIC_DISCOUNTS = [0, 30, 50]
|
||||
|
||||
const computeCoinPackPricing = (packs = []) => {
|
||||
if (!Array.isArray(packs) || !packs.length) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
const packsWithPrice = packs
|
||||
.filter((pack) => pack?.productId)
|
||||
.map((pack) => {
|
||||
const hasValidPrice =
|
||||
typeof pack.unitAmount === "number" &&
|
||||
typeof pack.coinAmount === "number" &&
|
||||
pack.coinAmount > 0;
|
||||
typeof pack.unitAmount === 'number' &&
|
||||
typeof pack.coinAmount === 'number' &&
|
||||
pack.coinAmount > 0
|
||||
return {
|
||||
...pack,
|
||||
hasValidPrice,
|
||||
pricePerCoin: hasValidPrice ? pack.unitAmount / pack.coinAmount : null,
|
||||
};
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
const basePack = packsWithPrice.reduce((current, pack) => {
|
||||
if (!pack.hasValidPrice) {
|
||||
return current;
|
||||
return current
|
||||
}
|
||||
if (!current) {
|
||||
return pack;
|
||||
return pack
|
||||
}
|
||||
if (
|
||||
typeof pack.coinAmount === "number" &&
|
||||
typeof current.coinAmount === "number" &&
|
||||
typeof pack.coinAmount === 'number' &&
|
||||
typeof current.coinAmount === 'number' &&
|
||||
pack.coinAmount < current.coinAmount
|
||||
) {
|
||||
return pack;
|
||||
return pack
|
||||
}
|
||||
return current;
|
||||
}, null);
|
||||
return current
|
||||
}, null)
|
||||
|
||||
const basePricePerCoin =
|
||||
basePack?.pricePerCoin && isFinite(basePack.pricePerCoin)
|
||||
? basePack.pricePerCoin
|
||||
: null;
|
||||
basePack?.pricePerCoin && isFinite(basePack.pricePerCoin) ? basePack.pricePerCoin : null
|
||||
|
||||
const bestDiscountPack = packsWithPrice.reduce((best, pack) => {
|
||||
if (!pack.hasValidPrice || !basePricePerCoin) {
|
||||
return best;
|
||||
return best
|
||||
}
|
||||
const discount =
|
||||
((basePricePerCoin - pack.pricePerCoin) / basePricePerCoin) * 100;
|
||||
const discount = ((basePricePerCoin - pack.pricePerCoin) / basePricePerCoin) * 100
|
||||
if (!best || discount > best.discount) {
|
||||
return { productId: pack.productId, discount };
|
||||
return { productId: pack.productId, discount }
|
||||
}
|
||||
return best;
|
||||
}, null);
|
||||
return best
|
||||
}, null)
|
||||
|
||||
const fallbackBase = basePricePerCoin || FALLBACK_BASE_PRICE_PER_COIN;
|
||||
const fallbackBase = basePricePerCoin || FALLBACK_BASE_PRICE_PER_COIN
|
||||
const fallbackBestId =
|
||||
!bestDiscountPack && packs.length
|
||||
? packs[packs.length - 1]?.productId
|
||||
: null;
|
||||
!bestDiscountPack && packs.length ? packs[packs.length - 1]?.productId : null
|
||||
|
||||
return packs.reduce((acc, pack, index) => {
|
||||
if (!pack?.productId) {
|
||||
return acc;
|
||||
return acc
|
||||
}
|
||||
|
||||
const currentPack = packsWithPrice.find(
|
||||
(item) => item.productId === pack.productId,
|
||||
);
|
||||
const computedPricePerCoin = currentPack?.pricePerCoin;
|
||||
const currentPack = packsWithPrice.find((item) => item.productId === pack.productId)
|
||||
const computedPricePerCoin = currentPack?.pricePerCoin
|
||||
const fallbackDiscount =
|
||||
FALLBACK_DISCOUNT_STEPS[
|
||||
Math.min(index, FALLBACK_DISCOUNT_STEPS.length - 1)
|
||||
] || 0;
|
||||
FALLBACK_DISCOUNT_STEPS[Math.min(index, FALLBACK_DISCOUNT_STEPS.length - 1)] || 0
|
||||
const pricePerCoin =
|
||||
typeof computedPricePerCoin === "number" && isFinite(computedPricePerCoin)
|
||||
typeof computedPricePerCoin === 'number' && isFinite(computedPricePerCoin)
|
||||
? computedPricePerCoin
|
||||
: fallbackBase * (1 - fallbackDiscount / 100);
|
||||
: fallbackBase * (1 - fallbackDiscount / 100)
|
||||
|
||||
const baseReference = fallbackBase || 1;
|
||||
const baseReference = fallbackBase || 1
|
||||
const rawDiscount =
|
||||
baseReference && pricePerCoin
|
||||
? ((baseReference - pricePerCoin) / baseReference) * 100
|
||||
: 0;
|
||||
const discountPercent = Math.max(
|
||||
0,
|
||||
Number.isFinite(rawDiscount) ? Math.round(rawDiscount) : 0,
|
||||
);
|
||||
baseReference && pricePerCoin ? ((baseReference - pricePerCoin) / baseReference) * 100 : 0
|
||||
const discountPercent = Math.max(0, Number.isFinite(rawDiscount) ? Math.round(rawDiscount) : 0)
|
||||
|
||||
const isBasePack =
|
||||
(basePack && basePack.productId === pack.productId) ||
|
||||
(!basePack && index === 0);
|
||||
(basePack && basePack.productId === pack.productId) || (!basePack && index === 0)
|
||||
|
||||
acc[pack.productId] = {
|
||||
currency: pack.currency,
|
||||
@@ -147,43 +130,35 @@ const computeCoinPackPricing = (packs = []) => {
|
||||
isBestValue:
|
||||
(bestDiscountPack && bestDiscountPack.productId === pack.productId) ||
|
||||
(!bestDiscountPack && fallbackBestId === pack.productId),
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
function CoinPackCard({
|
||||
pack,
|
||||
selected,
|
||||
pricingDetail,
|
||||
onSelect,
|
||||
staticDiscountPercent,
|
||||
}) {
|
||||
const handleSelect = React.useCallback(() => {
|
||||
if (typeof onSelect !== "function" || !pack?.productId) {
|
||||
return;
|
||||
}
|
||||
onSelect(pack.productId);
|
||||
}, [onSelect, pack?.productId]);
|
||||
|
||||
const formattedPrice = formatCurrency(pack?.unitAmount, pack?.currency);
|
||||
const perCoinPrice = pricingDetail?.formattedPricePerCoin;
|
||||
const discountPercent = pricingDetail?.discountPercent;
|
||||
const hasStaticDiscount = typeof staticDiscountPercent === "number";
|
||||
const effectiveDiscountPercent = hasStaticDiscount
|
||||
? staticDiscountPercent
|
||||
: discountPercent;
|
||||
const isBaseReference = !hasStaticDiscount && pricingDetail?.isBasePack;
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
function CoinPackCard({ pack, selected, pricingDetail, onSelect, staticDiscountPercent }) {
|
||||
const handleSelect = React.useCallback(() => {
|
||||
if (typeof onSelect !== 'function' || !pack?.productId) {
|
||||
return
|
||||
}
|
||||
onSelect(pack.productId)
|
||||
}, [onSelect, pack?.productId])
|
||||
|
||||
const formattedPrice = formatCurrency(pack?.unitAmount, pack?.currency)
|
||||
const perCoinPrice = pricingDetail?.formattedPricePerCoin
|
||||
const discountPercent = pricingDetail?.discountPercent
|
||||
const hasStaticDiscount = typeof staticDiscountPercent === 'number'
|
||||
const effectiveDiscountPercent = hasStaticDiscount ? staticDiscountPercent : discountPercent
|
||||
const isBaseReference = !hasStaticDiscount && pricingDetail?.isBasePack
|
||||
const discountLabel = hasStaticDiscount
|
||||
? staticDiscountPercent > 0
|
||||
? `-${staticDiscountPercent}%`
|
||||
: null
|
||||
: isBaseReference
|
||||
? "Pack de base (référence)"
|
||||
: typeof discountPercent === "number"
|
||||
? 'Pack de base (référence)'
|
||||
: typeof discountPercent === 'number'
|
||||
? `-${discountPercent}% vs pack de base`
|
||||
: null;
|
||||
: null
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
@@ -212,17 +187,12 @@ function CoinPackCard({
|
||||
{discountLabel ? (
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: -5,
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
styles.discountBadge,
|
||||
isBaseReference && styles.discountBadgeBase,
|
||||
]}
|
||||
>
|
||||
<View style={[styles.discountBadge, isBaseReference && styles.discountBadgeBase]}>
|
||||
<Text style={[styles.discountText]}>{discountLabel}</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -230,103 +200,83 @@ function CoinPackCard({
|
||||
{pack?.name ? <Text style={styles.packName}>{pack.name}</Text> : null}
|
||||
</View>
|
||||
<View style={styles.priceBlock}>
|
||||
{formattedPrice ? (
|
||||
<Text style={styles.packPrice}>{formattedPrice}</Text>
|
||||
) : null}
|
||||
{formattedPrice ? <Text style={styles.packPrice}>{formattedPrice}</Text> : null}
|
||||
</View>
|
||||
</BlurView>
|
||||
</Pressable>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
const CoinPackModal = ({ visible, onClose }) => {
|
||||
const [selectedPackId, setSelectedPackId] = React.useState(null);
|
||||
const [isProcessing, setIsProcessing] = React.useState(false);
|
||||
const [errorMessage, setErrorMessage] = React.useState(null);
|
||||
const modalMaxWidth = isWeb ? WEB_MODAL_MAX_WIDTH : undefined;
|
||||
const [selectedPackId, setSelectedPackId] = React.useState(null)
|
||||
const [isProcessing, setIsProcessing] = React.useState(false)
|
||||
const [errorMessage, setErrorMessage] = React.useState(null)
|
||||
const modalMaxWidth = isWeb ? WEB_MODAL_MAX_WIDTH : undefined
|
||||
|
||||
const {
|
||||
coinPacks,
|
||||
isCatalogLoading,
|
||||
catalogError,
|
||||
refreshCatalog,
|
||||
createCoinPackCheckout,
|
||||
} = useStripe();
|
||||
const packPricingById = React.useMemo(
|
||||
() => computeCoinPackPricing(coinPacks),
|
||||
[coinPacks],
|
||||
);
|
||||
const { coinPacks, isCatalogLoading, catalogError, refreshCatalog, createCoinPackCheckout } =
|
||||
useStripe()
|
||||
const packPricingById = React.useMemo(() => computeCoinPackPricing(coinPacks), [coinPacks])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!coinPacks.length) {
|
||||
setSelectedPackId(null);
|
||||
return;
|
||||
setSelectedPackId(null)
|
||||
return
|
||||
}
|
||||
setSelectedPackId((current) => {
|
||||
if (
|
||||
current &&
|
||||
coinPacks.some((pack) => pack?.productId && pack.productId === current)
|
||||
) {
|
||||
return current;
|
||||
if (current && coinPacks.some((pack) => pack?.productId && pack.productId === current)) {
|
||||
return current
|
||||
}
|
||||
return coinPacks[0]?.productId || null;
|
||||
});
|
||||
}, [coinPacks]);
|
||||
return coinPacks[0]?.productId || null
|
||||
})
|
||||
}, [coinPacks])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!visible) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (!coinPacks.length && !isCatalogLoading && !catalogError) {
|
||||
refreshCatalog();
|
||||
refreshCatalog()
|
||||
}
|
||||
}, [
|
||||
visible,
|
||||
coinPacks.length,
|
||||
isCatalogLoading,
|
||||
catalogError,
|
||||
refreshCatalog,
|
||||
]);
|
||||
}, [visible, coinPacks.length, isCatalogLoading, catalogError, refreshCatalog])
|
||||
|
||||
const closeModal = React.useCallback(
|
||||
({ force = false } = {}) => {
|
||||
if (isProcessing && !force) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
setIsProcessing(false);
|
||||
onClose?.();
|
||||
setIsProcessing(false)
|
||||
onClose?.()
|
||||
},
|
||||
[isProcessing, onClose],
|
||||
);
|
||||
[isProcessing, onClose]
|
||||
)
|
||||
|
||||
const handleCheckout = React.useCallback(async () => {
|
||||
if (!selectedPackId) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
setIsProcessing(true);
|
||||
setErrorMessage(null);
|
||||
setIsProcessing(true)
|
||||
setErrorMessage(null)
|
||||
|
||||
try {
|
||||
await createCoinPackCheckout(selectedPackId);
|
||||
closeModal({ force: true });
|
||||
await createCoinPackCheckout(selectedPackId)
|
||||
closeModal({ force: true })
|
||||
} catch (error) {
|
||||
console.error("[CoinPackModal] checkout error", error);
|
||||
console.error('[CoinPackModal] checkout error', error)
|
||||
setErrorMessage(
|
||||
error?.message ||
|
||||
"Une erreur est survenue lors de la création de la session Stripe.",
|
||||
);
|
||||
error?.message || 'Une erreur est survenue lors de la création de la session Stripe.'
|
||||
)
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
setIsProcessing(false)
|
||||
}
|
||||
}, [selectedPackId, createCoinPackCheckout, closeModal]);
|
||||
}, [selectedPackId, createCoinPackCheckout, closeModal])
|
||||
|
||||
const handleClose = React.useCallback(() => {
|
||||
closeModal();
|
||||
}, [closeModal]);
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
const isLoadingCoinPacks = isCatalogLoading && !coinPacks.length;
|
||||
const combinedErrorMessage = errorMessage || catalogError;
|
||||
const isLoadingCoinPacks = isCatalogLoading && !coinPacks.length
|
||||
const combinedErrorMessage = errorMessage || catalogError
|
||||
|
||||
const renderContent = () => {
|
||||
if (isLoadingCoinPacks) {
|
||||
@@ -334,15 +284,13 @@ const CoinPackModal = ({ visible, onClose }) => {
|
||||
<View style={styles.loaderContainer}>
|
||||
<ActivityIndicator color={Palette.white} />
|
||||
</View>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
if (!coinPacks.length) {
|
||||
return (
|
||||
<Text style={styles.emptyState}>
|
||||
Aucun pack de pièces n'est disponible pour le moment.
|
||||
</Text>
|
||||
);
|
||||
<Text style={styles.emptyState}>Aucun pack de pièces n'est disponible pour le moment.</Text>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -364,25 +312,20 @@ const CoinPackModal = ({ visible, onClose }) => {
|
||||
/>
|
||||
))}
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
animationType="slide"
|
||||
transparent
|
||||
visible={visible}
|
||||
onRequestClose={handleClose}
|
||||
>
|
||||
<Modal animationType="slide" transparent visible={visible} onRequestClose={handleClose}>
|
||||
<View style={styles.overlay}>
|
||||
<CreateLyricsHeader
|
||||
containerStyle={{
|
||||
width: "100%",
|
||||
width: '100%',
|
||||
maxWidth: modalMaxWidth,
|
||||
alignSelf: "center",
|
||||
alignSelf: 'center',
|
||||
borderWidth: isWeb ? 1 : 0,
|
||||
borderColor: "rgba(255,255,255,0.16)",
|
||||
backgroundColor: isWeb ? "rgba(12, 10, 18, 0.85)" : undefined,
|
||||
borderColor: 'rgba(255,255,255,0.16)',
|
||||
backgroundColor: isWeb ? 'rgba(12, 10, 18, 0.85)' : undefined,
|
||||
}}
|
||||
>
|
||||
<View style={styles.container}>
|
||||
@@ -400,27 +343,19 @@ const CoinPackModal = ({ visible, onClose }) => {
|
||||
<View style={styles.content}>{renderContent()}</View>
|
||||
|
||||
<Text style={styles.indicativeNote}>
|
||||
Tarifs indicatifs : les prix et quantités de jetons sont amenés à
|
||||
évoluer. Les remises sont affichées vs le pack de base pour mieux
|
||||
valoriser les offres volumineuses.
|
||||
Tarifs indicatifs : les prix et quantités de jetons sont amenés à évoluer. Les remises
|
||||
sont affichées vs le pack de base pour mieux valoriser les offres volumineuses.
|
||||
</Text>
|
||||
|
||||
<View style={styles.actions}>
|
||||
<GradientButton
|
||||
title={isProcessing ? "Redirection..." : "Acheter ce pack"}
|
||||
title={isProcessing ? 'Redirection...' : 'Acheter ce pack'}
|
||||
onPress={handleCheckout}
|
||||
disabled={
|
||||
!selectedPackId ||
|
||||
isProcessing ||
|
||||
isLoadingCoinPacks ||
|
||||
!coinPacks.length
|
||||
!selectedPackId || isProcessing || isLoadingCoinPacks || !coinPacks.length
|
||||
}
|
||||
/>
|
||||
<BorderGradientButton
|
||||
title="Fermer"
|
||||
onPress={handleClose}
|
||||
disabled={isProcessing}
|
||||
/>
|
||||
<BorderGradientButton title="Fermer" onPress={handleClose} disabled={isProcessing} />
|
||||
</View>
|
||||
|
||||
<Text style={styles.disclaimer}>
|
||||
@@ -430,81 +365,81 @@ const CoinPackModal = ({ visible, onClose }) => {
|
||||
</CreateLyricsHeader>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default CoinPackModal;
|
||||
export default CoinPackModal
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: gutters,
|
||||
paddingVertical: gutters * 1.5,
|
||||
},
|
||||
container: {
|
||||
gap: 24,
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
paddingBottom: gutters,
|
||||
paddingHorizontal: isWeb ? gutters * 1.5 : 0,
|
||||
width: "100%",
|
||||
width: '100%',
|
||||
maxWidth: isWeb ? WEB_MODAL_MAX_WIDTH : undefined,
|
||||
},
|
||||
header: {
|
||||
gap: 8,
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
title: {
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
fontSize: 22,
|
||||
color: Palette.white,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
},
|
||||
subtitle: {
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
},
|
||||
errorText: {
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
fontSize: 14,
|
||||
color: Palette.red,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
content: {
|
||||
width: "100%",
|
||||
maxWidth: isWeb ? WEB_MODAL_MAX_WIDTH : "100%",
|
||||
alignSelf: "center",
|
||||
width: '100%',
|
||||
maxWidth: isWeb ? WEB_MODAL_MAX_WIDTH : '100%',
|
||||
alignSelf: 'center',
|
||||
maxHeight: isWeb ? 420 : 360,
|
||||
},
|
||||
loaderContainer: {
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingVertical: 32,
|
||||
},
|
||||
emptyState: {
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
fontSize: 14,
|
||||
color: "rgba(255, 255, 255, 0.72)",
|
||||
textAlign: "center",
|
||||
color: 'rgba(255, 255, 255, 0.72)',
|
||||
textAlign: 'center',
|
||||
paddingHorizontal: 24,
|
||||
},
|
||||
packList: {
|
||||
width: "100%",
|
||||
maxWidth: isWeb ? WEB_MODAL_MAX_WIDTH : "100%",
|
||||
alignSelf: "center",
|
||||
width: '100%',
|
||||
maxWidth: isWeb ? WEB_MODAL_MAX_WIDTH : '100%',
|
||||
alignSelf: 'center',
|
||||
gap: gutters,
|
||||
paddingHorizontal: isWeb ? gutters * 1.5 : 0,
|
||||
},
|
||||
packListWeb: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
packListMobile: {
|
||||
paddingBottom: 12,
|
||||
@@ -512,15 +447,15 @@ const styles = StyleSheet.create({
|
||||
cardWrapper: {
|
||||
flex: 1,
|
||||
borderRadius: 20,
|
||||
overflow: "hidden",
|
||||
overflow: 'hidden',
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255, 255, 255, 0.12)",
|
||||
backgroundColor: "rgba(12, 14, 18, 0.45)",
|
||||
borderColor: 'rgba(255, 255, 255, 0.12)',
|
||||
backgroundColor: 'rgba(12, 14, 18, 0.45)',
|
||||
minWidth: isWeb ? 220 : undefined,
|
||||
},
|
||||
cardWrapperSelected: {
|
||||
borderColor: Palette.primary,
|
||||
shadowColor: "#000",
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 10 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 18,
|
||||
@@ -534,16 +469,16 @@ const styles = StyleSheet.create({
|
||||
gap: 16,
|
||||
paddingVertical: 20,
|
||||
paddingHorizontal: 24,
|
||||
justifyContent: "space-between",
|
||||
backgroundColor: "rgba(48, 52, 56, 0.55)",
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: 'rgba(48, 52, 56, 0.55)',
|
||||
},
|
||||
cardHeader: {
|
||||
gap: 12,
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
},
|
||||
coinRow: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
},
|
||||
coinAmount: {
|
||||
@@ -555,37 +490,37 @@ const styles = StyleSheet.create({
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
},
|
||||
packDescription: {
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
fontSize: 14,
|
||||
color: "rgba(255, 255, 255, 0.72)",
|
||||
textAlign: "center",
|
||||
color: 'rgba(255, 255, 255, 0.72)',
|
||||
textAlign: 'center',
|
||||
},
|
||||
priceBlock: {
|
||||
gap: 2,
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
},
|
||||
packPrice: {
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
fontSize: 18,
|
||||
color: Palette.white,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
},
|
||||
pricePerCoin: {
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
fontSize: 13,
|
||||
color: "rgba(255, 255, 255, 0.7)",
|
||||
textAlign: "center",
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
textAlign: 'center',
|
||||
},
|
||||
discountBadge: {
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 6,
|
||||
borderRadius: 999,
|
||||
backgroundColor: "rgba(255, 255, 255, 0.06)",
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.06)',
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255, 255, 255, 0.12)",
|
||||
borderColor: 'rgba(255, 255, 255, 0.12)',
|
||||
},
|
||||
discountBadgeBase: {
|
||||
borderColor: Palette.primary,
|
||||
@@ -599,7 +534,7 @@ const styles = StyleSheet.create({
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
fontSize: 13,
|
||||
color: Palette.white,
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
},
|
||||
discountTextBest: {
|
||||
color: Palette.white,
|
||||
@@ -607,22 +542,22 @@ const styles = StyleSheet.create({
|
||||
discountHelper: {
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
fontSize: 12,
|
||||
color: "rgba(255, 255, 255, 0.72)",
|
||||
textAlign: "center",
|
||||
color: 'rgba(255, 255, 255, 0.72)',
|
||||
textAlign: 'center',
|
||||
},
|
||||
discountHelperMuted: {
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
fontSize: 12,
|
||||
color: "rgba(255, 255, 255, 0.56)",
|
||||
textAlign: "center",
|
||||
color: 'rgba(255, 255, 255, 0.56)',
|
||||
textAlign: 'center',
|
||||
},
|
||||
tagBestValue: {
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 10,
|
||||
backgroundColor: Palette.transparentGreen,
|
||||
alignSelf: "center",
|
||||
position: "absolute",
|
||||
alignSelf: 'center',
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
},
|
||||
tagBestValueText: {
|
||||
@@ -631,22 +566,22 @@ const styles = StyleSheet.create({
|
||||
color: Palette.white,
|
||||
},
|
||||
actions: {
|
||||
width: "85%",
|
||||
alignSelf: "center",
|
||||
width: '85%',
|
||||
alignSelf: 'center',
|
||||
gap: 16,
|
||||
},
|
||||
disclaimer: {
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
fontSize: 13,
|
||||
color: "rgba(255, 255, 255, 0.7)",
|
||||
textAlign: "center",
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
textAlign: 'center',
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
indicativeNote: {
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
fontSize: 12,
|
||||
color: "rgba(255, 255, 255, 0.62)",
|
||||
textAlign: "center",
|
||||
color: 'rgba(255, 255, 255, 0.62)',
|
||||
textAlign: 'center',
|
||||
paddingHorizontal: 24,
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user