diff --git a/app/(app)/settings/index.tsx b/app/(app)/settings/index.tsx index e7e2404..b8f40d5 100644 --- a/app/(app)/settings/index.tsx +++ b/app/(app)/settings/index.tsx @@ -125,7 +125,7 @@ export default function Settings(): JSX.Element { { onPress: () => { if (Platform.OS !== 'web') { - push('/settings/sponsors'); + push('/settings/points'); return; } @@ -133,7 +133,7 @@ export default function Settings(): JSX.Element { }, startElement: ( ), - title: t('settings.sponsors'), + title: t('settings.points'), }, { onPress: () => push('/settings/block-users'), diff --git a/app/(app)/settings/points.tsx b/app/(app)/settings/points.tsx new file mode 100644 index 0000000..bdc08e2 --- /dev/null +++ b/app/(app)/settings/points.tsx @@ -0,0 +1,248 @@ +import { + endConnection, + finishTransaction, + getProducts, + initConnection, + isProductAndroid, + isProductIos, + purchaseErrorListener, + purchaseUpdatedListener, + requestPurchase, +} from 'expo-iap'; +import type { + Product, + ProductPurchase, + PurchaseError, +} from 'expo-iap/build/ExpoIap.types'; +import type {} from 'expo-iap/build/types/ExpoIapAndroid.types'; +import {Stack} from 'expo-router'; +import {useEffect, useState} from 'react'; +import {InteractionManager, View} from 'react-native'; +import {t} from '../../../src/STRINGS'; +import styled, {css} from '@emotion/native'; +import { + fetchCreatePurchase, + fetchUserPoints, +} from '../../../src/apis/purchaseQueries'; +import {useRecoilValue} from 'recoil'; +import {authRecoilState} from '../../../src/recoil/atoms'; +import {Button, Icon, Typography, useDooboo} from 'dooboo-ui'; +import {showAlert} from '../../../src/utils/alert'; + +const productSkus = [ + 'cpk.points.200', + 'cpk.points.500', + 'cpk.points.1000', + 'cpk.points.5000', + 'cpk.points.10000', + 'cpk.points.30000', +]; + +const Container = styled.View` + background-color: ${({theme}) => theme.bg.basic}; + + flex: 1; + align-self: stretch; +`; + +const Content = styled.ScrollView` + padding: 16px; +`; + +export default function App() { + const [isConnected, setIsConnected] = useState(false); + const [products, setProducts] = useState([]); + const [userPoints, setUserPoints] = useState(0); + const {authId} = useRecoilValue(authRecoilState); + const {theme} = useDooboo(); + + useEffect(() => { + const getUserPoints = async () => { + const data = await fetchUserPoints(authId!); + setUserPoints(data || 0); + }; + + authId && getUserPoints(); + }, [authId]); + + useEffect(() => { + const initIAP = async () => { + if (await initConnection()) { + setIsConnected(true); + } + + const products = await getProducts(productSkus); + products.sort((a, b) => { + if (isProductAndroid(a) && isProductAndroid(b)) { + return ( + parseInt(a?.oneTimePurchaseOfferDetails?.priceAmountMicros || '0') - + parseInt(b?.oneTimePurchaseOfferDetails?.priceAmountMicros || '0') + ); + } + + if (isProductIos(a) && isProductIos(b)) { + return a.price - b.price; + } + + return 0; + }); + setProducts(products); + }; + + initIAP(); + + return () => { + endConnection(); + }; + }, []); + + useEffect(() => { + const purchaseUpdatedSubs = purchaseUpdatedListener( + (purchase: ProductPurchase) => { + const ackPurchase = async (purchase: ProductPurchase) => { + await finishTransaction({ + purchase, + isConsumable: true, + }); + }; + + InteractionManager.runAfterInteractions(async () => { + const receipt = purchase && purchase.transactionReceipt; + + if (receipt) { + const result = await fetchCreatePurchase({ + authId: authId!, + points: parseInt(purchase?.productId.split('.').pop() || '0'), + productId: purchase?.productId || '', + receipt, + }); + + if (result) { + ackPurchase(purchase); + } + } + }); + }, + ); + + const purchaseErrorSubs = purchaseErrorListener((error: PurchaseError) => { + InteractionManager.runAfterInteractions(() => { + showAlert(error?.message); + }); + }); + + return () => { + purchaseUpdatedSubs.remove(); + purchaseErrorSubs.remove(); + endConnection(); + }; + }, [authId]); + + return ( + + + + + {t('points.myPoints')} + + + + + {userPoints} + + + + + + {isConnected + ? products.map((item) => { + if (isProductAndroid(item)) { + return ( + + + {item.title} + +