Skip to content

Commit 71a1a07

Browse files
committed
Merge branch 'develop'
2 parents 7573415 + ef6f995 commit 71a1a07

File tree

9 files changed

+386
-200
lines changed

9 files changed

+386
-200
lines changed

.github/FUNDING.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# These are supported funding model platforms
2+
3+
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4+
patreon: # Replace with a single Patreon username
5+
open_collective: # Replace with a single Open Collective username
6+
ko_fi: # Replace with a single Ko-fi username
7+
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8+
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9+
liberapay: # Replace with a single Liberapay username
10+
issuehunt: # Replace with a single IssueHunt username
11+
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12+
polar: # Replace with a single Polar username
13+
buy_me_a_coffee: 'thiendo261' # Replace with a single Buy Me a Coffee username
14+
thanks_dev: # Replace with a single thanks.dev username
15+
custom: ['https://www.paypal.me/PurrseusDev'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

README.md

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,80 @@
1-
# react-native-xenon
1+
<div align="center">
22

3-
A comprehensive tool for analyzing HTTP(S) requests and logs in React Native apps. Designed for use across all environments, it offers an intuitive interface for efficient debugging and issue resolution.
3+
# React Native Xenon
4+
5+
### A powerful in-app debugging tool for React Native.
6+
7+
</div>
8+
9+
<div align="center">
10+
11+
[![GitHub Actions Workflow Status][github-actions-status-badge]][github-actions-status-link]
12+
[![NPM Version][npm-version-badge]][npm-version-link]
13+
[![React Native][react-native-badge]][react-native-link]
14+
[![Runs With Expo][expo-badge]][expo-link]
15+
[![Types Included][typescript-badge]][typescript-link]
16+
<br />
17+
[![GitHub License][github-license-badge]][github-license-link]
18+
[![NPM Downloads Per Month][npm-downloads-per-month-badge]][npm-downloads-per-month-link]
19+
[![Buy Me A Coffee][buy-me-a-coffee-badge]][buy-me-a-coffee-link]
20+
21+
</div>
22+
23+
## Features
24+
25+
- :iphone: **In-app debugging** – Debug apps in any environment without the need for debug builds.
26+
- :globe_with_meridians: **Network Inspection** – Monitor HTTP(S) requests (XHR, Fetch) and WebSocket connections.
27+
- :page_with_curl: **Log Capture** – Intercept console messages like log, info, warn, and error for enhanced debugging.
28+
- :zap: **Draggable Bubble UI** – Seamlessly debug without disrupting your workflow.
29+
- :sparkles: **React Native & Expo Support** – Built for compatibility across both platforms.
430

531
## Installation
632

7-
Install the Xenon with `yarn` or `npm`. You will also need to install `react-native-safe-area-context` if you haven't already.
33+
Install Xenon and its dependencies.
34+
35+
### React Native
836

937
```sh
10-
yarn add react-native-xenon react-native-safe-area-context
38+
yarn add react-native-xenon
39+
yarn add react-native-safe-area-context react-native-screens
40+
```
41+
42+
#### Android
43+
44+
`react-native-screens` package requires one additional configuration step to properly work on Android devices. Edit `MainActivity.kt` file which is located under `android/app/src/main/java/<your package name>/`.
45+
46+
Add the highlighted code to the body of `MainActivity` class:
47+
48+
```diff
49+
+ import android.os.Bundle
50+
// ...
51+
52+
class MainActivity: ReactActivity() {
53+
// ...
54+
+ override fun onCreate(savedInstanceState: Bundle?) {
55+
+ super.onCreate(null)
56+
+ }
57+
// ...
58+
}
1159
```
1260

13-
or
61+
This change is required to avoid crashes related to View state being not persisted consistently across Activity restarts.
62+
63+
#### iOS
64+
65+
Don't forget to install pods when you are developing for iOS.
1466

1567
```sh
16-
npm install react-native-xenon react-native-safe-area-context
68+
cd ios && pod install; cd ..
1769
```
1870

1971
### Expo
2072

2173
```sh
22-
npx expo install react-native-xenon react-native-safe-area-context
74+
npx expo install react-native-xenon
75+
npx expo install react-native-safe-area-context react-native-screens
2376
```
2477

25-
> [!NOTE]
26-
> You can skip installing `react-native-safe-area-context` if you have created a project using [the default template](https://docs.expo.dev/get-started/create-a-project). This library is installed as peer dependency for Expo Router library.
27-
2878
## Usage
2979

3080
Add `Xenon.Component` in your app root component.
@@ -54,6 +104,16 @@ And hide it by calling the `hide` method.
54104
Xenon.hide();
55105
```
56106

107+
> [!WARNING]
108+
> `<Xenon.Component />` is enabled by default in all environments, **including production**. This could expose sensitive debugging tools to end users, creating potential security risks.
109+
> To avoid this, make sure to conditionally render the component only in non-production environments. For example:
110+
>
111+
> ```tsx
112+
> {isProduction ? null : <Xenon.Component />}
113+
> ```
114+
>
115+
> Additionally, consider other approaches such as environment-based feature flags or access control to ensure only authorized users (e.g., developers) can interact with it.
116+
57117
## Props
58118
59119
| **Prop** | **Type** | **Description** |
@@ -65,16 +125,53 @@ Xenon.hide();
65125
66126
## Methods
67127
68-
| **Method** | **Return type** | **Description** |
128+
| **Method** | **Return Type** | **Description** |
69129
| ------------- | --------------- | ------------------------------------------------------------------------------------------- |
70130
| `isVisible()` | `boolean` | Checks whether the debugger is currently visible. |
71131
| `show()` | `void` | Makes the debugger visible. If it is already visible, this method has no additional effect. |
72132
| `hide() ` | `void` | Hides the debugger. If it is already hidden, this method has no additional effect. |
73133
134+
## Examples
135+
136+
To try out Xenon, you can run the example project:
137+
138+
```sh
139+
# Clone the repo
140+
git clone https://github.com/purrseus/react-native-xenon.git
141+
cd react-native-xenon
142+
143+
# Install dependencies
144+
yarn install
145+
146+
# Start the Expo development server
147+
yarn example start
148+
```
149+
150+
See the [example](./example) directory for more information.
151+
74152
## Contributing
75153

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

78156
## License
79157

80158
This project is [MIT](./LICENSE) licensed.
159+
160+
<!-- badges -->
161+
162+
[github-actions-status-badge]: https://img.shields.io/github/actions/workflow/status/purrseus/react-native-xenon/ci.yml?style=for-the-badge&logo=github&label=%20&labelColor=1B1B1D
163+
[github-actions-status-link]: ./.github/workflows/ci.yml
164+
[npm-version-badge]: https://img.shields.io/npm/v/react-native-xenon?style=for-the-badge&color=CC3F3E&labelColor=1B1B1D&logo=npm&label=%20&logoColor=CC3F3E
165+
[npm-version-link]: https://www.npmjs.com/package/react-native-xenon
166+
[react-native-badge]: https://img.shields.io/badge/%20React%20Native-67DAFB?style=for-the-badge&logo=react&logoColor=67DAFB&labelColor=1B1B1D
167+
[react-native-link]: https://reactnative.dev
168+
[expo-badge]: https://img.shields.io/badge/Expo-FFFFFF?style=for-the-badge&logo=expo&labelColor=1B1B1D&logoColor=FFFFFF
169+
[expo-link]: https://expo.dev
170+
[typescript-badge]: https://img.shields.io/badge/TypeScript-3077C6?style=for-the-badge&logo=typescript&logoColor=3077C6&labelColor=1B1B1D
171+
[typescript-link]: https://www.typescriptlang.org
172+
[github-license-badge]: https://img.shields.io/badge/License-MIT-44CD11?style=for-the-badge&labelColor=1B1B1D
173+
[github-license-link]: ./LICENSE
174+
[npm-downloads-per-month-badge]: https://img.shields.io/npm/dm/react-native-xenon?style=for-the-badge&labelColor=1B1B1D
175+
[npm-downloads-per-month-link]: https://www.npmjs.com/package/react-native-xenon
176+
[buy-me-a-coffee-badge]: https://img.shields.io/badge/%20-Buy%20me%20a%20coffee-FEDD03?style=for-the-badge&logo=buymeacoffee&labelColor=1B1B1D
177+
[buy-me-a-coffee-link]: https://www.buymeacoffee.com/thiendo261

example/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"react-dom": "18.3.1",
1818
"react-native": "0.76.3",
1919
"react-native-safe-area-context": "4.12.0",
20+
"react-native-screens": "~4.4.0",
2021
"react-native-web": "~0.19.13"
2122
},
2223
"devDependencies": {

example/src/App.tsx

Lines changed: 85 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -13,101 +13,102 @@ function Button({ title, onPress }: { title: string; onPress: () => void }) {
1313

1414
export default function App() {
1515
return (
16-
<View style={styles.container}>
17-
<Button
18-
title="Fetch: Get pokemon"
19-
onPress={() => {
20-
fetch('https://pokeapi.co/api/v2/pokemon/ditto')
21-
.then(res => res.json())
22-
.then(console.log);
23-
}}
24-
/>
16+
<>
17+
<View style={styles.container}>
18+
<Button
19+
title="Fetch: Get pokemon"
20+
onPress={() => {
21+
fetch('https://pokeapi.co/api/v2/pokemon/ditto')
22+
.then(res => res.json())
23+
.then(console.log);
24+
}}
25+
/>
2526

26-
<Button
27-
title="Fetch: Get posts"
28-
onPress={() => {
29-
fetch('https://jsonplaceholder.typicode.com/posts?userId=1')
30-
.then(res => res.json())
31-
.then(console.log);
32-
}}
33-
/>
27+
<Button
28+
title="Fetch: Get posts"
29+
onPress={() => {
30+
fetch('https://jsonplaceholder.typicode.com/posts?userId=1')
31+
.then(res => res.json())
32+
.then(console.log);
33+
}}
34+
/>
3435

35-
<Button
36-
title="Fetch: Create post"
37-
onPress={() => {
38-
fetch('https://jsonplaceholder.typicode.com/posts', {
39-
method: 'POST',
40-
body: JSON.stringify({
41-
title: 'foo',
42-
body: 'bar',
43-
userId: 1,
44-
}),
45-
headers: {
46-
'Content-type': 'application/json; charset=UTF-8',
47-
},
48-
})
49-
.then(res => res.json())
50-
.then(console.log);
51-
}}
52-
/>
53-
54-
<Button
55-
title="Axios: Get pokemon"
56-
onPress={() => {
57-
axios('https://pokeapi.co/api/v2/pokemon/ditto').then(console.log);
58-
}}
59-
/>
36+
<Button
37+
title="Fetch: Create post"
38+
onPress={() => {
39+
fetch('https://jsonplaceholder.typicode.com/posts', {
40+
method: 'POST',
41+
body: JSON.stringify({
42+
title: 'foo',
43+
body: 'bar',
44+
userId: 1,
45+
}),
46+
headers: {
47+
'Content-type': 'application/json; charset=UTF-8',
48+
},
49+
})
50+
.then(res => res.json())
51+
.then(console.log);
52+
}}
53+
/>
6054

61-
<Button
62-
title="Axios: Get posts"
63-
onPress={() => {
64-
axios('https://jsonplaceholder.typicode.com/posts?userId=1').then(console.log);
65-
}}
66-
/>
55+
<Button
56+
title="Axios: Get pokemon"
57+
onPress={() => {
58+
axios('https://pokeapi.co/api/v2/pokemon/ditto').then(console.log);
59+
}}
60+
/>
6761

68-
<Button
69-
title="Axios: Create post"
70-
onPress={() => {
71-
axios
72-
.post('https://jsonplaceholder.typicode.com/posts', {
73-
title: 'foo',
74-
body: 'bar',
75-
userId: 1,
76-
})
77-
.then(console.log);
78-
}}
79-
/>
62+
<Button
63+
title="Axios: Get posts"
64+
onPress={() => {
65+
axios('https://jsonplaceholder.typicode.com/posts?userId=1').then(console.log);
66+
}}
67+
/>
8068

81-
<Button
82-
title="Echo Websocket"
83-
onPress={() => {
84-
const socket = new WebSocket('wss://echo.websocket.org');
69+
<Button
70+
title="Axios: Create post"
71+
onPress={() => {
72+
axios
73+
.post('https://jsonplaceholder.typicode.com/posts', {
74+
title: 'foo',
75+
body: 'bar',
76+
userId: 1,
77+
})
78+
.then(console.log);
79+
}}
80+
/>
8581

86-
const message = `Hello Server! It's ${new Date().toISOString()}`;
82+
<Button
83+
title="Echo Websocket"
84+
onPress={() => {
85+
const socket = new WebSocket('wss://echo.websocket.org');
8786

88-
socket.onopen = () => {
89-
socket.send(message);
90-
console.log('WebSocket.send:', message);
91-
};
87+
const message = `Hello Server! It's ${new Date().toISOString()}`;
9288

93-
socket.onmessage = event => {
94-
console.log('WebSocket.onmessage:', event.data);
95-
if (event.data === message) {
96-
socket.close();
97-
}
98-
};
99-
}}
100-
/>
89+
socket.onopen = () => {
90+
socket.send(message);
91+
console.log('WebSocket.send:', message);
92+
};
10193

102-
<Button
103-
title="Toggle Debugger"
104-
onPress={() => {
105-
Xenon.isVisible() ? Xenon.hide() : Xenon.show();
106-
}}
107-
/>
94+
socket.onmessage = event => {
95+
console.log('WebSocket.onmessage:', event.data);
96+
if (event.data === message) {
97+
socket.close();
98+
}
99+
};
100+
}}
101+
/>
108102

103+
<Button
104+
title="Toggle Debugger"
105+
onPress={() => {
106+
Xenon.isVisible() ? Xenon.hide() : Xenon.show();
107+
}}
108+
/>
109+
</View>
109110
<Xenon.Component />
110-
</View>
111+
</>
111112
);
112113
}
113114

0 commit comments

Comments
 (0)