Skip to content

Commit

Permalink
feat(wip): sponsors page
Browse files Browse the repository at this point in the history
  • Loading branch information
hyochan committed Aug 17, 2024
1 parent fce5336 commit 10f66b4
Show file tree
Hide file tree
Showing 8 changed files with 370 additions and 32 deletions.
2 changes: 1 addition & 1 deletion app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default ({config}: ConfigContext): ExpoConfig => ({
'expo-build-properties',
{
// https://github.com/software-mansion/react-native-screens/issues/2219
ios: {newArchEnabled: true},
ios: {newArchEnabled: true, deploymentTarget: '15.0'},
android: {newArchEnabled: true},
},
],
Expand Down
30 changes: 30 additions & 0 deletions app/(app)/settings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import useAppState from '../../../src/hooks/useAppState';
import {openEmail} from '../../../src/utils/common';
import ErrorBoundary from 'react-native-error-boundary';
import FallbackComponent from '../../../src/components/uis/FallbackComponent';
import {showAlert} from '../../../src/utils/alert';

const Container = styled.View`
background-color: ${({theme}) => theme.bg.basic};
Expand Down Expand Up @@ -121,6 +122,35 @@ export default function Settings(): JSX.Element {
),
title: t('settings.updateProfile'),
},
{
onPress: () => {
if (Platform.OS !== 'web') {
push('/settings/sponsors');
return;
}

showAlert(t('error.notSupportedInWeb'));
},
startElement: (
<Icon
name="HeartStraight"
size={24}
style={css`
margin-right: 16px;
`}
/>
),
endElement: (
<Icon
name="CaretRight"
size={16}
style={css`
margin-left: auto;
`}
/>
),
title: t('settings.sponsors'),
},
{
onPress: () => push('/settings/block-users'),
startElement: (
Expand Down
270 changes: 270 additions & 0 deletions app/(app)/settings/sponsors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
import {
endConnection,
getProducts,
getSubscriptions,
initConnection,
isProductAndroid,
isProductIos,
isSubscriptionProductAndroid,
isSubscriptionProductIos,
purchaseErrorListener,
purchaseUpdatedListener,
requestPurchase,
requestSubscription,
} from 'expo-iap';
import type {
Product,
ProductPurchase,
PurchaseError,
SubscriptionProduct,
} from 'expo-iap/build/ExpoIap.types';
import type {RequestSubscriptionAndroidProps} from 'expo-iap/build/types/ExpoIapAndroid.types';
import {Stack} from 'expo-router';
import {useEffect, useState} from 'react';
import {
Alert,
Button,
InteractionManager,
Pressable,
SafeAreaView,
ScrollView,
StyleSheet,
Text,
View,
} from 'react-native';
import {t} from '../../../src/STRINGS';

const productSkus = [
'cpk.points.200',
'cpk.points.500',
'cpk.points.1000',
'cpk.points.5000',
'cpk.points.10000',
'cpk.points.30000',
];

const subscriptionSkus = [
'cpk.membership.monthly.bronze',
'cpk.membership.monthly.silver',
];

const operations = [
'initConnection',
'getProducts',
'getSubscriptions',
'endConnection',
];
type Operation = (typeof operations)[number];

export default function App() {
const [isConnected, setIsConnected] = useState(false);
const [products, setProducts] = useState<Product[]>([]);
const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);

const handleOperation = async (operation: Operation) => {
switch (operation) {
case 'initConnection':
if (await initConnection()) setIsConnected(true);
return;

case 'endConnection':
if (await endConnection()) {
setProducts([]);
setIsConnected(false);
}
break;

case 'getProducts':
try {
const products = await getProducts(productSkus);
setProducts(products);
} catch (error) {
console.error(error);
}
break;

case 'getSubscriptions':
try {
const subscriptions = await getSubscriptions(subscriptionSkus);
setSubscriptions(subscriptions);
} catch (error) {
console.error(error);
}
break;

default:
console.log('Unknown operation');
}
};

useEffect(() => {
const purchaseUpdatedSubs = purchaseUpdatedListener(
(purchase: ProductPurchase) => {
InteractionManager.runAfterInteractions(() => {
Alert.alert('Purchase updated', JSON.stringify(purchase));
});
},
);

const purchaseErrorSubs = purchaseErrorListener((error: PurchaseError) => {
InteractionManager.runAfterInteractions(() => {
Alert.alert('Purchase error', JSON.stringify(error));
});
});

return () => {
purchaseUpdatedSubs.remove();
purchaseErrorSubs.remove();
endConnection();
};
}, []);

return (
<SafeAreaView style={styles.container}>
<Stack.Screen options={{title: t('settings.sponsors')}} />
<Text style={styles.title}>Expo IAP Example</Text>
<View style={styles.buttons}>
<ScrollView contentContainerStyle={styles.buttonsWrapper} horizontal>
{operations.map((operation) => (
<Pressable
key={operation}
onPress={() => handleOperation(operation)}
>
<View style={styles.buttonView}>
<Text>{operation}</Text>
</View>
</Pressable>
))}
</ScrollView>
</View>
<View style={styles.content}>
{!isConnected ? (
<Text>Not connected</Text>
) : (
<View style={{gap: 12}}>
<Text style={{fontSize: 20}}>Products</Text>
{products.map((item) => {
if (isProductAndroid(item)) {
return (
<View key={item.title} style={{gap: 12}}>
<Text>
{item.title} -{' '}
{item.oneTimePurchaseOfferDetails?.formattedPrice}
</Text>
<Button
title="Buy"
onPress={() => {
requestPurchase({
skus: [item.productId],
});
}}
/>
</View>
);
}

if (isProductIos(item)) {
return (
<View key={item.id} style={{gap: 12}}>
<Text>
{item.displayName} - {item.displayPrice}
</Text>
<Button
title="Buy"
onPress={() => {
requestPurchase({
sku: item.id,
});
}}
/>
</View>
);
}
})}

<Text style={{fontSize: 20}}>Subscriptions</Text>
{subscriptions.map((item) => {
if (isSubscriptionProductAndroid(item)) {
return item.subscriptionOfferDetails?.map((offer) => (
<View key={offer.offerId} style={{gap: 12}}>
<Text>
{item.title} -{' '}
{offer.pricingPhases.pricingPhaseList
.map((ppl) => ppl.billingPeriod)
.join(',')}
</Text>
<Button
title="Subscribe"
onPress={() => {
requestSubscription({
skus: [item.productId],
...(offer.offerToken && {
subscriptionOffers: [
{
sku: item.productId,
offerToken: offer.offerToken,
},
],
}),
} as RequestSubscriptionAndroidProps);
}}
/>
</View>
));
}

if (isSubscriptionProductIos(item)) {
return (
<View key={item.id} style={{gap: 12}}>
<Text>
{item.displayName} - {item.displayPrice}
</Text>
<Button
title="Subscribe"
onPress={() => {
requestSubscription({sku: item.id});
}}
/>
</View>
);
}
})}
</View>
)}
</View>
</SafeAreaView>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
},
title: {
marginTop: 24,
fontSize: 20,
fontWeight: 'bold',
},
buttons: {
height: 90,
},
buttonsWrapper: {
padding: 24,

gap: 8,
},
buttonView: {
borderRadius: 8,
borderWidth: 1,
borderColor: '#000',
padding: 8,
},
content: {
flex: 1,
alignSelf: 'stretch',
padding: 24,
gap: 12,
},
});
2 changes: 2 additions & 0 deletions assets/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"failedToFetchData": "Failed to fetch data",
"failedToGetPushToken": "Failed to get push token for push notification!",
"mustUsePhysicalDeviceForSubject": "Must use physical device for {{subject}}",
"notSupportedInWeb": "This feature is not supported in web",
"projectIdNotFound": "Project ID not found",
"unableToOpenEmailClient": "Unable to open email client"
},
Expand Down Expand Up @@ -144,6 +145,7 @@
"darkMode": "Dark Mode",
"loginInfo": "Login Information",
"notificationSettings": "Notification Settings",
"sponsors": "Sponsors",
"termsOfService": "Terms of Service",
"title": "Settings",
"updateProfile": "Update Profile"
Expand Down
2 changes: 2 additions & 0 deletions assets/langs/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"failedToFetchData": "데이터를 가져오는 데 실패했습니다",
"failedToGetPushToken": "푸시 토큰을 가져오는 데 실패했습니다",
"mustUsePhysicalDeviceForSubject": "{{subject}}을(를) 위해 실제 기기를 사용해야 합니다",
"notSupportedInWeb": "웹에서는 지원하지 않는 기능입니다.",
"projectIdNotFound": "프로젝트 ID를 찾을 수 없습니다",
"unableToOpenEmailClient": "이메일 클라이언트를 열 수 없습니다"
},
Expand Down Expand Up @@ -144,6 +145,7 @@
"darkMode": "다크 모드",
"loginInfo": "로그인 정보",
"notificationSettings": "알림 설정",
"sponsors": "후원사",
"termsOfService": "서비스 약관",
"title": "설정",
"updateProfile": "프로필 수정"
Expand Down
5 changes: 4 additions & 1 deletion eas.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
}
},
"production": {
"env": {
"ENVIRONMENT": "production"
},
"channel": "production",
"cache": {
"disabled": true
Expand All @@ -32,4 +35,4 @@
"submit": {
"production": {}
}
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"expo-device": "~6.0.2",
"expo-file-system": "^17.0.1",
"expo-font": "~12.0.9",
"expo-iap": "^2.0.0-rc.2",
"expo-image": "~1.12.13",
"expo-image-picker": "~15.0.7",
"expo-linear-gradient": "^13.0.2",
Expand Down
Loading

0 comments on commit 10f66b4

Please sign in to comment.