chore: init react-native-rate-app
This commit is contained in:
parent
6561af6522
commit
6f54ce15fe
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1 @@
|
||||
* @huextrat
|
||||
15
.github/FUNDING.yml
vendored
Normal file
15
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [huextrat]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
thanks_dev: # Replace with a single thanks.dev username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
4
.github/actions/setup/action.yml
vendored
4
.github/actions/setup/action.yml
vendored
@ -5,13 +5,13 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
- name: Cache dependencies
|
||||
id: yarn-cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
|
||||
205
.github/workflows/ci.yml
vendored
205
.github/workflows/ci.yml
vendored
@ -15,34 +15,31 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Lint files
|
||||
- name: Biome check
|
||||
run: yarn lint
|
||||
|
||||
- name: Typecheck files
|
||||
run: yarn typecheck
|
||||
# test:
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v4
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
# - name: Setup
|
||||
# uses: ./.github/actions/setup
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Run unit tests
|
||||
run: yarn test --maxWorkers=2 --coverage
|
||||
# - name: Run unit tests
|
||||
# run: yarn test --maxWorkers=2 --coverage
|
||||
|
||||
build-library:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
@ -50,108 +47,108 @@ jobs:
|
||||
- name: Build package
|
||||
run: yarn prepare
|
||||
|
||||
build-android:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TURBO_CACHE_DIR: .turbo/android
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
# build-android:
|
||||
# runs-on: ubuntu-latest
|
||||
# env:
|
||||
# TURBO_CACHE_DIR: .turbo/android
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v4
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
# - name: Setup
|
||||
# uses: ./.github/actions/setup
|
||||
|
||||
- name: Cache turborepo for Android
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ env.TURBO_CACHE_DIR }}
|
||||
key: ${{ runner.os }}-turborepo-android-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-turborepo-android-
|
||||
# - name: Cache turborepo for Android
|
||||
# uses: actions/cache@v4
|
||||
# with:
|
||||
# path: ${{ env.TURBO_CACHE_DIR }}
|
||||
# key: ${{ runner.os }}-turborepo-android-${{ hashFiles('yarn.lock') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-turborepo-android-
|
||||
|
||||
- name: Check turborepo cache for Android
|
||||
run: |
|
||||
TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status")
|
||||
# - name: Check turborepo cache for Android
|
||||
# run: |
|
||||
# TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status")
|
||||
|
||||
if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
|
||||
echo "turbo_cache_hit=1" >> $GITHUB_ENV
|
||||
fi
|
||||
# if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
|
||||
# echo "turbo_cache_hit=1" >> $GITHUB_ENV
|
||||
# fi
|
||||
|
||||
- name: Install JDK
|
||||
if: env.turbo_cache_hit != 1
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
# - name: Install JDK
|
||||
# if: env.turbo_cache_hit != 1
|
||||
# uses: actions/setup-java@v4
|
||||
# with:
|
||||
# distribution: 'zulu'
|
||||
# java-version: '17'
|
||||
|
||||
- name: Finalize Android SDK
|
||||
if: env.turbo_cache_hit != 1
|
||||
run: |
|
||||
/bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null"
|
||||
# - name: Finalize Android SDK
|
||||
# if: env.turbo_cache_hit != 1
|
||||
# run: |
|
||||
# /bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null"
|
||||
|
||||
- name: Cache Gradle
|
||||
if: env.turbo_cache_hit != 1
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/wrapper
|
||||
~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
# - name: Cache Gradle
|
||||
# if: env.turbo_cache_hit != 1
|
||||
# uses: actions/cache@v4
|
||||
# with:
|
||||
# path: |
|
||||
# ~/.gradle/wrapper
|
||||
# ~/.gradle/caches
|
||||
# key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-gradle-
|
||||
|
||||
- name: Build example for Android
|
||||
env:
|
||||
JAVA_OPTS: "-XX:MaxHeapSize=6g"
|
||||
run: |
|
||||
yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}"
|
||||
# - name: Build example for Android
|
||||
# env:
|
||||
# JAVA_OPTS: "-XX:MaxHeapSize=6g"
|
||||
# run: |
|
||||
# yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}"
|
||||
|
||||
build-ios:
|
||||
runs-on: macos-14
|
||||
env:
|
||||
TURBO_CACHE_DIR: .turbo/ios
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
# build-ios:
|
||||
# runs-on: macos-14
|
||||
# env:
|
||||
# TURBO_CACHE_DIR: .turbo/ios
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v4
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
# - name: Setup
|
||||
# uses: ./.github/actions/setup
|
||||
|
||||
- name: Cache turborepo for iOS
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ env.TURBO_CACHE_DIR }}
|
||||
key: ${{ runner.os }}-turborepo-ios-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-turborepo-ios-
|
||||
# - name: Cache turborepo for iOS
|
||||
# uses: actions/cache@v4
|
||||
# with:
|
||||
# path: ${{ env.TURBO_CACHE_DIR }}
|
||||
# key: ${{ runner.os }}-turborepo-ios-${{ hashFiles('yarn.lock') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-turborepo-ios-
|
||||
|
||||
- name: Check turborepo cache for iOS
|
||||
run: |
|
||||
TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status")
|
||||
# - name: Check turborepo cache for iOS
|
||||
# run: |
|
||||
# TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status")
|
||||
|
||||
if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
|
||||
echo "turbo_cache_hit=1" >> $GITHUB_ENV
|
||||
fi
|
||||
# if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
|
||||
# echo "turbo_cache_hit=1" >> $GITHUB_ENV
|
||||
# fi
|
||||
|
||||
- name: Cache cocoapods
|
||||
if: env.turbo_cache_hit != 1
|
||||
id: cocoapods-cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
**/ios/Pods
|
||||
key: ${{ runner.os }}-cocoapods-${{ hashFiles('example/ios/Podfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cocoapods-
|
||||
# - name: Cache cocoapods
|
||||
# if: env.turbo_cache_hit != 1
|
||||
# id: cocoapods-cache
|
||||
# uses: actions/cache@v4
|
||||
# with:
|
||||
# path: |
|
||||
# **/ios/Pods
|
||||
# key: ${{ runner.os }}-cocoapods-${{ hashFiles('example/ios/Podfile.lock') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-cocoapods-
|
||||
|
||||
- name: Install cocoapods
|
||||
if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd example/ios
|
||||
pod install
|
||||
env:
|
||||
NO_FLIPPER: 1
|
||||
# - name: Install cocoapods
|
||||
# if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true'
|
||||
# run: |
|
||||
# cd example/ios
|
||||
# pod install
|
||||
# env:
|
||||
# NO_FLIPPER: 1
|
||||
|
||||
- name: Build example for iOS
|
||||
run: |
|
||||
yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}"
|
||||
# - name: Build example for iOS
|
||||
# run: |
|
||||
# yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}"
|
||||
|
||||
32
.github/workflows/release.yml
vendored
Normal file
32
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: Release
|
||||
|
||||
concurrency:
|
||||
group: release
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release 🚀
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Prepare
|
||||
run: yarn prepare
|
||||
|
||||
- name: Release
|
||||
run: yarn release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
1
.husky/commit-msg
Normal file
1
.husky/commit-msg
Normal file
@ -0,0 +1 @@
|
||||
yarn commitlint --edit "$1"
|
||||
2
.husky/pre-commit
Normal file
2
.husky/pre-commit
Normal file
@ -0,0 +1,2 @@
|
||||
yarn lint
|
||||
yarn format:check
|
||||
70
.releaserc.json
Normal file
70
.releaserc.json
Normal file
@ -0,0 +1,70 @@
|
||||
{
|
||||
"branches": [
|
||||
"main"
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"@semantic-release/commit-analyzer",
|
||||
{
|
||||
"preset": "angular",
|
||||
"releaseRules": [
|
||||
{
|
||||
"type": "docs",
|
||||
"scope": "README",
|
||||
"release": "patch"
|
||||
},
|
||||
{
|
||||
"type": "fix",
|
||||
"release": "patch"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"release": "patch"
|
||||
},
|
||||
{
|
||||
"type": "refactor",
|
||||
"release": "patch"
|
||||
},
|
||||
{
|
||||
"type": "feat",
|
||||
"release": "patch"
|
||||
},
|
||||
{
|
||||
"type": "feat",
|
||||
"scope": "minor",
|
||||
"release": "minor"
|
||||
},
|
||||
{
|
||||
"type": "feat",
|
||||
"scope": "major",
|
||||
"release": "major"
|
||||
}
|
||||
],
|
||||
"parserOpts": {
|
||||
"noteKeywords": [
|
||||
"BREAKING CHANGE",
|
||||
"BREAKING CHANGES"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"@semantic-release/release-notes-generator",
|
||||
[
|
||||
"@semantic-release/changelog",
|
||||
{
|
||||
"changelogTitle": "# Changelog"
|
||||
}
|
||||
],
|
||||
"@semantic-release/npm",
|
||||
[
|
||||
"@semantic-release/git",
|
||||
{
|
||||
"assets": [
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"message": "chore(release): ${nextRelease.version} [skip ci]"
|
||||
}
|
||||
],
|
||||
"@semantic-release/github"
|
||||
]
|
||||
}
|
||||
541
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
541
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
874
.yarn/releases/yarn-3.6.1.cjs
vendored
874
.yarn/releases/yarn-3.6.1.cjs
vendored
File diff suppressed because one or more lines are too long
925
.yarn/releases/yarn-4.5.0.cjs
vendored
Executable file
925
.yarn/releases/yarn-4.5.0.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
13
.yarnrc.yml
13
.yarnrc.yml
@ -1,10 +1,9 @@
|
||||
nodeLinker: node-modules
|
||||
compressionLevel: mixed
|
||||
|
||||
enableGlobalCache: false
|
||||
|
||||
nmHoistingLimits: workspaces
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||
spec: "@yarnpkg/plugin-workspace-tools"
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.6.1.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.5.0.cjs
|
||||
|
||||
5
CHANGELOG.md
Normal file
5
CHANGELOG.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
# 0.0.1 (2024-09-16)
|
||||
|
||||
Init react-native-app-rate project
|
||||
@ -78,17 +78,16 @@ Running "RateAppExample" with {"fabric":true,"initialProps":{"concurrentRoot":tr
|
||||
|
||||
Note the `"fabric":true` and `"concurrentRoot":true` properties.
|
||||
|
||||
Make sure your code passes TypeScript and ESLint. Run the following to verify:
|
||||
Make sure your code passes Biome. Run the following to verify:
|
||||
|
||||
```sh
|
||||
yarn typecheck
|
||||
yarn lint
|
||||
```
|
||||
|
||||
To fix formatting errors, run the following:
|
||||
|
||||
```sh
|
||||
yarn lint --fix
|
||||
yarn lint:fix
|
||||
```
|
||||
|
||||
Remember to add tests for your change if possible. Run the unit tests by:
|
||||
@ -112,29 +111,17 @@ Our pre-commit hooks verify that your commit message matches this format when co
|
||||
|
||||
### Linting and tests
|
||||
|
||||
[ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/)
|
||||
|
||||
We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.
|
||||
We use [TypeScript](https://www.typescriptlang.org/) for type checking, [Biome](https://biomejs.dev/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.
|
||||
|
||||
Our pre-commit hooks verify that the linter and tests pass when committing.
|
||||
|
||||
### Publishing to npm
|
||||
|
||||
We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc.
|
||||
|
||||
To publish new versions, run the following:
|
||||
|
||||
```sh
|
||||
yarn release
|
||||
```
|
||||
|
||||
### Scripts
|
||||
|
||||
The `package.json` file contains various scripts for common tasks:
|
||||
|
||||
- `yarn`: setup project by installing dependencies.
|
||||
- `yarn typecheck`: type-check files with TypeScript.
|
||||
- `yarn lint`: lint files with ESLint.
|
||||
- `yarn lint`: lint files with Biome.
|
||||
- `yarn lint:fix`: lint fix files with Biome.
|
||||
- `yarn test`: run unit tests with Jest.
|
||||
- `yarn example start`: start the Metro server for the example app.
|
||||
- `yarn example android`: run the example app on Android.
|
||||
|
||||
119
README.md
119
README.md
@ -1,33 +1,130 @@
|
||||
# react-native-rate-app
|
||||
# React Native Rate App
|
||||
|
||||
In-App Rating on Android & iOS
|
||||
<p align="center">
|
||||
A powerful and easy-to-use library for implementing in-app ratings in React Native applications.
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/huextrat/react-native-rate-app/blob/main/LICENSE">
|
||||
<img alt="License" src="https://img.shields.io/badge/license-MIT-blue.svg" />
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/react-native-rate-app">
|
||||
<img alt="npm version" src="https://img.shields.io/npm/v/react-native-rate-app.svg" />
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/react-native-rate-app">
|
||||
<img alt="npm downloads" src="https://img.shields.io/npm/dm/react-native-rate-app.svg" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Features
|
||||
|
||||
- 🚀 Easy integration with React Native projects
|
||||
- 🔄 Cross-platform support (iOS and Android)
|
||||
- 📱 Supports Android 21+ and iOS 14+
|
||||
- 🏗️ Supports the new architecture for React Native
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
yarn add react-native-rate-app
|
||||
```
|
||||
or
|
||||
```sh
|
||||
npm install react-native-rate-app
|
||||
```
|
||||
|
||||
## Usage
|
||||
## iOS Setup
|
||||
|
||||
To use the in-app review functionality on iOS, you need to add the `StoreKit` framework to your project and update your `Info.plist` file.
|
||||
|
||||
```js
|
||||
import { multiply } from 'react-native-rate-app';
|
||||
### Adding the StoreKit Framework
|
||||
|
||||
// ...
|
||||
1. Open your project in Xcode.
|
||||
2. Select your project in the Project Navigator.
|
||||
3. Select your app target.
|
||||
4. Go to the "Build Phases" tab.
|
||||
5. Expand the "Link Binary With Libraries" section.
|
||||
6. Click the "+" button and add `StoreKit.framework`.
|
||||
|
||||
const result = await multiply(3, 7);
|
||||
### Updating Info.plist
|
||||
|
||||
To allow your app to open the App Store and handle the in-app review functionality, you need to add the `LSApplicationQueriesSchemes` key to your `Info.plist` file.
|
||||
|
||||
1. Open your `Info.plist` file.
|
||||
2. Add `itms-apps` string
|
||||
|
||||
Example:
|
||||
```xml
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>itms-apps</string>
|
||||
</array>
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
This library will throw an error if something goes wrong during the execution of its methods. Make sure to handle these errors appropriately in your application.
|
||||
|
||||
### `RateApp.requestReview()`
|
||||
|
||||
Requests a review from the user using the native in-app review dialog.
|
||||
|
||||
```javascript
|
||||
const result = await RateApp.requestReview();
|
||||
console.log(result); // true if successful, false otherwise
|
||||
```
|
||||
|
||||
### Important Notes
|
||||
|
||||
- **Quotas**: In-app reviews are subject to rate limits set by the operating system. This means the review dialog might not always appear to the user, depending on how often it has been requested previously. Note that `requestReview` will return `true` even if the rate limits have been reached. For more information, please refer to the official documentation from Apple and Google.
|
||||
- **Development Mode**: The in-app review dialog is always displayed in development mode, regardless of rate limits. This is useful for testing purposes.
|
||||
- **Best Practices**: According to operating system guidelines, it is recommended to request a review during a natural flow in your app, rather than from a button. For example, you might request a review after a user has completed a task or achieved a milestone within your app.
|
||||
|
||||
[Apple Documentation](https://developer.apple.com/documentation/storekit/skstorereviewcontroller/3566727-requestreview#discussion)<br>
|
||||
[Google Documentation](https://developer.android.com/guide/playcore/in-app-review)
|
||||
|
||||
### `RateApp.openStoreForReview(options)`
|
||||
|
||||
Opens the app store page for the app, allowing the user to leave a review.
|
||||
|
||||
```javascript
|
||||
const result = await RateApp.openStoreForReview({
|
||||
iOSAppId: "your-ios-app-id", // Required on iOS, macOS
|
||||
androidPackageName: "your.android.package.name", // Required on Android
|
||||
androidMarket: AndroidMarket.GOOGLE, // Optional, defaults to GOOGLE
|
||||
});
|
||||
console.log(result); // true if successful, false otherwise
|
||||
```
|
||||
|
||||
### Supported Android Markets
|
||||
|
||||
The `androidMarket` option in `RateApp.openStoreForReview` supports the following markets:
|
||||
|
||||
- `AndroidMarket.GOOGLE`: Google Play Store
|
||||
- `AndroidMarket.AMAZON`: Amazon Appstore
|
||||
- `AndroidMarket.SAMSUNG`: Samsung Galaxy Store
|
||||
- `AndroidMarket.HUAWEI`: Huawei AppGallery
|
||||
|
||||
### Updating Info.plist
|
||||
|
||||
You need to add the `LSApplicationQueriesSchemes` key to your `Info.plist` file to allow your app to open the App Store.
|
||||
|
||||
1. Open your `Info.plist` file.
|
||||
2. Add the following entry:
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
|
||||
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for more details.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
---
|
||||
## Support
|
||||
|
||||
Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
|
||||
If you like this project, please consider supporting it by giving it a ⭐️ on GitHub!
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
- [create-react-native-library](https://github.com/callstack/react-native-builder-bob) for the initial project setup
|
||||
@ -14,11 +14,6 @@ buildscript {
|
||||
}
|
||||
}
|
||||
|
||||
def reactNativeArchitectures() {
|
||||
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
||||
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
||||
}
|
||||
|
||||
def isNewArchitectureEnabled() {
|
||||
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
||||
}
|
||||
@ -38,24 +33,10 @@ def getExtOrIntegerDefault(name) {
|
||||
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["RateApp_" + name]).toInteger()
|
||||
}
|
||||
|
||||
def supportsNamespace() {
|
||||
def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
|
||||
def major = parsed[0].toInteger()
|
||||
def minor = parsed[1].toInteger()
|
||||
|
||||
// Namespace support was added in 7.3.0
|
||||
return (major == 7 && minor >= 3) || major >= 8
|
||||
}
|
||||
|
||||
android {
|
||||
if (supportsNamespace()) {
|
||||
def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
|
||||
if (agpVersion.tokenize('.')[0].toInteger() >= 7) {
|
||||
namespace "com.rateapp"
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile "src/main/AndroidManifestNew.xml"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
||||
@ -63,8 +44,6 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
||||
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
||||
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
||||
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
@ -89,12 +68,7 @@ android {
|
||||
sourceSets {
|
||||
main {
|
||||
if (isNewArchitectureEnabled()) {
|
||||
java.srcDirs += [
|
||||
"src/newarch",
|
||||
// Codegen specs
|
||||
"generated/java",
|
||||
"generated/jni"
|
||||
]
|
||||
java.srcDirs += ["src/newarch"]
|
||||
} else {
|
||||
java.srcDirs += ["src/oldarch"]
|
||||
}
|
||||
@ -110,17 +84,8 @@ repositories {
|
||||
def kotlin_version = getExtOrDefault("kotlinVersion")
|
||||
|
||||
dependencies {
|
||||
// For < 0.71, this will be from the local maven repo
|
||||
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation "com.facebook.react:react-native:+"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
}
|
||||
|
||||
if (isNewArchitectureEnabled()) {
|
||||
react {
|
||||
jsRootDir = file("../src/")
|
||||
libraryName = "RateApp"
|
||||
codegenJavaPackageName = "com.rateapp"
|
||||
}
|
||||
implementation "com.google.android.play:review:2.0.1"
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
RateApp_kotlinVersion=1.7.0
|
||||
RateApp_kotlinVersion=1.9.24
|
||||
RateApp_minSdkVersion=21
|
||||
RateApp_targetSdkVersion=31
|
||||
RateApp_compileSdkVersion=31
|
||||
RateApp_ndkversion=21.4.7075529
|
||||
RateApp_targetSdkVersion=34
|
||||
RateApp_compileSdkVersion=34
|
||||
RateApp_ndkversion=26.1.10909125
|
||||
|
||||
@ -1,3 +1 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.rateapp">
|
||||
</manifest>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"/>
|
||||
|
||||
@ -4,6 +4,9 @@ import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.bridge.ReactMethod
|
||||
import com.facebook.react.bridge.Promise
|
||||
|
||||
import com.google.android.play.core.review.ReviewManager
|
||||
import com.google.android.play.core.review.ReviewManagerFactory
|
||||
|
||||
class RateAppModule internal constructor(context: ReactApplicationContext) :
|
||||
RateAppSpec(context) {
|
||||
|
||||
@ -11,11 +14,30 @@ class RateAppModule internal constructor(context: ReactApplicationContext) :
|
||||
return NAME
|
||||
}
|
||||
|
||||
// Example method
|
||||
// See https://reactnative.dev/docs/native-modules-android
|
||||
@ReactMethod
|
||||
override fun multiply(a: Double, b: Double, promise: Promise) {
|
||||
promise.resolve(a * b)
|
||||
override fun requestReview(promise: Promise) {
|
||||
val manager: ReviewManager = ReviewManagerFactory.create(reactApplicationContext)
|
||||
val request = manager.requestReviewFlow()
|
||||
request.addOnCompleteListener { task ->
|
||||
if (task.isSuccessful) {
|
||||
val reviewInfo = task.result
|
||||
reviewInfo?.let {
|
||||
val activity = currentActivity
|
||||
if (activity != null) {
|
||||
val flow = manager.launchReviewFlow(activity, it)
|
||||
flow.addOnCompleteListener { result ->
|
||||
if (result.isSuccessful) {
|
||||
promise.resolve(true)
|
||||
} else {
|
||||
promise.reject("REVIEW_FLOW_FAILED", "Review flow failed to complete")
|
||||
}
|
||||
}
|
||||
}
|
||||
} ?: promise.reject("REVIEW_INFO_NULL", "Review info is null")
|
||||
} else {
|
||||
promise.reject("REQUEST_REVIEW_FLOW_FAILED", "Request review flow failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@ -19,7 +19,6 @@ class RateAppPackage : TurboReactPackage() {
|
||||
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
||||
return ReactModuleInfoProvider {
|
||||
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
|
||||
val isTurboModule: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
|
||||
moduleInfos[RateAppModule.NAME] = ReactModuleInfo(
|
||||
RateAppModule.NAME,
|
||||
RateAppModule.NAME,
|
||||
@ -27,7 +26,7 @@ class RateAppPackage : TurboReactPackage() {
|
||||
false, // needsEagerInit
|
||||
true, // hasConstants
|
||||
false, // isCxxModule
|
||||
isTurboModule // isTurboModule
|
||||
true // isTurboModule
|
||||
)
|
||||
moduleInfos
|
||||
}
|
||||
|
||||
@ -7,5 +7,5 @@ import com.facebook.react.bridge.Promise
|
||||
abstract class RateAppSpec internal constructor(context: ReactApplicationContext) :
|
||||
ReactContextBaseJavaModule(context) {
|
||||
|
||||
abstract fun multiply(a: Double, b: Double, promise: Promise)
|
||||
abstract fun requestReview(promise: Promise)
|
||||
}
|
||||
|
||||
34
biome.json
Normal file
34
biome.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.1/schema.json",
|
||||
"vcs": {
|
||||
"enabled": false,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": false
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false,
|
||||
"ignore": []
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"formatWithErrors": false,
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2,
|
||||
"lineEnding": "lf",
|
||||
"lineWidth": 80
|
||||
},
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double"
|
||||
}
|
||||
}
|
||||
}
|
||||
1
commitlint.config.js
Normal file
1
commitlint.config.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = { extends: ['@commitlint/config-conventional'] };
|
||||
112
example/Gemfile.lock
Normal file
112
example/Gemfile.lock
Normal file
@ -0,0 +1,112 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.7)
|
||||
base64
|
||||
nkf
|
||||
rexml
|
||||
activesupport (7.1.4)
|
||||
base64
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
connection_pool (>= 2.2.5)
|
||||
drb
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
mutex_m
|
||||
tzinfo (~> 2.0)
|
||||
addressable (2.8.7)
|
||||
public_suffix (>= 2.0.2, < 7.0)
|
||||
algoliasearch (1.27.5)
|
||||
httpclient (~> 2.8, >= 2.8.3)
|
||||
json (>= 1.5.1)
|
||||
atomos (0.1.3)
|
||||
base64 (0.2.0)
|
||||
bigdecimal (3.1.8)
|
||||
claide (1.1.0)
|
||||
cocoapods (1.15.2)
|
||||
addressable (~> 2.8)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.15.2)
|
||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||
cocoapods-downloader (>= 2.1, < 3.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
cocoapods-search (>= 1.0.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.6.0, < 2.0)
|
||||
cocoapods-try (>= 1.1.0, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
escape (~> 0.0.4)
|
||||
fourflusher (>= 2.3.0, < 3.0)
|
||||
gh_inspector (~> 1.0)
|
||||
molinillo (~> 0.8.0)
|
||||
nap (~> 1.0)
|
||||
ruby-macho (>= 2.3.0, < 3.0)
|
||||
xcodeproj (>= 1.23.0, < 2.0)
|
||||
cocoapods-core (1.15.2)
|
||||
activesupport (>= 5.0, < 8)
|
||||
addressable (~> 2.8)
|
||||
algoliasearch (~> 1.0)
|
||||
concurrent-ruby (~> 1.1)
|
||||
fuzzy_match (~> 2.0.4)
|
||||
nap (~> 1.0)
|
||||
netrc (~> 0.11)
|
||||
public_suffix (~> 4.0)
|
||||
typhoeus (~> 1.0)
|
||||
cocoapods-deintegrate (1.0.5)
|
||||
cocoapods-downloader (2.1)
|
||||
cocoapods-plugins (1.0.0)
|
||||
nap
|
||||
cocoapods-search (1.0.1)
|
||||
cocoapods-trunk (1.6.0)
|
||||
nap (>= 0.8, < 2.0)
|
||||
netrc (~> 0.11)
|
||||
cocoapods-try (1.2.0)
|
||||
colored2 (3.1.2)
|
||||
concurrent-ruby (1.3.4)
|
||||
connection_pool (2.4.1)
|
||||
drb (2.2.1)
|
||||
escape (0.0.4)
|
||||
ethon (0.16.0)
|
||||
ffi (>= 1.15.0)
|
||||
ffi (1.17.0)
|
||||
fourflusher (2.3.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.14.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.7.2)
|
||||
minitest (5.25.1)
|
||||
molinillo (0.8.0)
|
||||
mutex_m (0.2.0)
|
||||
nanaimo (0.3.0)
|
||||
nap (1.1.0)
|
||||
netrc (0.11.0)
|
||||
nkf (0.2.0)
|
||||
public_suffix (4.0.7)
|
||||
rexml (3.3.7)
|
||||
ruby-macho (2.5.1)
|
||||
typhoeus (1.4.1)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
xcodeproj (1.25.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (>= 3.3.2, < 4.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
activesupport (>= 6.1.7.5, != 7.1.0)
|
||||
cocoapods (>= 1.13, != 1.15.1, != 1.15.0)
|
||||
|
||||
RUBY VERSION
|
||||
ruby 3.0.0p0
|
||||
|
||||
BUNDLED WITH
|
||||
2.4.21
|
||||
3
example/ios/.xcode.env.local
Normal file
3
example/ios/.xcode.env.local
Normal file
@ -0,0 +1,3 @@
|
||||
# This file should not be committed to the repository.
|
||||
# But there is an issue with the current version of react-native
|
||||
export NODE_BINARY=$(which node)
|
||||
@ -5,7 +5,7 @@ require Pod::Executable.execute_command('node', ['-p',
|
||||
{paths: [process.argv[1]]},
|
||||
)', __dir__]).strip
|
||||
|
||||
platform :ios, min_ios_version_supported
|
||||
platform :ios, '14.0'
|
||||
prepare_react_native_project!
|
||||
|
||||
linkage = ENV['USE_FRAMEWORKS']
|
||||
|
||||
1792
example/ios/Podfile.lock
Normal file
1792
example/ios/Podfile.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -12,8 +12,10 @@
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
2C68AB2CEFC9DAF5E3F08B66 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 6B30A70D1480755735ECB123 /* PrivacyInfo.xcprivacy */; };
|
||||
7699B88040F8A987B510C191 /* libPods-RateAppExample-RateAppExampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-RateAppExample-RateAppExampleTests.a */; };
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
||||
9A74FF152C98E7A7002C8875 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A74FF142C98E7A7002C8875 /* StoreKit.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -36,14 +38,15 @@
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = RateAppExample/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = RateAppExample/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = RateAppExample/main.m; sourceTree = "<group>"; };
|
||||
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = RateAppExample/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
19F6CBCC0A4E27FBF8BF4A61 /* libPods-RateAppExample-RateAppExampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RateAppExample-RateAppExampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3B4392A12AC88292D35C810B /* Pods-RateAppExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RateAppExample.debug.xcconfig"; path = "Target Support Files/Pods-RateAppExample/Pods-RateAppExample.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
5709B34CF0A7D63546082F79 /* Pods-RateAppExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RateAppExample.release.xcconfig"; path = "Target Support Files/Pods-RateAppExample/Pods-RateAppExample.release.xcconfig"; sourceTree = "<group>"; };
|
||||
5B7EB9410499542E8C5724F5 /* Pods-RateAppExample-RateAppExampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RateAppExample-RateAppExampleTests.debug.xcconfig"; path = "Target Support Files/Pods-RateAppExample-RateAppExampleTests/Pods-RateAppExample-RateAppExampleTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
5DCACB8F33CDC322A6C60F78 /* libPods-RateAppExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RateAppExample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6B30A70D1480755735ECB123 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = RateAppExample/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = RateAppExample/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
89C6BE57DB24E9ADA2F236DE /* Pods-RateAppExample-RateAppExampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RateAppExample-RateAppExampleTests.release.xcconfig"; path = "Target Support Files/Pods-RateAppExample-RateAppExampleTests/Pods-RateAppExample-RateAppExampleTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
9A74FF142C98E7A7002C8875 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -61,6 +64,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0C80B921A6F3F58F76C31292 /* libPods-RateAppExample.a in Frameworks */,
|
||||
9A74FF152C98E7A7002C8875 /* StoreKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -93,7 +97,7 @@
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */,
|
||||
6B30A70D1480755735ECB123 /* PrivacyInfo.xcprivacy */,
|
||||
);
|
||||
name = RateAppExample;
|
||||
sourceTree = "<group>";
|
||||
@ -101,6 +105,7 @@
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9A74FF142C98E7A7002C8875 /* StoreKit.framework */,
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||
5DCACB8F33CDC322A6C60F78 /* libPods-RateAppExample.a */,
|
||||
19F6CBCC0A4E27FBF8BF4A61 /* libPods-RateAppExample-RateAppExampleTests.a */,
|
||||
@ -245,6 +250,7 @@
|
||||
files = (
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
2C68AB2CEFC9DAF5E3F08B66 /* PrivacyInfo.xcprivacy in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -431,7 +437,7 @@
|
||||
"-lc++",
|
||||
"$(inherited)",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "rateapp.example";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = rateapp.example;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RateAppExample.app/RateAppExample";
|
||||
};
|
||||
@ -455,7 +461,7 @@
|
||||
"-lc++",
|
||||
"$(inherited)",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "rateapp.example";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = rateapp.example;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RateAppExample.app/RateAppExample";
|
||||
};
|
||||
@ -470,6 +476,7 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = RateAppExample/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@ -480,7 +487,7 @@
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "rateapp.example";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = rateapp.example;
|
||||
PRODUCT_NAME = RateAppExample;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@ -496,6 +503,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
INFOPLIST_FILE = RateAppExample/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@ -506,7 +514,7 @@
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "rateapp.example";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = rateapp.example;
|
||||
PRODUCT_NAME = RateAppExample;
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@ -582,7 +590,11 @@
|
||||
"-DFOLLY_CFG_NO_COROUTINES=1",
|
||||
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
|
||||
);
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
||||
USE_HERMES = true;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@ -647,7 +659,10 @@
|
||||
"-DFOLLY_CFG_NO_COROUTINES=1",
|
||||
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
|
||||
);
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
USE_HERMES = true;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
|
||||
10
example/ios/RateAppExample.xcworkspace/contents.xcworkspacedata
generated
Normal file
10
example/ios/RateAppExample.xcworkspace/contents.xcworkspacedata
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:RateAppExample.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@ -14,13 +14,13 @@
|
||||
"react-native": "0.75.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/preset-env": "^7.20.0",
|
||||
"@babel/runtime": "^7.20.0",
|
||||
"@babel/core": "7.25.2",
|
||||
"@babel/preset-env": "7.25.4",
|
||||
"@babel/runtime": "7.25.6",
|
||||
"@react-native/babel-preset": "0.75.3",
|
||||
"@react-native/metro-config": "0.75.3",
|
||||
"@react-native/typescript-config": "0.75.3",
|
||||
"react-native-builder-bob": "^0.30.2"
|
||||
"react-native-builder-bob": "0.30.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
||||
@ -1,17 +1,27 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { StyleSheet, View, Text } from 'react-native';
|
||||
import { multiply } from 'react-native-rate-app';
|
||||
import { Button, StyleSheet, View } from "react-native";
|
||||
import RateApp, { AndroidMarket } from "react-native-rate-app";
|
||||
|
||||
export default function App() {
|
||||
const [result, setResult] = useState<number | undefined>();
|
||||
const onPressRate = async () => {
|
||||
const result = await RateApp.requestReview().catch((err) =>
|
||||
console.log(err),
|
||||
);
|
||||
console.log("rate", result);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
multiply(3, 7).then(setResult);
|
||||
}, []);
|
||||
const onPressOpenStoreForReview = async () => {
|
||||
const result = await RateApp.openStoreForReview({
|
||||
iOSAppId: "389801252",
|
||||
androidPackageName: "com.instagram.android",
|
||||
androidMarket: AndroidMarket.GOOGLE,
|
||||
}).catch((err) => console.log(err));
|
||||
console.log("store listing", result);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text>Result: {result}</Text>
|
||||
<Button title={"Rate"} onPress={onPressRate} />
|
||||
<Button title={"Open Store Review"} onPress={onPressOpenStoreForReview} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@ -19,12 +29,8 @@ export default function App() {
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
box: {
|
||||
width: 60,
|
||||
height: 60,
|
||||
marginVertical: 20,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 8,
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,18 +1,30 @@
|
||||
#import "RateApp.h"
|
||||
#import <StoreKit/StoreKit.h>
|
||||
|
||||
static NSString *const kNoActiveSceneError = @"no_active_scene";
|
||||
|
||||
@implementation RateApp
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
// Example method
|
||||
// See // https://reactnative.dev/docs/native-modules-ios
|
||||
RCT_EXPORT_METHOD(multiply:(double)a
|
||||
b:(double)b
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
RCT_EXPORT_METHOD(requestReview:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
NSNumber *result = @(a * b);
|
||||
UIWindowScene *scene = [self findActiveScene];
|
||||
if (scene) {
|
||||
[SKStoreReviewController requestReviewInScene:scene];
|
||||
resolve(@(YES));
|
||||
} else {
|
||||
reject(kNoActiveSceneError, @"No active scene found", nil);
|
||||
}
|
||||
}
|
||||
|
||||
resolve(result);
|
||||
- (UIWindowScene *) findActiveScene {
|
||||
for (UIWindowScene *scene in UIApplication.sharedApplication.connectedScenes) {
|
||||
if (scene.activationState == UISceneActivationStateForegroundActive) {
|
||||
return scene;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Don't compile this code when we build for the old architecture.
|
||||
|
||||
14
lefthook.yml
14
lefthook.yml
@ -1,14 +0,0 @@
|
||||
pre-commit:
|
||||
parallel: true
|
||||
commands:
|
||||
lint:
|
||||
glob: "*.{js,ts,jsx,tsx}"
|
||||
run: npx eslint {staged_files}
|
||||
types:
|
||||
glob: "*.{js,ts, jsx, tsx}"
|
||||
run: npx tsc
|
||||
commit-msg:
|
||||
parallel: true
|
||||
commands:
|
||||
commitlint:
|
||||
run: npx commitlint --edit
|
||||
94
package.json
94
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-rate-app",
|
||||
"version": "0.1.0",
|
||||
"version": "0.0.1",
|
||||
"description": "In-App Rating on Android & iOS",
|
||||
"source": "./src/index.tsx",
|
||||
"main": "./lib/commonjs/index.js",
|
||||
@ -38,11 +38,13 @@
|
||||
"scripts": {
|
||||
"example": "yarn workspace react-native-rate-app-example",
|
||||
"test": "jest",
|
||||
"typecheck": "tsc",
|
||||
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
||||
"lint": "npx biome check ./src && npx biome format ./src && yarn lint:example",
|
||||
"lint:example": "npx biome check ./example/src && npx biome format ./example/src",
|
||||
"lint:fix": "npx biome lint --write ./src && npx biome format --write ./src && npx biome check --write ./src && yarn lint:fix:example",
|
||||
"lint:fix:example": "npx biome lint --write ./example/src && npx biome format --write ./example/src && npx biome check --write ./example/src",
|
||||
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
||||
"prepare": "bob build",
|
||||
"release": "release-it"
|
||||
"release": "semantic-release"
|
||||
},
|
||||
"keywords": [
|
||||
"react-native",
|
||||
@ -63,28 +65,26 @@
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/config-conventional": "^17.0.2",
|
||||
"@evilmartians/lefthook": "^1.5.0",
|
||||
"@react-native/eslint-config": "^0.73.1",
|
||||
"@release-it/conventional-changelog": "^5.0.0",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/react": "^18.2.44",
|
||||
"commitlint": "^17.0.2",
|
||||
"del-cli": "^5.1.0",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"jest": "^29.7.0",
|
||||
"prettier": "^3.0.3",
|
||||
"@biomejs/biome": "1.9.1",
|
||||
"@commitlint/cli": "19.5.0",
|
||||
"@commitlint/config-conventional": "19.5.0",
|
||||
"@semantic-release/changelog": "6.0.3",
|
||||
"@semantic-release/git": "10.0.1",
|
||||
"@types/jest": "29.5.13",
|
||||
"@types/react": "18.3.6",
|
||||
"commitlint": "19.5.0",
|
||||
"del-cli": "5.1.0",
|
||||
"husky": "9.1.6",
|
||||
"jest": "29.7.0",
|
||||
"react": "18.3.1",
|
||||
"react-native": "0.75.3",
|
||||
"react-native-builder-bob": "^0.30.2",
|
||||
"release-it": "^15.0.0",
|
||||
"turbo": "^1.10.7",
|
||||
"typescript": "^5.2.2"
|
||||
"react-native-builder-bob": "0.30.2",
|
||||
"semantic-release": "24.1.1",
|
||||
"turbo": "2.1.2",
|
||||
"typescript": "5.6.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "^18.2.44"
|
||||
"@types/react": "18.3.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
@ -93,7 +93,7 @@
|
||||
"workspaces": [
|
||||
"example"
|
||||
],
|
||||
"packageManager": "yarn@3.6.1",
|
||||
"packageManager": "yarn@4.5.0",
|
||||
"jest": {
|
||||
"preset": "react-native",
|
||||
"modulePathIgnorePatterns": [
|
||||
@ -106,54 +106,6 @@
|
||||
"@commitlint/config-conventional"
|
||||
]
|
||||
},
|
||||
"release-it": {
|
||||
"git": {
|
||||
"commitMessage": "chore: release ${version}",
|
||||
"tagName": "v${version}"
|
||||
},
|
||||
"npm": {
|
||||
"publish": true
|
||||
},
|
||||
"github": {
|
||||
"release": true
|
||||
},
|
||||
"plugins": {
|
||||
"@release-it/conventional-changelog": {
|
||||
"preset": "angular"
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"extends": [
|
||||
"@react-native",
|
||||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
"quoteProps": "consistent",
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"eslintIgnore": [
|
||||
"node_modules/",
|
||||
"lib/"
|
||||
],
|
||||
"prettier": {
|
||||
"quoteProps": "consistent",
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": false
|
||||
},
|
||||
"react-native-builder-bob": {
|
||||
"source": "src",
|
||||
"output": "lib",
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
require "json"
|
||||
|
||||
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
||||
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "react-native-rate-app"
|
||||
@ -11,31 +10,14 @@ Pod::Spec.new do |s|
|
||||
s.license = package["license"]
|
||||
s.authors = package["author"]
|
||||
|
||||
s.platforms = { :ios => min_ios_version_supported }
|
||||
s.platforms = { :ios => "14.0" }
|
||||
s.source = { :git => "https://github.com/huextrat/react-native-rate-app.git", :tag => "#{s.version}" }
|
||||
|
||||
s.source_files = "ios/**/*.{h,m,mm,cpp}"
|
||||
|
||||
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
|
||||
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
|
||||
if respond_to?(:install_modules_dependencies, true)
|
||||
if defined?(install_modules_dependencies()) != nil
|
||||
install_modules_dependencies(s)
|
||||
else
|
||||
s.dependency "React-Core"
|
||||
|
||||
# Don't install the dependencies when we run `pod install` in the old architecture.
|
||||
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
|
||||
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
|
||||
s.pod_target_xcconfig = {
|
||||
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
|
||||
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
|
||||
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
|
||||
}
|
||||
s.dependency "React-Codegen"
|
||||
s.dependency "RCT-Folly"
|
||||
s.dependency "RCTRequired"
|
||||
s.dependency "RCTTypeSafety"
|
||||
s.dependency "ReactCommon/turbomodule/core"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* @type {import('@react-native-community/cli-types').UserDependencyConfig}
|
||||
*/
|
||||
module.exports = {
|
||||
dependency: {
|
||||
platforms: {
|
||||
android: {
|
||||
cmakeListsPath: 'generated/jni/CMakeLists.txt',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
7
renovate.json
Normal file
7
renovate.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
],
|
||||
"enabledManagers": ["npm", "github-actions"]
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
import type { TurboModule } from 'react-native';
|
||||
import { TurboModuleRegistry } from 'react-native';
|
||||
import type { TurboModule } from "react-native";
|
||||
import { TurboModuleRegistry } from "react-native";
|
||||
|
||||
export interface Spec extends TurboModule {
|
||||
multiply(a: number, b: number): Promise<number>;
|
||||
requestReview(): Promise<boolean>;
|
||||
}
|
||||
|
||||
export default TurboModuleRegistry.getEnforcing<Spec>('RateApp');
|
||||
export default TurboModuleRegistry.getEnforcing<Spec>("RateApp");
|
||||
|
||||
@ -1 +0,0 @@
|
||||
it.todo('write a test');
|
||||
10
src/constants.ts
Normal file
10
src/constants.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { AndroidMarket } from "./types";
|
||||
|
||||
export const IOS_REVIEW_URL = "itms-apps://apps.apple.com/app/id";
|
||||
|
||||
export const ANDROID_MARKET_URLS: Record<AndroidMarket, string> = {
|
||||
[AndroidMarket.GOOGLE]: "market://details?id={packageName}",
|
||||
[AndroidMarket.AMAZON]: "amzn://apps/android?p={packageName}",
|
||||
[AndroidMarket.SAMSUNG]: "samsungapps://ProductDetail/{packageName}",
|
||||
[AndroidMarket.HUAWEI]: "appmarket://details?id={packageName}",
|
||||
};
|
||||
116
src/index.tsx
116
src/index.tsx
@ -1,29 +1,91 @@
|
||||
import { NativeModules, Platform } from 'react-native';
|
||||
import { Linking, Platform } from "react-native";
|
||||
import NativeRateApp from "./NativeRateApp";
|
||||
import { ANDROID_MARKET_URLS, IOS_REVIEW_URL } from "./constants";
|
||||
import { AndroidMarket, type OpenStoreForReviewProps } from "./types";
|
||||
|
||||
const LINKING_ERROR =
|
||||
`The package 'react-native-rate-app' doesn't seem to be linked. Make sure: \n\n` +
|
||||
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
|
||||
'- You rebuilt the app after installing the package\n' +
|
||||
'- You are not using Expo Go\n';
|
||||
|
||||
// @ts-expect-error
|
||||
const isTurboModuleEnabled = global.__turboModuleProxy != null;
|
||||
|
||||
const RateAppModule = isTurboModuleEnabled
|
||||
? require('./NativeRateApp').default
|
||||
: NativeModules.RateApp;
|
||||
|
||||
const RateApp = RateAppModule
|
||||
? RateAppModule
|
||||
: new Proxy(
|
||||
{},
|
||||
{
|
||||
get() {
|
||||
throw new Error(LINKING_ERROR);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export function multiply(a: number, b: number): Promise<number> {
|
||||
return RateApp.multiply(a, b);
|
||||
/**
|
||||
* Custom error for rate app operations
|
||||
*/
|
||||
class RateAppError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "RateAppError";
|
||||
}
|
||||
}
|
||||
|
||||
const RateApp = {
|
||||
/**
|
||||
* Requests a review from the user.
|
||||
* @returns A promise that resolves to a boolean indicating whether the review was successfully requested.
|
||||
*/
|
||||
async requestReview(): Promise<boolean> {
|
||||
try {
|
||||
return await NativeRateApp.requestReview();
|
||||
} catch (error) {
|
||||
throw new RateAppError(`Failed to request review: ${error}`);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens the store listing for the app.
|
||||
* @param props The properties for the store listing.
|
||||
* @returns A promise that resolves to a boolean indicating whether the store listing was successfully opened.
|
||||
*/
|
||||
async openStoreForReview({
|
||||
iOSAppId,
|
||||
androidPackageName,
|
||||
androidMarket = AndroidMarket.GOOGLE,
|
||||
}: OpenStoreForReviewProps): Promise<boolean> {
|
||||
const isIOS = Platform.OS === "ios";
|
||||
const ismacOS = Platform.OS === "macos";
|
||||
const isAndroid = Platform.OS === "android";
|
||||
let url = "";
|
||||
|
||||
if (isIOS || ismacOS) {
|
||||
if (!iOSAppId) {
|
||||
throw new RateAppError("iOSAppId is required for iOS and macOS");
|
||||
}
|
||||
url = `${IOS_REVIEW_URL}${iOSAppId}?action=write-review`;
|
||||
} else if (isAndroid) {
|
||||
if (!androidPackageName) {
|
||||
throw new RateAppError("androidPackageName is required for Android");
|
||||
}
|
||||
url = this.getAndroidMarketUrl(androidMarket, androidPackageName);
|
||||
} else {
|
||||
throw new RateAppError(`Unsupported platform: ${Platform.OS}`);
|
||||
}
|
||||
|
||||
try {
|
||||
const canOpenURL = await Linking.canOpenURL(url);
|
||||
if (canOpenURL) {
|
||||
await Linking.openURL(url);
|
||||
}
|
||||
return canOpenURL;
|
||||
} catch (error) {
|
||||
throw new RateAppError(`Failed to open store for review: ${error}`);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the URL for the Android market.
|
||||
* @param androidMarket The market where the app's store listing should be opened on Android.
|
||||
* @param androidPackageName The package name of the app to open the store listing for on Android.
|
||||
* @returns The URL for the Android market.
|
||||
*/
|
||||
getAndroidMarketUrl(
|
||||
androidMarket: AndroidMarket,
|
||||
androidPackageName: string,
|
||||
): string {
|
||||
const urlTemplate = ANDROID_MARKET_URLS[androidMarket];
|
||||
if (!urlTemplate) {
|
||||
throw new RateAppError(`Unsupported Android market: ${androidMarket}`);
|
||||
}
|
||||
return urlTemplate.replace("{packageName}", androidPackageName);
|
||||
},
|
||||
};
|
||||
|
||||
export * from "./types";
|
||||
export * from "./constants";
|
||||
export const { requestReview, openStoreForReview, getAndroidMarketUrl } =
|
||||
RateApp;
|
||||
export default RateApp;
|
||||
|
||||
22
src/types.ts
Normal file
22
src/types.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export enum AndroidMarket {
|
||||
GOOGLE = "google",
|
||||
AMAZON = "amazon",
|
||||
SAMSUNG = "samsung",
|
||||
HUAWEI = "huawei",
|
||||
}
|
||||
|
||||
export interface OpenStoreForReviewProps {
|
||||
/**
|
||||
* The App Store ID of the app to open the store listing for on iOS.
|
||||
*/
|
||||
iOSAppId?: string;
|
||||
/**
|
||||
* The package name of the app to open the store listing for on Android.
|
||||
*/
|
||||
androidPackageName?: string;
|
||||
/**
|
||||
* The market where the app's store listing should be opened on Android.
|
||||
* @default AndroidMarket.GOOGLE
|
||||
*/
|
||||
androidMarket?: AndroidMarket;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user