chore: init react-native-rate-app

This commit is contained in:
Hugo EXTRAT 2024-09-17 14:17:53 +02:00
parent 6561af6522
commit 6f54ce15fe
No known key found for this signature in database
GPG Key ID: 9A1C6BCBF89ECB2A
45 changed files with 15091 additions and 1821 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1 @@
* @huextrat

15
.github/FUNDING.yml vendored Normal file
View 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']

View File

@ -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

View File

@ -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
View 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
View File

@ -0,0 +1 @@
yarn commitlint --edit "$1"

2
.husky/pre-commit Normal file
View File

@ -0,0 +1,2 @@
yarn lint
yarn format:check

2
.nvmrc
View File

@ -1 +1 @@
v18
20.17.0

70
.releaserc.json Normal file
View 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"
]
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

925
.yarn/releases/yarn-4.5.0.cjs vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -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
View File

@ -0,0 +1,5 @@
# Changelog
# 0.0.1 (2024-09-16)
Init react-native-app-rate project

View File

@ -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
View File

@ -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

View File

@ -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"
}

View File

@ -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

View File

@ -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"/>

View File

@ -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 {

View File

@ -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
}

View File

@ -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
View 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
View File

@ -0,0 +1 @@
module.exports = { extends: ['@commitlint/config-conventional'] };

112
example/Gemfile.lock Normal file
View 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

View 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)

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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;

View 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>

View File

@ -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"

View File

@ -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,
},
});

View File

@ -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.

View File

@ -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

View File

@ -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",

View File

@ -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

View File

@ -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
View File

@ -0,0 +1,7 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"enabledManagers": ["npm", "github-actions"]
}

View File

@ -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");

View File

@ -1 +0,0 @@
it.todo('write a test');

10
src/constants.ts Normal file
View 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}",
};

View File

@ -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
View 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;
}

11605
yarn.lock Normal file

File diff suppressed because it is too large Load Diff