Browse Source

DRAFT | Purchases

pull/3/head
Vlad Narizhnyi 1 year ago
parent
commit
359f5a6b82
  1. 28
      babel.config.js
  2. 6
      ios/Podfile.lock
  3. 14
      ios/Truth.xcodeproj/project.pbxproj
  4. 16
      ios/Truth/Info.plist
  5. 43
      metro.config.js
  6. 953
      package-lock.json
  7. 171
      package.json
  8. 1
      src/module/common/typing/enums/index.ts
  9. 5
      src/module/common/typing/enums/products.enum.ts
  10. 2
      src/module/root/screens/loading-screen.tsx
  11. 1
      src/module/settings/atoms/index.ts
  12. 74
      src/module/settings/atoms/purchases.atom.tsx
  13. 1
      src/module/settings/config/index.ts
  14. 16
      src/module/settings/config/purchases.config.ts
  15. 1
      src/module/settings/index.ts
  16. 59
      src/module/settings/screens/purchases.screen.tsx
  17. 12
      src/module/settings/screens/settings.screen.tsx
  18. 1
      src/module/settings/services/index.ts
  19. 83
      src/module/settings/services/purchases.service.ts
  20. 81
      tsconfig.json

28
babel.config.js

@ -1,3 +1,27 @@ @@ -1,3 +1,27 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
};
presets: ['module:metro-react-native-babel-preset'],
plugins: [
[
require.resolve('babel-plugin-module-resolver'),
{
root: ['./'],
alias: {
/**
* Regular expression is used to match all files inside `./src` directory and map each `.src/folder/[..]` to `~folder/[..]` path
*/
'^~(.+)': './src/\\1',
},
extensions: [
'.ios.js',
'.android.js',
'.js',
'.jsx',
'.json',
'.tsx',
'.ts',
'.native.js',
],
},
],
],
}

6
ios/Podfile.lock

@ -1276,6 +1276,8 @@ PODS: @@ -1276,6 +1276,8 @@ PODS:
- RNGestureHandler (2.13.4):
- RCT-Folly (= 2021.07.22.00)
- React-Core
- RNIap (12.11.0):
- React-Core
- RNScreens (3.27.0):
- RCT-Folly (= 2021.07.22.00)
- React-Core
@ -1362,6 +1364,7 @@ DEPENDENCIES: @@ -1362,6 +1364,7 @@ DEPENDENCIES:
- "RNFBFirestore (from `../node_modules/@react-native-firebase/firestore`)"
- "RNFlashList (from `../node_modules/@shopify/flash-list`)"
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNIap (from `../node_modules/react-native-iap`)
- RNScreens (from `../node_modules/react-native-screens`)
- RNSVG (from `../node_modules/react-native-svg`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
@ -1492,6 +1495,8 @@ EXTERNAL SOURCES: @@ -1492,6 +1495,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/@shopify/flash-list"
RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler"
RNIap:
:path: "../node_modules/react-native-iap"
RNScreens:
:path: "../node_modules/react-native-screens"
RNSVG:
@ -1575,6 +1580,7 @@ SPEC CHECKSUMS: @@ -1575,6 +1580,7 @@ SPEC CHECKSUMS:
RNFBFirestore: f16efbd47cd136fe84cb93dc87ad4155807db221
RNFlashList: 4b4b6b093afc0df60ae08f9cbf6ccd4c836c667a
RNGestureHandler: 6e46dde1f87e5f018a54fe5d40cd0e0b942b49ee
RNIap: fc9af04ee706894a80c9d8f979bae930b0dee191
RNScreens: 3c2d122f5e08c192e254c510b212306da97d2581
RNSVG: d7d7bc8229af3842c9cfc3a723c815a52cdd1105
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8

14
ios/Truth.xcodeproj/project.pbxproj

@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
3F5CDF352995282400BD4B7F /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3F5CDF342995282400BD4B7F /* GoogleService-Info.plist */; };
52BCE0F228C76D5A008C74BC /* Fonts in Resources */ = {isa = PBXBuildFile; fileRef = 52BCE0EE28C76941008C74BC /* Fonts */; };
52BCE0F328C77143008C74BC /* fontello.ttf in Resources */ = {isa = PBXBuildFile; fileRef = DA10DFDF3E05483BA976401B /* fontello.ttf */; };
63120D812B0DE8C900E76BCD /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63120D802B0DE8C900E76BCD /* StoreKit.framework */; };
637B62A42B07BAFB008D8917 /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 637B62A32B07BAFB008D8917 /* Roboto-Bold.ttf */; };
637B62A62B07BB0B008D8917 /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 637B62A52B07BB0B008D8917 /* Roboto-Regular.ttf */; };
8D22E0E3287C39ED0031C6E5 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D22E0E2287C39ED0031C6E5 /* Launch Screen.storyboard */; };
@ -47,6 +48,7 @@ @@ -47,6 +48,7 @@
3F5CDF302995158300BD4B7F /* ios */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ios; sourceTree = "<group>"; };
3F5CDF342995282400BD4B7F /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
52BCE0EE28C76941008C74BC /* Fonts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Fonts; sourceTree = "<group>"; };
63120D802B0DE8C900E76BCD /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
637B62A12B07B836008D8917 /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "Roboto-Regular.ttf"; path = "../src/assets/resources/fonts/Roboto-Regular.ttf"; sourceTree = "<group>"; };
637B62A22B07B836008D8917 /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "Roboto-Bold.ttf"; path = "../src/assets/resources/fonts/Roboto-Bold.ttf"; sourceTree = "<group>"; };
637B62A32B07BAFB008D8917 /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "Roboto-Bold.ttf"; path = "../src/assets/resources/fonts/Roboto-Bold.ttf"; sourceTree = "<group>"; };
@ -75,6 +77,7 @@ @@ -75,6 +77,7 @@
buildActionMask = 2147483647;
files = (
BE14AC86F1757C0EC41F56B3 /* libPods-Truth.a in Frameworks */,
63120D812B0DE8C900E76BCD /* StoreKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -115,6 +118,7 @@ @@ -115,6 +118,7 @@
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
isa = PBXGroup;
children = (
63120D802B0DE8C900E76BCD /* StoreKit.framework */,
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
7A2B08B3350C2BC99B4477A9 /* libPods-Truth.a */,
28F2047B035775EA39D23C93 /* libPods-Truth-TruthTests.a */,
@ -743,10 +747,7 @@ @@ -743,10 +747,7 @@
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
OTHER_LDFLAGS = "$(inherited) ";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
};
@ -819,10 +820,7 @@ @@ -819,10 +820,7 @@
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
OTHER_LDFLAGS = "$(inherited) ";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;

16
ios/Truth/Info.plist

@ -28,6 +28,20 @@ @@ -28,6 +28,20 @@
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>New Exception Domain</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
<key>New Exception Domain 1</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
@ -36,7 +50,7 @@ @@ -36,7 +50,7 @@
</dict>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string/>
<string></string>
<key>UIAppFonts</key>
<array>
<string>fontello.ttf</string>

43
metro.config.js

@ -1,33 +1,10 @@ @@ -1,33 +1,10 @@
/**
* Metro configuration for React Native
* https://github.com/facebook/react-native
*
* @format
*/
//module.exports = {
// transformer: {
// getTransformOptions: async () => ({
// transform: {
// experimentalImportSupport: false,
// inlineRequires: true,
// },
// }),
// },
//};
const { getDefaultConfig } = require('metro-config');
module.exports = (async() => {
const {
resolver: { sourceExts, assetExts },
} = await getDefaultConfig();
return {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: assetExts.filter(ext => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
},
};
})();
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
}

953
package-lock.json generated

File diff suppressed because it is too large Load Diff

171
package.json

@ -1,86 +1,89 @@ @@ -1,86 +1,89 @@
{
"rnpm": {
"assets": [
"./resources/fonts/"
]
},
"name": "truth",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
},
"dependencies": {
"@react-native-async-storage/async-storage": "^1.17.11",
"@react-native-firebase/app": "^17.0.0",
"@react-native-firebase/firestore": "^17.0.0",
"@react-navigation/native": "^6.0.11",
"@react-navigation/native-stack": "^6.7.0",
"@reduxjs/toolkit": "^1.9.2",
"@shopify/flash-list": "^1.6.3",
"i18next": "^21.8.14",
"i18next-react-native-async-storage": "^1.0.4",
"jet-tools": "^1.3.0",
"link": "^1.5.1",
"lodash": "^4.17.21",
"react": "18.2.0",
"react-i18next": "^11.18.1",
"react-native": "0.72.7",
"react-native-actions-sheet": "*",
"react-native-animated-loader": "^1.0.0",
"react-native-gesture-handler": "^2.5.0",
"react-native-icomoon": "^0.1.1",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-modal": "^13.0.1",
"react-native-safe-area-context": "^4.3.1",
"react-native-screens": "^3.27.0",
"react-native-splash-screen": "^3.3.0",
"react-native-svg": "^12.5.1",
"react-native-svg-transformer": "^1.0.0",
"react-native-vector-icons": "^9.2.0",
"react-redux": "^8.0.5",
"validate.js": "^0.13.1"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@react-native/eslint-config": "^0.72.2",
"@react-native/metro-config": "^0.72.11",
"@tsconfig/react-native": "^3.0.0",
"@types/lodash": "^4.14.201",
"@types/react": "^18.0.24",
"@types/react-native": "^0.66.15",
"@types/react-native-vector-icons": "^6.4.12",
"@types/react-redux": "^7.1.25",
"@types/react-test-renderer": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.7.0",
"@typescript-eslint/parser": "^5.7.0",
"babel-jest": "^29.2.1",
"eslint": "^8.19.0",
"jest": "^29.2.1",
"metro-react-native-babel-preset": "0.76.8",
"prettier": "^2.4.1",
"react-test-renderer": "18.2.0",
"reactotron-react-native": "^5.0.3",
"typescript": "4.8.4"
},
"resolutions": {
"@types/react": "^17"
},
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
}
"rnpm": {
"assets": [
"./resources/fonts/"
]
},
"name": "truth",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"pod": "cd ./ios && pod install && cd ../"
},
"dependencies": {
"@react-native-async-storage/async-storage": "^1.17.11",
"@react-native-firebase/app": "^17.0.0",
"@react-native-firebase/firestore": "^17.0.0",
"@react-navigation/native": "^6.0.11",
"@react-navigation/native-stack": "^6.7.0",
"@reduxjs/toolkit": "^1.9.2",
"@shopify/flash-list": "^1.6.3",
"babel-plugin-module-resolver": "^5.0.0",
"i18next": "^21.8.14",
"i18next-react-native-async-storage": "^1.0.4",
"jet-tools": "^1.3.0",
"link": "^1.5.1",
"lodash": "^4.17.21",
"react": "18.2.0",
"react-i18next": "^11.18.1",
"react-native": "0.72.7",
"react-native-actions-sheet": "*",
"react-native-animated-loader": "^1.0.0",
"react-native-gesture-handler": "^2.5.0",
"react-native-iap": "^12.11.0",
"react-native-icomoon": "^0.1.1",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-modal": "^13.0.1",
"react-native-safe-area-context": "^4.3.1",
"react-native-screens": "^3.27.0",
"react-native-splash-screen": "^3.3.0",
"react-native-svg": "^12.5.1",
"react-native-svg-transformer": "^1.0.0",
"react-native-vector-icons": "^9.2.0",
"react-redux": "^8.0.5",
"validate.js": "^0.13.1"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@react-native/eslint-config": "^0.72.2",
"@react-native/metro-config": "^0.72.11",
"@tsconfig/react-native": "^3.0.0",
"@types/lodash": "^4.14.201",
"@types/react": "^18.0.24",
"@types/react-native": "^0.66.15",
"@types/react-native-vector-icons": "^6.4.12",
"@types/react-redux": "^7.1.25",
"@types/react-test-renderer": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.7.0",
"@typescript-eslint/parser": "^5.7.0",
"babel-jest": "^29.2.1",
"eslint": "^8.19.0",
"jest": "^29.2.1",
"metro-react-native-babel-preset": "0.76.8",
"prettier": "^2.4.1",
"react-test-renderer": "18.2.0",
"reactotron-react-native": "^5.0.3",
"typescript": "4.8.4"
},
"resolutions": {
"@types/react": "^17"
},
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
}
}

1
src/module/common/typing/enums/index.ts

@ -2,3 +2,4 @@ export * from './route-keys.enum' @@ -2,3 +2,4 @@ export * from './route-keys.enum'
export * from './fonts.enum'
export * from './storage-key.enum'
export * from './language.enum'
export * from './products.enum';

5
src/module/common/typing/enums/products.enum.ts

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
export enum ProductsEnum {
All = 'ALL',
Under18 = 'un18',
Crazy = 'Crz',
}

2
src/module/root/screens/loading-screen.tsx

@ -9,6 +9,7 @@ import { @@ -9,6 +9,7 @@ import {
} from '../../common'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { useTranslation } from 'react-i18next'
import { PurchasesService, purchasesService } from '../../settings'
import { fetchPostsAsync } from '../../../store/slices'
export const LoadingScreen: FC = () => {
@ -40,6 +41,7 @@ export const LoadingScreen: FC = () => { @@ -40,6 +41,7 @@ export const LoadingScreen: FC = () => {
if (isOnBoard && language) {
nav.navigate(RouteKey.Packages)
purchasesService.init()
} else if (language && !isOnBoard) {
nav.navigate(RouteKey.Onboarding)
} else if (!language) {

1
src/module/settings/atoms/index.ts

@ -1,2 +1,3 @@ @@ -1,2 +1,3 @@
export * from './selected-language-in-settings.atom'
export * from './switch-notifications.atom'
export * from './purchases.atom'

74
src/module/settings/atoms/purchases.atom.tsx

@ -0,0 +1,74 @@ @@ -0,0 +1,74 @@
import React, { FC } from 'react'
import { StyleSheet, View } from 'react-native'
import { $size, Icon, Txt, colors } from '../../common'
import { TouchableOpacity } from 'react-native-gesture-handler'
interface IProps {
title: string
price: string
iconName: string
hasDiscount: boolean
onPress: () => void
}
export const PurchaseAtom: FC<IProps> = ({
title,
price,
hasDiscount,
iconName,
onPress,
}) => {
const renderDiscountAtom = () => {
return (
<View style={styles.discount}>
<Txt style={styles.discountTxt}>-30%</Txt>
</View>
)
}
return (
<TouchableOpacity style={styles.container} onPress={onPress}>
<View style={styles.row}>
<Icon name={iconName} size={$size(24)} color={colors.purple} />
<Txt mod="lg" color={colors.purple}>
{title}
</Txt>
{hasDiscount && renderDiscountAtom()}
</View>
<Txt mod="lg" style={styles.price}>
{price + ' $'}
</Txt>
</TouchableOpacity>
)
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
height: $size(28),
marginBottom: $size(24),
},
row: {
flexDirection: 'row',
alignItems: 'center',
columnGap: 8,
},
price: {
fontWeight: '600',
color: colors.purple,
},
discount: {
width: $size(49),
borderRadius: 40,
backgroundColor: colors.red,
justifyContent: 'center',
alignItems: 'center',
},
discountTxt: {
fontSize: $size(14),
lineHeight: $size(28),
fontWeight: '900',
},
})

1
src/module/settings/config/index.ts

@ -1,2 +1,3 @@ @@ -1,2 +1,3 @@
export * from './settings.config'
export * from './privacy-text.config'
export * from './purchases.config'

16
src/module/settings/config/purchases.config.ts

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
import { ProductsEnum } from '../../common'
export const purchasesConfig: any = {
[ProductsEnum.Under18]: {
name: 'Open package "Under 18"',
icon: 'ghost',
},
[ProductsEnum.Crazy]: {
name: 'Open package "Crazy"',
icon: 'crazy',
},
[ProductsEnum.All]: {
name: 'Open all packages',
icon: 'all_packages',
},
}

1
src/module/settings/index.ts

@ -3,3 +3,4 @@ export * from './atoms' @@ -3,3 +3,4 @@ export * from './atoms'
export * from './components'
export * from './config'
export * from './validator'
export * from './services'

59
src/module/settings/screens/purchases.screen.tsx

@ -1,21 +1,42 @@ @@ -1,21 +1,42 @@
import React, { FC } from 'react'
import React, { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { StyleSheet, View } from 'react-native'
import { Alert, StyleSheet, TouchableOpacity } from 'react-native'
import {
$size,
ButtonPrimary,
colors,
Header,
Icon,
ProductsEnum,
ScreenLayout,
Txt,
useNav,
} from '../../common'
import { SheetManager } from 'react-native-actions-sheet'
import { purchasesService } from '../services'
import { PurchaseAtom } from '../atoms'
import { useIAP } from 'react-native-iap'
export const PurchasesScreen: FC = () => {
const { t } = useTranslation()
const nav = useNav()
const purchaseProduct = (productId: ProductsEnum) => {
purchasesService.purchaseProduct(productId)
}
const handlePurchaseSuccess = (purchaseResult: string) => {
console.log('Purchase successful:', purchaseResult)
Alert.alert('Purchase Successful', 'Thank you for your purchase!')
}
const handlePurchaseError = (purchaseError: any) => {
console.error('Purchase failed:', purchaseError)
Alert.alert(
'Purchase Failed',
'There was an error processing your purchase.',
)
}
return (
<ScreenLayout
headerComponent={
@ -25,7 +46,30 @@ export const PurchasesScreen: FC = () => { @@ -25,7 +46,30 @@ export const PurchasesScreen: FC = () => {
onPressLeft={() => nav.goBack()}
/>
}>
<View></View>
<>
{purchasesService.products.map(it => {
return (
<PurchaseAtom
key={it.productId}
title={it.name}
price={it.price}
hasDiscount={it.productId === ProductsEnum.All}
iconName={it.icon}
onPress={() => purchaseProduct(it.productId)}
/>
)
})}
<TouchableOpacity style={styles.row}>
<Icon
name="restore"
size={$size(24)}
color={colors.purple}
/>
<Txt mod="lg" color={colors.purple}>
Restore purchases
</Txt>
</TouchableOpacity>
</>
</ScreenLayout>
)
}
@ -40,4 +84,9 @@ const styles = StyleSheet.create({ @@ -40,4 +84,9 @@ const styles = StyleSheet.create({
color: colors.purple,
lineHeight: $size(30),
},
row: {
flexDirection: 'row',
alignItems: 'center',
columnGap: 8,
},
})

12
src/module/settings/screens/settings.screen.tsx

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import React, { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { Share, View } from 'react-native'
import { Share } from 'react-native'
import { SettingsItem } from '../components/settings-item.component'
import { settingsConfig } from '../config/settings.config'
import {
@ -11,9 +11,9 @@ import { @@ -11,9 +11,9 @@ import {
useNav,
} from '../../common'
import { SheetManager } from 'react-native-actions-sheet'
import UALogo from '../../../assets/icons/UKR.svg'
import UAELogo from '../../../assets/icons/UAE.svg'
import ENGLogo from '../../../assets/icons/ENG.svg'
import UA from '../../../assets/icons/UKR.svg'
import UAE from '../../../assets/icons/UAE.svg'
import ENG from '../../../assets/icons/ENG.svg'
export const SettingsScreen: FC = () => {
const { t, i18n } = useTranslation()
@ -21,12 +21,12 @@ export const SettingsScreen: FC = () => { @@ -21,12 +21,12 @@ export const SettingsScreen: FC = () => {
const configActions = [
{
icon: () => <ENGLogo />,
icon: () => <ENG />,
onPress: () => onChangeLanguage(Language.EN),
label: 'English',
},
{
icon: () => <UALogo />,
icon: () => <UA />,
onPress: () => onChangeLanguage(Language.UA),
label: 'Українська',
},

1
src/module/settings/services/index.ts

@ -0,0 +1 @@ @@ -0,0 +1 @@
export * from './purchases.service'

83
src/module/settings/services/purchases.service.ts

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
import {
initConnection,
getProducts,
Product,
requestPurchase,
purchaseUpdatedListener,
getPurchaseHistory,
} from 'react-native-iap'
import { ProductsEnum } from '../../common'
import { purchasesConfig } from '../config'
const ID_PRODUCTS = ['ALL', 'Crz', 'un18']
interface ProductItem {
productId: ProductsEnum
price: string
name: string
icon: string
}
export class PurchasesService {
public products: ProductItem[] = []
public init() {
this.initializeIAP()
this.loadProducts()
}
private async initializeIAP() {
try {
await initConnection()
} catch (error) {
console.error('Failed to initialize IAP:', error)
throw error
}
}
private async loadProducts() {
try {
const products: Product[] = await getProducts({ skus: ID_PRODUCTS })
this.products = this.transformProductsData(products)
const purchaseHistory = await getPurchaseHistory()
console.log('products', this.products)
console.log('purchaseHistory', purchaseHistory)
} catch (error) {
console.error('Error loading products:', error)
throw error
}
}
public async purchaseProduct(productId: ProductsEnum) {
try {
const purchase = await requestPurchase({ sku: productId })
this.purchaseListener()
console.log('Purchase successful:', purchase)
} catch (error) {
console.error('Purchase error:', error)
throw error
}
}
private async purchaseListener() {
purchaseUpdatedListener(purchase => {
console.log('purchaseListener', purchase)
})
}
private transformProductsData = (products: Product[]) => {
return products
.map(product => {
return {
...purchasesConfig[product.productId],
productId: product.productId,
price: product.price,
}
})
.sort()
}
}
export const purchasesService = new PurchasesService()

81
tsconfig.json

@ -1,64 +1,23 @@ @@ -1,64 +1,23 @@
{
"compilerOptions": {
/* Basic Options */
"target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": ["es2017"], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
"jsx": "react-native", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "removeComments": true, /* Do not emit comments to output. */
"noEmit": true, /* Do not emit outputs. */
// "incremental": true, /* Enable incremental compilation */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
"isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"resolveJsonModule": true /* Allows importing modules with a .json extension, which is a common practice in node projects. */
/* Source Map Options */
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
"extends": "@tsconfig/react-native/tsconfig.json",
"compilerOptions": {
"jsx": "react",
"baseUrl": ".",
"paths": { "~*": ["./src/*"] },
"strictNullChecks": false,
"esModuleInterop": true
// "jsx": "react-native" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
},
"include": [
"src",
".eslintrc.js",
"react-native.config.js",
"metro.config.js",
"index.js",
"babel.config.js",
"jest.config.js"
],
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
},
"exclude": [
"node_modules", "babel.config.js", "metro.config.js", "jest.config.js"
]
/* Completeness */
"skipLibCheck": true
}

Loading…
Cancel
Save