Skip to content

Commit c67893b

Browse files
authored
Merge pull request #2 from JairajJangle/beta
feat: added support for expo managed workflows
2 parents 628fb8e + 46657e9 commit c67893b

File tree

11 files changed

+820
-302
lines changed

11 files changed

+820
-302
lines changed

.github/workflows/beta-release.yml

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
name: Beta Release
2+
on:
3+
push:
4+
branches:
5+
- beta
6+
pull_request:
7+
branches:
8+
- beta
9+
merge_group:
10+
types:
11+
- checks_requested
12+
13+
jobs:
14+
lint:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
20+
- name: Setup
21+
uses: ./.github/actions/setup
22+
23+
- name: Lint files
24+
run: yarn lint
25+
26+
- name: Typecheck files
27+
run: yarn typecheck
28+
29+
test:
30+
runs-on: ubuntu-latest
31+
steps:
32+
- name: Checkout
33+
uses: actions/checkout@v4
34+
35+
- name: Setup
36+
uses: ./.github/actions/setup
37+
38+
- name: Run unit tests
39+
run: yarn test --maxWorkers=2 --coverage
40+
41+
build-library:
42+
runs-on: ubuntu-latest
43+
steps:
44+
- name: Checkout
45+
uses: actions/checkout@v4
46+
47+
- name: Setup
48+
uses: ./.github/actions/setup
49+
50+
- name: Build package
51+
run: yarn prepare
52+
53+
build-android:
54+
runs-on: ubuntu-latest
55+
env:
56+
TURBO_CACHE_DIR: .turbo/android
57+
steps:
58+
- name: Checkout
59+
uses: actions/checkout@v4
60+
61+
- name: Setup
62+
uses: ./.github/actions/setup
63+
64+
- name: Cache turborepo for Android
65+
uses: actions/cache@v4
66+
with:
67+
path: ${{ env.TURBO_CACHE_DIR }}
68+
key: ${{ runner.os }}-turborepo-android-${{ hashFiles('yarn.lock') }}
69+
restore-keys: |
70+
${{ runner.os }}-turborepo-android-
71+
72+
- name: Check turborepo cache for Android
73+
run: |
74+
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")
75+
76+
if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
77+
echo "turbo_cache_hit=1" >> $GITHUB_ENV
78+
fi
79+
80+
- name: Install JDK
81+
if: env.turbo_cache_hit != 1
82+
uses: actions/setup-java@v4
83+
with:
84+
distribution: 'zulu'
85+
java-version: '17'
86+
87+
- name: Finalize Android SDK
88+
if: env.turbo_cache_hit != 1
89+
run: |
90+
/bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null"
91+
92+
- name: Cache Gradle
93+
if: env.turbo_cache_hit != 1
94+
uses: actions/cache@v4
95+
with:
96+
path: |
97+
~/.gradle/wrapper
98+
~/.gradle/caches
99+
key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }}
100+
restore-keys: |
101+
${{ runner.os }}-gradle-
102+
103+
- name: Build example for Android
104+
env:
105+
JAVA_OPTS: "-XX:MaxHeapSize=6g"
106+
run: |
107+
yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}"
108+
109+
build-ios:
110+
runs-on: macos-latest
111+
env:
112+
TURBO_CACHE_DIR: .turbo/ios
113+
steps:
114+
- name: Checkout
115+
uses: actions/checkout@v4
116+
117+
- name: Setup
118+
uses: ./.github/actions/setup
119+
120+
- name: Cache turborepo for iOS
121+
uses: actions/cache@v4
122+
with:
123+
path: ${{ env.TURBO_CACHE_DIR }}
124+
key: ${{ runner.os }}-turborepo-ios-${{ hashFiles('yarn.lock') }}
125+
restore-keys: |
126+
${{ runner.os }}-turborepo-ios-
127+
128+
- name: Check turborepo cache for iOS
129+
run: |
130+
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")
131+
132+
if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
133+
echo "turbo_cache_hit=1" >> $GITHUB_ENV
134+
fi
135+
136+
- name: Cache cocoapods
137+
if: env.turbo_cache_hit != 1
138+
id: cocoapods-cache
139+
uses: actions/cache@v4
140+
with:
141+
path: |
142+
**/ios/Pods
143+
key: ${{ runner.os }}-cocoapods-${{ hashFiles('example/ios/Podfile.lock') }}
144+
restore-keys: |
145+
${{ runner.os }}-cocoapods-
146+
147+
- name: Install cocoapods
148+
if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true'
149+
run: |
150+
cd example/ios
151+
pod install
152+
env:
153+
NO_FLIPPER: 1
154+
155+
- name: Build example for iOS
156+
run: |
157+
yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" 2>&1 | grep -i error > ios_errorlog.txt
158+
159+
- name: Upload iOS error log
160+
uses: actions/upload-artifact@v4
161+
with:
162+
name: ios-error-log
163+
path: ios_errorlog.txt
164+
165+
publish-beta:
166+
needs: [lint, test, build-library, build-android, build-ios]
167+
runs-on: ubuntu-latest
168+
permissions:
169+
contents: write # To publish a GitHub release
170+
issues: write # To comment on released issues
171+
pull-requests: write # To comment on released pull requests
172+
id-token: write # To enable use of OIDC for npm provenance
173+
if: github.ref == 'refs/heads/beta'
174+
steps:
175+
- name: Checkout
176+
uses: actions/checkout@v4
177+
with:
178+
fetch-depth: 0 # Ensures all tags are fetched
179+
180+
- name: Setup
181+
uses: ./.github/actions/setup
182+
183+
- name: Setup Node.js
184+
uses: actions/setup-node@v4
185+
with:
186+
node-version: "lts/*" # Use the latest LTS version of Node.js
187+
registry-url: 'https://registry.npmjs.org/' # Specify npm registry
188+
189+
- name: Verify the integrity of provenance attestations and registry signatures for installed dependencies
190+
run: npm audit signatures # Check the signatures to verify integrity
191+
192+
- name: Release Beta
193+
run: npx semantic-release # Run semantic-release to manage versioning and publishing
194+
env:
195+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GitHub token for authentication
196+
197+
# Why NODE_AUTH_TOKEN instead of NPM_TOKEN: https://github.com/semantic-release/semantic-release/issues/2313
198+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # npm token for publishing package

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
# [1.2.0-beta.2](https://github.com/JairajJangle/react-native-navigation-mode/compare/v1.2.0-beta.1...v1.2.0-beta.2) (2025-07-21)
2+
3+
4+
### Bug Fixes
5+
6+
* expo managed workflow incorrectly loading es module from common js context ([29d171f](https://github.com/JairajJangle/react-native-navigation-mode/commit/29d171f3ce9fd818340ecb0d8d6bfb82042fed0d))
7+
8+
# [1.2.0-beta.1](https://github.com/JairajJangle/react-native-navigation-mode/compare/v1.1.1...v1.2.0-beta.1) (2025-07-21)
9+
10+
11+
### Features
12+
13+
* added support for expo managed workflows ([b60fc17](https://github.com/JairajJangle/react-native-navigation-mode/commit/b60fc17bbfb8d2682d5bc4c3840ce085c1d94a0a))
14+
115
## [1.1.1](https://github.com/JairajJangle/react-native-navigation-mode/compare/v1.1.0...v1.1.1) (2025-07-17)
216

317

README.md

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
🧭 Detect Android navigation mode (3-button, 2-button, or gesture navigation) with native precision using Turbo modules.
44

5-
[![npm version](https://img.shields.io/npm/v/react-native-navigation-mode)](https://badge.fury.io/js/react-native-navigation-mode) [![License](https://img.shields.io/github/license/JairajJangle/react-native-navigation-mode)](https://github.com/JairajJangle/react-native-navigation-mode/blob/main/LICENSE) [![Workflow Status](https://github.com/JairajJangle/react-native-navigation-mode/actions/workflows/ci.yml/badge.svg)](https://github.com/JairajJangle/react-native-navigation-mode/actions/workflows/ci.yml) ![Android](https://img.shields.io/badge/-Android-555555?logo=android&logoColor=3DDC84) ![iOS](https://img.shields.io/badge/-iOS-555555?logo=apple&logoColor=white) [![GitHub issues](https://img.shields.io/github/issues/JairajJangle/react-native-navigation-mode)](https://github.com/JairajJangle/react-native-navigation-mode/issues?q=is%3Aopen+is%3Aissue) ![TS](https://img.shields.io/badge/TypeScript-strict_💪-blue) ![Turbo Module](https://img.shields.io/badge/Turbo%20Module-⚡-orange) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/react-native-navigation-mode)
5+
[![npm version](https://img.shields.io/npm/v/react-native-navigation-mode)](https://badge.fury.io/js/react-native-navigation-mode) [![License](https://img.shields.io/github/license/JairajJangle/react-native-navigation-mode)](https://github.com/JairajJangle/react-native-navigation-mode/blob/main/LICENSE) [![Workflow Status](https://github.com/JairajJangle/react-native-navigation-mode/actions/workflows/ci.yml/badge.svg)](https://github.com/JairajJangle/react-native-navigation-mode/actions/workflows/ci.yml) ![Android](https://img.shields.io/badge/-Android-555555?logo=android&logoColor=3DDC84) ![iOS](https://img.shields.io/badge/-iOS-555555?logo=apple&logoColor=white) [![GitHub issues](https://img.shields.io/github/issues/JairajJangle/react-native-navigation-mode)](https://github.com/JairajJangle/react-native-navigation-mode/issues?q=is%3Aopen+is%3Aissue) ![TS](https://img.shields.io/badge/TypeScript-strict_💪-blue) ![Turbo Module](https://img.shields.io/badge/Turbo%20Module-⚡-orange) ![Expo](https://img.shields.io/badge/Expo-SDK_52+-000020?logo=expo) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/react-native-navigation-mode)
66

77
<table align="center">
88
<tr>
@@ -45,11 +45,14 @@
4545
- 📦 **Zero Dependencies** - Lightweight and performant
4646
- 🛡️ **TypeScript** - Full type safety out of the box
4747
- ↕️ **Edge To Edge Support** - Full support for `react-native-edge-to-edge`
48+
- 📲 **Expo Support** - Works with Expo SDK 52+ managed workflow and development builds
4849

4950
## 🚀 Quick Start
5051

5152
### Installation
5253

54+
#### React Native (Bare Workflow)
55+
5356
Using yarn:
5457

5558
```sh
@@ -64,6 +67,44 @@ npm install react-native-navigation-mode
6467

6568
> **Note:** Auto-linking should handle setup automatically for all newer RN versions.
6669
70+
#### Expo (Managed Workflow)
71+
72+
```sh
73+
npx expo install react-native-navigation-mode
74+
```
75+
76+
##### Expo Configuration
77+
78+
Add the plugin to your `app.json` or `app.config.ts`:
79+
80+
```json
81+
{
82+
"expo": {
83+
"plugins": [
84+
"react-native-navigation-mode"
85+
]
86+
}
87+
}
88+
```
89+
90+
For bare workflow or custom native code, you'll need to prebuild:
91+
92+
```sh
93+
npx expo prebuild
94+
```
95+
96+
##### Development Builds
97+
98+
Since this library contains native code, it requires a custom development build. You cannot use it with standard Expo Go.
99+
100+
#### Requirements
101+
102+
- **React Native**: 0.77.0+
103+
- **Expo SDK**: 52+ (for managed workflow)
104+
- **Android**: API 21+ (Android 5.0+)
105+
- **iOS**: Any version (returns gesture navigation)
106+
- **New Architecture**: Required (enabled by default in RN 0.77+ and Expo SDK 52+)
107+
67108
---
68109

69110
### Basic Usage
@@ -275,7 +316,7 @@ This library uses **official Android APIs** to directly query the system's navig
275316
With Android 15 enforcing edge-to-edge display for apps targeting API 35 and Google mandating this for Play Store updates starting August 31, 2025, proper navigation detection is now **essential**:
276317

277318
- **Edge-to-edge enforcement** - Android 16 will remove the opt-out entirely
278-
- **Expo SDK 53+** - New projects use edge-to-edge by default
319+
- **Expo SDK 52+** - New projects use edge-to-edge by default
279320
- **React Native 0.79+** - Built-in support for 16KB page size and edge-to-edge
280321
- **Safe area management** - Critical for preventing content overlap with system bars
281322

@@ -307,6 +348,7 @@ const isGesture = await isGestureNavigation(); // 🎯 Always accurate
307348
| -------- | ------------ | ------------------------------------------------------------ |
308349
| Android | ✅ Full | Detects all navigation modes and navigation bar height via native Android APIs |
309350
| iOS | ✅ Compatible | Always returns `gesture` and `navigationBarHeight: 0` (iOS uses gesture navigation) |
351+
| Expo | ✅ Full | Supported in managed workflow with SDK 52+ |
310352

311353
### Android Compatibility
312354

@@ -352,6 +394,12 @@ The library uses multiple detection methods for maximum accuracy:
352394
- This is normal on devices without navigation bars (some tablets)
353395
- On older Android versions, fallback detection may not work on all devices
354396

397+
**Expo: "Package does not contain a valid config plugin"**
398+
399+
- Ensure you've installed the latest version of the library
400+
- Try clearing your cache: `npx expo start --clear`
401+
- Make sure the plugin is added to your `app.json`
402+
355403
## 🤝 Contributing
356404

357405
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.

app.plugin.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const { createRunOncePlugin } = require('@expo/config-plugins');
2+
3+
/**
4+
* Expo Config Plugin for react-native-navigation-mode
5+
* This plugin ensures the native module is properly linked in managed workflow
6+
*/
7+
const withNavigationMode = (config) => {
8+
// No additional configuration needed for this module
9+
// The module will be auto-linked through the Expo modules system
10+
return config;
11+
};
12+
13+
const pkg = require('./package.json');
14+
15+
module.exports = createRunOncePlugin(withNavigationMode, pkg.name, pkg.version);

example/ios/NavigationModeExample.xcodeproj/project.pbxproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
5DCACB8F33CDC322A6C60F78 /* libPods-NavigationModeExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NavigationModeExample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
2525
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = NavigationModeExample/AppDelegate.swift; sourceTree = "<group>"; };
2626
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = NavigationModeExample/LaunchScreen.storyboard; sourceTree = "<group>"; };
27+
98B434302E2E8FB4004B224E /* hermes.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = hermes.xcframework; path = "Pods/hermes-engine/destroot/Library/Frameworks/universal/hermes.xcframework"; sourceTree = "<group>"; };
2728
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
2829
/* End PBXFileReference section */
2930

@@ -54,6 +55,7 @@
5455
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
5556
isa = PBXGroup;
5657
children = (
58+
98B434302E2E8FB4004B224E /* hermes.xcframework */,
5759
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
5860
5DCACB8F33CDC322A6C60F78 /* libPods-NavigationModeExample.a */,
5961
);
@@ -185,7 +187,7 @@
185187
};
186188
00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = {
187189
isa = PBXShellScriptBuildPhase;
188-
buildActionMask = 2147483647;
190+
buildActionMask = 12;
189191
files = (
190192
);
191193
inputFileListPaths = (

0 commit comments

Comments
 (0)