diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..3c17ca08f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[*.{js,json}] +indent_size = 2 diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..6f493fc85 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +**/*.js +!/*.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..cad5fcf52 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,19 @@ +{ + "env": { + "es6": true, + "node": true + }, + "extends": ["plugin:prettier/recommended", "plugin:react/recommended"], + "parser": "babel-eslint", + "parserOptions": { + "ecmaVersion": 6 + }, + "plugins": [ + "react", + "react-native" + ], + "rules": { + "comma-dangle": ["error", "always-multiline"], + "react/jsx-handler-names": "error" + } +} diff --git a/.github/workflows/integrate.yml b/.github/workflows/integrate.yml new file mode 100644 index 000000000..1710b40a1 --- /dev/null +++ b/.github/workflows/integrate.yml @@ -0,0 +1,71 @@ +name: Integrate React Native AdMob +on: + schedule: + - cron: 0 12 * * 1 + pull_request: + branches: + - master +jobs: + integrate: + name: Create React Native App and integrate React Native Admob + runs-on: macOS-latest + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + with: + node-version: '10.x' + - name: Install xmlstarlet + run: brew install xmlstarlet + - name: Install xcpretty + run: gem install xcpretty + - name: Configure Xcode + run: sudo xcode-select --switch /Applications/Xcode_11.app + - name: Install React Native CLI + run: sudo npm install -g react-native-cli + - name: Initialize React Native application + run: react-native init ReactNativeAdMobExample + - name: Add react-native-admob + working-directory: ./ReactNativeAdMobExample + run: yarn add react-native-admob@next + - name: Configure Android Project + working-directory: ./ReactNativeAdMobExample + run: | + xml ed -L \ + -s /manifest/application -t elem -n meta-data \ + -i /manifest/application/meta-data -t attr -n android:name -v "com.google.android.gms.ads.APPLICATION_ID" \ + -i /manifest/application/meta-data -t attr -n android:value -v "ca-app-pub-3940256099942544~1458002511" \ + ./android/app/src/main/AndroidManifest.xml + - name: Configure iOS Project + working-directory: ./ReactNativeAdMobExample/ios + run: | + cp ../../Example/ios/ExampleTests/ExampleTests.m ./ReactNativeAdMobExampleTests/ReactNativeAdMobExampleTests.m + sed -i -e '/target '"'"'ReactNativeAdMobExample'"'"' do/a\ + pod '"'"'Google-Mobile-Ads-SDK'"'"' + ' Podfile + /usr/libexec/PlistBuddy -c 'Add :GADApplicationIdentifier string ca-app-pub-3940256099942544~1458002511' \ + ReactNativeAdMobExample/Info.plist + pod install + - name: Update App.js + working-directory: ./ReactNativeAdMobExample + run: cp ../Example/App.js ./App.js + - name: Start packager + working-directory: ./ReactNativeAdMobExample + run: react-native start & + - name: Run iOS + working-directory: ./ReactNativeAdMobExample + run: react-native run-ios --simulator "iPhone 11 Pro" + - name: Take screenshot after delay + run: sleep 30 && xcrun simctl io booted screenshot screenshot.png + - uses: actions/upload-artifact@master + with: + name: screenshot + path: ./screenshot.png + - name: Run iOS tests + working-directory: ./ReactNativeAdMobExample/ios + run: | + set -o pipefail && xcodebuild \ + -workspace ReactNativeAdMobExample.xcworkspace \ + -scheme ReactNativeAdMobExample \ + -sdk iphonesimulator \ + -destination 'platform=iOS Simulator,name=iPhone 11 Pro' \ + test | xcpretty diff --git a/.gitignore b/.gitignore index 98f5fbc09..07ca25ac0 100644 --- a/.gitignore +++ b/.gitignore @@ -47,7 +47,7 @@ Carthage/Build # fastlane # -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the # screenshots whenever they are needed. # For more information about the recommended setup visit: # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md @@ -70,3 +70,4 @@ Example/android/app/app.iml Example/.buckconfig Example/.vscode/ Example/android/app/BUCK +/yarn.lock diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..304ccc793 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "trailingComma": "es5", + "proseWrap": "always", + "arrowParens": "always" +} diff --git a/.watchmanconfig b/.watchmanconfig new file mode 100644 index 000000000..4e7a8d43f --- /dev/null +++ b/.watchmanconfig @@ -0,0 +1,3 @@ +{ + "ignore_dirs": ["Example", "node_modules"] +} diff --git a/Example/.eslintrc.js b/Example/.eslintrc.js new file mode 100644 index 000000000..40c6dcd05 --- /dev/null +++ b/Example/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: '@react-native-community', +}; diff --git a/Example/.flowconfig b/Example/.flowconfig index e28e2f5bd..1319ea127 100644 --- a/Example/.flowconfig +++ b/Example/.flowconfig @@ -1,41 +1,99 @@ [ignore] +; We fork some components by platform +.*/*[.]android.js -# We fork some components by platform. -.*/*.android.js +; Ignore "BUCK" generated dirs +/\.buckd/ -# Ignore templates with `@flow` in header -.*/local-cli/generator.* +; Ignore unexpected extra "@providesModule" +.*/node_modules/.*/node_modules/fbjs/.* -# Ignore malformed json -.*/node_modules/y18n/test/.*\.json +; Ignore duplicate module providers +; For RN Apps installed via npm, "Libraries" folder is inside +; "node_modules/react-native" but in the source repo it is in the root +node_modules/react-native/Libraries/react-native/React.js + +; Ignore polyfills +node_modules/react-native/Libraries/polyfills/.* + +; These should not be required directly +; require from fbjs/lib instead: require('fbjs/lib/warning') +node_modules/warning/.* + +; Flow doesn't support platforms +.*/Libraries/Utilities/HMRLoadingView.js + +[untyped] +.*/node_modules/@react-native-community/cli/.*/.* [include] [libs] node_modules/react-native/Libraries/react-native/react-native-interface.js -node_modules/react-native/flow -flow/ +node_modules/react-native/flow/ [options] -module.system=haste +emoji=true -esproposal.class_static_fields=enable -esproposal.class_instance_fields=enable +esproposal.optional_chaining=enable +esproposal.nullish_coalescing=enable -experimental.strict_type_args=true +module.file_ext=.js +module.file_ext=.json +module.file_ext=.ios.js + +module.system=haste +module.system.haste.use_name_reducers=true +# get basename +module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1' +# strip .js or .js.flow suffix +module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1' +# strip .ios suffix +module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1' +module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1' +module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1' +module.system.haste.paths.blacklist=.*/__tests__/.* +module.system.haste.paths.blacklist=.*/__mocks__/.* +module.system.haste.paths.whitelist=/node_modules/react-native/Libraries/.* +module.system.haste.paths.whitelist=/node_modules/react-native/RNTester/.* +module.system.haste.paths.whitelist=/node_modules/react-native/IntegrationTests/.* +module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/react-native/react-native-implementation.js +module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Animated/src/polyfills/.* munge_underscores=true -module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' suppress_type=$FlowIssue suppress_type=$FlowFixMe -suppress_type=$FixMe +suppress_type=$FlowFixMeProps +suppress_type=$FlowFixMeState + +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError + +[lints] +sketchy-null-number=warn +sketchy-null-mixed=warn +sketchy-number=warn +untyped-type-import=warn +nonstrict-import=warn +deprecated-type=warn +unsafe-getters-setters=warn +inexact-spread=warn +unnecessary-invariant=warn +signature-verification-failure=warn +deprecated-utility=error -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-7]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-7]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ -suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy +[strict] +deprecated-type +nonstrict-import +sketchy-null +unclear-type +unsafe-getters-setters +untyped-import +untyped-type-import [version] -^0.27.0 +^0.98.0 diff --git a/Example/.gitattributes b/Example/.gitattributes new file mode 100644 index 000000000..d42ff1835 --- /dev/null +++ b/Example/.gitattributes @@ -0,0 +1 @@ +*.pbxproj -text diff --git a/Example/.gitignore b/Example/.gitignore index e0117914c..828cc8846 100644 --- a/Example/.gitignore +++ b/Example/.gitignore @@ -21,22 +21,39 @@ DerivedData *.ipa *.xcuserstate project.xcworkspace -ios/GoogleMobileAds.framework/ -# Android/IJ +# Android/IntelliJ # -*.iml +build/ .idea .gradle local.properties +*.iml # node.js # node_modules/ npm-debug.log +yarn-error.log # BUCK buck-out/ \.buckd/ -android/app/libs -android/keystores/debug.keystore +*.keystore + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/ + +*/fastlane/report.xml +*/fastlane/Preview.html +*/fastlane/screenshots + +# Bundle artifact +*.jsbundle + +# CocoaPods +/ios/Pods/ diff --git a/Example/.prettierrc.js b/Example/.prettierrc.js new file mode 100644 index 000000000..355e7d1e4 --- /dev/null +++ b/Example/.prettierrc.js @@ -0,0 +1,6 @@ +module.exports = { + bracketSpacing: true, + jsxBracketSameLine: true, + singleQuote: true, + trailingComma: 'all', +}; diff --git a/Example/.watchmanconfig b/Example/.watchmanconfig index 9e26dfeeb..0967ef424 100644 --- a/Example/.watchmanconfig +++ b/Example/.watchmanconfig @@ -1 +1 @@ -{} \ No newline at end of file +{} diff --git a/Example/App.js b/Example/App.js new file mode 100644 index 000000000..c3475a03c --- /dev/null +++ b/Example/App.js @@ -0,0 +1,224 @@ +import React, { Component } from 'react'; +import { + Button, + Platform, + ScrollView, + StyleSheet, + Text, + View, +} from 'react-native'; +import { + AdMobBanner, + AdMobInterstitial, + AdMobRewarded, + PublisherBanner, +} from 'react-native-admob'; + +const BannerExample = ({ style, title, children, ...props }) => ( + + {title} + {children} + +); + +const bannerWidths = [200, 250, 320]; + +export default class Example extends Component { + constructor() { + super(); + this.state = { + fluidSizeIndex: 0, + }; + } + + componentDidMount() { + AdMobRewarded.setTestDevices([AdMobRewarded.simulatorId]); + AdMobRewarded.setAdUnitID('ca-app-pub-3940256099942544/5224354917'); + + AdMobRewarded.addEventListener('rewarded', reward => + console.log('AdMobRewarded => rewarded', reward), + ); + AdMobRewarded.addEventListener('adLoaded', () => + console.log('AdMobRewarded => adLoaded'), + ); + AdMobRewarded.addEventListener('adFailedToLoad', error => + console.warn(error), + ); + AdMobRewarded.addEventListener('adOpened', () => + console.log('AdMobRewarded => adOpened'), + ); + AdMobRewarded.addEventListener('videoStarted', () => + console.log('AdMobRewarded => videoStarted'), + ); + AdMobRewarded.addEventListener('adClosed', () => { + console.log('AdMobRewarded => adClosed'); + AdMobRewarded.requestAd().catch(error => console.warn(error)); + }); + AdMobRewarded.addEventListener('adLeftApplication', () => + console.log('AdMobRewarded => adLeftApplication'), + ); + + AdMobRewarded.requestAd().catch(error => console.warn(error)); + + AdMobInterstitial.setTestDevices([AdMobInterstitial.simulatorId]); + AdMobInterstitial.setAdUnitID('ca-app-pub-3940256099942544/1033173712'); + + AdMobInterstitial.addEventListener('adLoaded', () => + console.log('AdMobInterstitial adLoaded'), + ); + AdMobInterstitial.addEventListener('adFailedToLoad', error => + console.warn(error), + ); + AdMobInterstitial.addEventListener('adOpened', () => + console.log('AdMobInterstitial => adOpened'), + ); + AdMobInterstitial.addEventListener('adClosed', () => { + console.log('AdMobInterstitial => adClosed'); + AdMobInterstitial.requestAd().catch(error => console.warn(error)); + }); + AdMobInterstitial.addEventListener('adLeftApplication', () => + console.log('AdMobInterstitial => adLeftApplication'), + ); + + AdMobInterstitial.requestAd().catch(error => console.warn(error)); + } + + componentWillUnmount() { + AdMobRewarded.removeAllListeners(); + AdMobInterstitial.removeAllListeners(); + } + + showRewarded() { + AdMobRewarded.showAd().catch(error => console.warn(error)); + } + + showInterstitial() { + AdMobInterstitial.showAd().catch(error => console.warn(error)); + } + + render() { + return ( + + + + (this._basicExample = el)} + /> +