Vlad Narizhnyi
1 year ago
20 changed files with 1348 additions and 220 deletions
@ -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', |
||||
], |
||||
}, |
||||
], |
||||
], |
||||
} |
||||
|
@ -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, |
||||
}, |
||||
}), |
||||
}, |
||||
} |
||||
|
@ -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" |
||||
] |
||||
} |
||||
} |
||||
|
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
export enum ProductsEnum { |
||||
All = 'ALL', |
||||
Under18 = 'un18', |
||||
Crazy = 'Crz', |
||||
} |
@ -1,2 +1,3 @@
@@ -1,2 +1,3 @@
|
||||
export * from './selected-language-in-settings.atom' |
||||
export * from './switch-notifications.atom' |
||||
export * from './purchases.atom' |
||||
|
@ -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,2 +1,3 @@
@@ -1,2 +1,3 @@
|
||||
export * from './settings.config' |
||||
export * from './privacy-text.config' |
||||
export * from './purchases.config' |
||||
|
@ -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', |
||||
}, |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './purchases.service' |
@ -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() |
@ -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…
Reference in new issue