expotesting2 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +289 -0
- package/apps/expo-app/app.json +60 -0
- package/apps/expo-app/babel.config.js +7 -0
- package/apps/expo-app/index.js +6 -0
- package/apps/expo-app/package.json +46 -0
- package/apps/expo-app/src/App.jsx +37 -0
- package/apps/expo-app/src/navigation/RootNavigator.jsx +82 -0
- package/apps/expo-app/src/navigation/types.js +5 -0
- package/apps/expo-app/src/screens/HomeScreen.jsx +178 -0
- package/package.json +24 -0
- package/packages/animations/package.json +20 -0
- package/packages/animations/src/components/FadeView.jsx +42 -0
- package/packages/animations/src/components/ScaleView.jsx +28 -0
- package/packages/animations/src/components/SlideView.jsx +32 -0
- package/packages/animations/src/hooks/useFade.js +50 -0
- package/packages/animations/src/hooks/useScale.js +59 -0
- package/packages/animations/src/hooks/useSlide.js +53 -0
- package/packages/animations/src/index.js +21 -0
- package/packages/animations/src/reanimated.js +83 -0
- package/packages/core/package.json +22 -0
- package/packages/core/src/components/Button.jsx +92 -0
- package/packages/core/src/components/Card.jsx +47 -0
- package/packages/core/src/components/Container.jsx +61 -0
- package/packages/core/src/components/Input.jsx +83 -0
- package/packages/core/src/components/List.jsx +80 -0
- package/packages/core/src/components/index.js +9 -0
- package/packages/core/src/hooks/index.js +5 -0
- package/packages/core/src/hooks/useAsync.js +60 -0
- package/packages/core/src/hooks/useCounter.js +36 -0
- package/packages/core/src/hooks/useToggle.js +18 -0
- package/packages/core/src/index.js +5 -0
- package/packages/core/src/theme/index.js +67 -0
- package/packages/core/src/utils/helpers.js +93 -0
- package/packages/core/src/utils/index.js +10 -0
- package/packages/device/package.json +24 -0
- package/packages/device/src/hooks/useCamera.js +45 -0
- package/packages/device/src/hooks/useGallery.js +70 -0
- package/packages/device/src/hooks/useLocation.js +99 -0
- package/packages/device/src/index.js +5 -0
- package/packages/examples/package.json +36 -0
- package/packages/examples/src/experiments/animations-device/AnimationsDeviceScreen.jsx +291 -0
- package/packages/examples/src/experiments/basic-app/BasicAppScreen.jsx +162 -0
- package/packages/examples/src/experiments/components-props-state/ComponentsStateScreen.jsx +280 -0
- package/packages/examples/src/experiments/navigation/NavigationScreen.jsx +202 -0
- package/packages/examples/src/experiments/network-storage/NetworkStorageScreen.jsx +367 -0
- package/packages/examples/src/experiments/state-management/StateManagementScreen.jsx +255 -0
- package/packages/examples/src/index.js +76 -0
- package/packages/navigation/package.json +20 -0
- package/packages/navigation/src/DrawerNavigator.jsx +35 -0
- package/packages/navigation/src/StackNavigator.jsx +51 -0
- package/packages/navigation/src/TabNavigator.jsx +44 -0
- package/packages/navigation/src/createAppNavigator.jsx +48 -0
- package/packages/navigation/src/index.js +8 -0
- package/packages/navigation/src/types.js +18 -0
- package/packages/network/package.json +19 -0
- package/packages/network/src/apiClient.js +90 -0
- package/packages/network/src/fetchHelpers.js +97 -0
- package/packages/network/src/hooks/useFetch.js +56 -0
- package/packages/network/src/index.js +3 -0
- package/packages/network/src/types.js +4 -0
- package/packages/state/package.json +22 -0
- package/packages/state/src/context/AuthContext.jsx +94 -0
- package/packages/state/src/context/ThemeContext.jsx +79 -0
- package/packages/state/src/context/index.js +3 -0
- package/packages/state/src/index.js +5 -0
- package/packages/state/src/redux/hooks.js +12 -0
- package/packages/state/src/redux/index.js +7 -0
- package/packages/state/src/redux/slices/counterSlice.js +39 -0
- package/packages/state/src/redux/slices/postsSlice.js +92 -0
- package/packages/state/src/redux/store.js +32 -0
- package/packages/storage/package.json +24 -0
- package/packages/storage/src/asyncStorage.js +82 -0
- package/packages/storage/src/index.js +2 -0
- package/packages/storage/src/sqlite/database.js +65 -0
- package/packages/storage/src/sqlite/index.js +3 -0
- package/packages/storage/src/sqlite/operations.js +112 -0
- package/packages/storage/src/sqlite/useSQLite.js +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# expotesting
|
|
2
|
+
|
|
3
|
+
> **Production-ready React Native (Expo) learning library** — modular, fully typed, aligned to a structured university syllabus.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
`expotesting` is a **pnpm + Turborepo monorepo** that functions as both a learning platform and a reusable component/hook library for React Native (Expo) development. It mirrors academic progression across three modules:
|
|
10
|
+
|
|
11
|
+
| Module | Topic | Experiments |
|
|
12
|
+
|--------|-------|-------------|
|
|
13
|
+
| 1 | React Basics — Components, Navigation, JSX | 01, 02, 03 |
|
|
14
|
+
| 2 | State, Networking & Data | 04, 05 |
|
|
15
|
+
| 3 | Advanced — Animations & Device Features | 06 |
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Monorepo Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
expotesting/
|
|
23
|
+
├── apps/
|
|
24
|
+
│ └── expo-app/ # Demo app — runs all 6 experiments
|
|
25
|
+
├── packages/
|
|
26
|
+
│ ├── core/ # Components, hooks, theme, utils
|
|
27
|
+
│ ├── navigation/ # Stack, Tab, Drawer navigators + factory
|
|
28
|
+
│ ├── state/ # Context API + Redux Toolkit
|
|
29
|
+
│ ├── network/ # Axios client + fetch helpers + useFetch
|
|
30
|
+
│ ├── storage/ # AsyncStorage + SQLite wrappers + useSQLite
|
|
31
|
+
│ ├── animations/ # Animated API + Reanimated utilities
|
|
32
|
+
│ ├── device/ # useCamera, useGallery, useLocation
|
|
33
|
+
│ └── examples/ # All 6 runnable experiment screens
|
|
34
|
+
├── package.json # Root (private, pnpm workspace)
|
|
35
|
+
├── pnpm-workspace.yaml
|
|
36
|
+
├── turbo.json
|
|
37
|
+
└── tsconfig.base.json
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Getting Started
|
|
43
|
+
|
|
44
|
+
### Prerequisites
|
|
45
|
+
- Node.js ≥ 18
|
|
46
|
+
- pnpm ≥ 9 (`npm i -g pnpm`)
|
|
47
|
+
- Expo Go app on a physical device, or an iOS/Android simulator
|
|
48
|
+
|
|
49
|
+
### Install
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pnpm install
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Run the demo app
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pnpm start
|
|
59
|
+
# or
|
|
60
|
+
cd apps/expo-app && expo start
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Packages
|
|
66
|
+
|
|
67
|
+
### `@expotesting/core`
|
|
68
|
+
|
|
69
|
+
Reusable UI components and hooks with zero external dependencies beyond React Native.
|
|
70
|
+
|
|
71
|
+
**Components**
|
|
72
|
+
```tsx
|
|
73
|
+
import { Container, Card, Input, Button, List } from '@expotesting/core';
|
|
74
|
+
|
|
75
|
+
<Container scroll padded>
|
|
76
|
+
<Card title="Profile" subtitle="Software Engineer" elevation="md">
|
|
77
|
+
<Input label="Email" value={email} onChangeText={setEmail} />
|
|
78
|
+
<Button onPress={handleSubmit} loading={submitting}>Submit</Button>
|
|
79
|
+
</Card>
|
|
80
|
+
<List data={items} renderItem={({ item }) => <Text>{item.name}</Text>} />
|
|
81
|
+
</Container>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Hooks**
|
|
85
|
+
```ts
|
|
86
|
+
import { useToggle, useCounter, useAsync } from '@expotesting/core';
|
|
87
|
+
|
|
88
|
+
const { value: isOpen, toggle } = useToggle(false);
|
|
89
|
+
const { count, increment, decrement, reset } = useCounter(0, { min: 0, max: 10 });
|
|
90
|
+
const { data, loading, error, execute } = useAsync(() => fetchUser(id));
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Theme tokens**
|
|
94
|
+
```ts
|
|
95
|
+
import { palette, spacing, typography, radius, shadows } from '@expotesting/core';
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### `@expotesting/navigation`
|
|
101
|
+
|
|
102
|
+
Pre-wired navigators built on React Navigation v6.
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
import { createAppNavigator } from '@expotesting/navigation';
|
|
106
|
+
import { NavigationContainer } from '@react-navigation/native';
|
|
107
|
+
|
|
108
|
+
const Navigator = createAppNavigator({
|
|
109
|
+
type: 'tabs', // 'stack' | 'tabs' | 'drawer'
|
|
110
|
+
screens: [
|
|
111
|
+
{ name: 'Home', component: HomeScreen },
|
|
112
|
+
{ name: 'Profile', component: ProfileScreen },
|
|
113
|
+
],
|
|
114
|
+
activeColor: '#6200EE',
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
export default function App() {
|
|
118
|
+
return (
|
|
119
|
+
<NavigationContainer>
|
|
120
|
+
<Navigator />
|
|
121
|
+
</NavigationContainer>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
### `@expotesting/state`
|
|
129
|
+
|
|
130
|
+
#### Context API
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import { ThemeProvider, useTheme, AuthProvider, useAuth } from '@expotesting/state';
|
|
134
|
+
|
|
135
|
+
// Wrap app
|
|
136
|
+
<ThemeProvider><AuthProvider>{children}</AuthProvider></ThemeProvider>
|
|
137
|
+
|
|
138
|
+
// Consume
|
|
139
|
+
const { colors, isDark, toggleTheme } = useTheme();
|
|
140
|
+
const { user, login, logout, isAuthenticated } = useAuth();
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### Redux Toolkit
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
import { Provider } from 'react-redux';
|
|
147
|
+
import {
|
|
148
|
+
store,
|
|
149
|
+
useAppSelector, useAppDispatch,
|
|
150
|
+
increment, decrement, fetchPosts,
|
|
151
|
+
selectCount, selectAllPosts,
|
|
152
|
+
} from '@expotesting/state';
|
|
153
|
+
|
|
154
|
+
// Root
|
|
155
|
+
<Provider store={store}>{children}</Provider>
|
|
156
|
+
|
|
157
|
+
// In components
|
|
158
|
+
const count = useAppSelector(selectCount);
|
|
159
|
+
const dispatch = useAppDispatch();
|
|
160
|
+
dispatch(increment());
|
|
161
|
+
dispatch(fetchPosts({ page: 1, limit: 10 }));
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
### `@expotesting/network`
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { apiClient, useFetch } from '@expotesting/network';
|
|
170
|
+
|
|
171
|
+
// Axios instance
|
|
172
|
+
const { data } = await apiClient.get<User>('/users/1');
|
|
173
|
+
await apiClient.post('/posts', { title: 'Hello' });
|
|
174
|
+
|
|
175
|
+
// Hook
|
|
176
|
+
const { data, loading, error, refetch } = useFetch<Post[]>('/posts?_limit=10');
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
### `@expotesting/storage`
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
import { appStorage, createStorage, useSQLite } from '@expotesting/storage';
|
|
185
|
+
|
|
186
|
+
// AsyncStorage
|
|
187
|
+
await appStorage.setItem('theme', 'dark');
|
|
188
|
+
const theme = await appStorage.getItem<string>('theme');
|
|
189
|
+
|
|
190
|
+
// Namespaced
|
|
191
|
+
const authStorage = createStorage('auth');
|
|
192
|
+
await authStorage.setItem('token', jwt);
|
|
193
|
+
|
|
194
|
+
// SQLite hook
|
|
195
|
+
const { db, ready, error } = useSQLite('myapp.db');
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
if (!ready || !db) return;
|
|
198
|
+
void db.runAsync('CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, name TEXT)');
|
|
199
|
+
}, [ready, db]);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
### `@expotesting/animations`
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
import { FadeView, ScaleView, SlideView } from '@expotesting/animations';
|
|
208
|
+
import { reanimatedFadeIn, reanimatedSpringScale } from '@expotesting/animations';
|
|
209
|
+
|
|
210
|
+
// Animated API components
|
|
211
|
+
<FadeView visible={show} duration={300}>
|
|
212
|
+
<MyContent />
|
|
213
|
+
</FadeView>
|
|
214
|
+
|
|
215
|
+
<ScaleView visible={show}>
|
|
216
|
+
<MyContent />
|
|
217
|
+
</ScaleView>
|
|
218
|
+
|
|
219
|
+
<SlideView visible={show} direction="top">
|
|
220
|
+
<MyContent />
|
|
221
|
+
</SlideView>
|
|
222
|
+
|
|
223
|
+
// Reanimated hooks
|
|
224
|
+
import { useFade, useScale } from '@expotesting/animations';
|
|
225
|
+
const { opacity, fadeIn, fadeOut } = useFade();
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
### `@expotesting/device`
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import { useCamera, useGallery, useLocation } from '@expotesting/device';
|
|
234
|
+
|
|
235
|
+
// Camera
|
|
236
|
+
const { hasPermission, requestPermission, capturePhoto, lastPhoto } = useCamera();
|
|
237
|
+
|
|
238
|
+
// Gallery
|
|
239
|
+
const { pickImage, selectedAsset } = useGallery({ allowsEditing: true });
|
|
240
|
+
|
|
241
|
+
// Location
|
|
242
|
+
const { location, getLocation, loading } = useLocation({ accuracy: 'balanced' });
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Experiments
|
|
248
|
+
|
|
249
|
+
| # | Screen | Topics |
|
|
250
|
+
|---|--------|--------|
|
|
251
|
+
| 01 | `BasicAppScreen` | View · Text · Image · StyleSheet · Flexbox · Component Composition |
|
|
252
|
+
| 02 | `ComponentsStateScreen` | useState · useReducer · Event Handling · Controlled Inputs |
|
|
253
|
+
| 03 | `NavHomeScreen` / `NavDetailsScreen` | Stack with typed params · Navigation.navigate · route.params |
|
|
254
|
+
| 04 | `StateManagementScreen` | Context API · Redux counter · Async thunk (fetchPosts) |
|
|
255
|
+
| 05 | `NetworkStorageScreen` | fetch() · Axios · FlatList · AsyncStorage · SQLite CRUD |
|
|
256
|
+
| 06 | `AnimationsDeviceScreen` | Animated API · Reanimated entering/exiting · Camera · Gallery · GPS |
|
|
257
|
+
|
|
258
|
+
Each experiment is a self-contained React component importable from `@expotesting/examples`.
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
import { BasicAppScreen, EXPERIMENT_REGISTRY } from '@expotesting/examples';
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## TypeScript
|
|
267
|
+
|
|
268
|
+
All packages are authored in **strict TypeScript**. Type definitions ship alongside source files. No separate `@types/*` packages are required for the internal API.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Tooling
|
|
273
|
+
|
|
274
|
+
| Tool | Purpose |
|
|
275
|
+
|------|---------|
|
|
276
|
+
| pnpm workspaces | Monorepo package management |
|
|
277
|
+
| Turborepo | Build orchestration + caching |
|
|
278
|
+
| TypeScript 5 strict | Type safety |
|
|
279
|
+
| Expo SDK ~52 | Managed workflow |
|
|
280
|
+
| React Navigation 6 | Navigation |
|
|
281
|
+
| Redux Toolkit 2 | State management |
|
|
282
|
+
| Axios 1 | HTTP client |
|
|
283
|
+
| Reanimated 3 | Advanced animations |
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## License
|
|
288
|
+
|
|
289
|
+
MIT
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"expo": {
|
|
3
|
+
"name": "expotesting",
|
|
4
|
+
"slug": "expotesting",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"orientation": "portrait",
|
|
7
|
+
"icon": "./assets/icon.png",
|
|
8
|
+
"userInterfaceStyle": "automatic",
|
|
9
|
+
"splash": {
|
|
10
|
+
"image": "./assets/splash.png",
|
|
11
|
+
"resizeMode": "contain",
|
|
12
|
+
"backgroundColor": "#6200EE"
|
|
13
|
+
},
|
|
14
|
+
"assetBundlePatterns": ["**/*"],
|
|
15
|
+
"ios": {
|
|
16
|
+
"supportsTablet": true,
|
|
17
|
+
"bundleIdentifier": "com.expotesting.app",
|
|
18
|
+
"infoPlist": {
|
|
19
|
+
"NSCameraUsageDescription": "Used in Experiment 06 to demonstrate Expo Camera.",
|
|
20
|
+
"NSPhotoLibraryUsageDescription": "Used in Experiment 06 to demonstrate ImagePicker.",
|
|
21
|
+
"NSLocationWhenInUseUsageDescription": "Used in Experiment 06 to demonstrate GPS location."
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"android": {
|
|
25
|
+
"adaptiveIcon": {
|
|
26
|
+
"foregroundImage": "./assets/adaptive-icon.png",
|
|
27
|
+
"backgroundColor": "#6200EE"
|
|
28
|
+
},
|
|
29
|
+
"package": "com.expotesting.app",
|
|
30
|
+
"permissions": [
|
|
31
|
+
"CAMERA",
|
|
32
|
+
"READ_EXTERNAL_STORAGE",
|
|
33
|
+
"WRITE_EXTERNAL_STORAGE",
|
|
34
|
+
"ACCESS_FINE_LOCATION",
|
|
35
|
+
"ACCESS_COARSE_LOCATION"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"web": {
|
|
39
|
+
"favicon": "./assets/favicon.png"
|
|
40
|
+
},
|
|
41
|
+
"plugins": [
|
|
42
|
+
[
|
|
43
|
+
"expo-camera",
|
|
44
|
+
{ "cameraPermission": "Allow expotesting to access camera." }
|
|
45
|
+
],
|
|
46
|
+
[
|
|
47
|
+
"expo-image-picker",
|
|
48
|
+
{ "photosPermission": "Allow expotesting to access photos." }
|
|
49
|
+
],
|
|
50
|
+
[
|
|
51
|
+
"expo-location",
|
|
52
|
+
{ "locationWhenInUsePermission": "Allow expotesting to access your location." }
|
|
53
|
+
],
|
|
54
|
+
"expo-sqlite",
|
|
55
|
+
[
|
|
56
|
+
"react-native-reanimated"
|
|
57
|
+
]
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "expo-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "expotesting demo app — showcases all 6 experiments",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"private": true,
|
|
7
|
+
"scripts": {
|
|
8
|
+
"start": "expo start",
|
|
9
|
+
"android": "expo start --android",
|
|
10
|
+
"ios": "expo start --ios",
|
|
11
|
+
"web": "expo start --web"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@expotesting/animations": "workspace:*",
|
|
15
|
+
"@expotesting/core": "workspace:*",
|
|
16
|
+
"@expotesting/device": "workspace:*",
|
|
17
|
+
"@expotesting/examples": "workspace:*",
|
|
18
|
+
"@expotesting/navigation": "workspace:*",
|
|
19
|
+
"@expotesting/network": "workspace:*",
|
|
20
|
+
"@expotesting/state": "workspace:*",
|
|
21
|
+
"@expotesting/storage": "workspace:*",
|
|
22
|
+
"@react-native-async-storage/async-storage": "^1.23.1",
|
|
23
|
+
"@react-navigation/bottom-tabs": "^6.6.1",
|
|
24
|
+
"@react-navigation/drawer": "^6.7.2",
|
|
25
|
+
"@react-navigation/native": "^6.1.18",
|
|
26
|
+
"@react-navigation/native-stack": "^6.11.0",
|
|
27
|
+
"@reduxjs/toolkit": "^2.3.0",
|
|
28
|
+
"axios": "^1.7.0",
|
|
29
|
+
"expo": "~52.0.0",
|
|
30
|
+
"expo-camera": "~16.0.0",
|
|
31
|
+
"expo-image-picker": "~16.0.0",
|
|
32
|
+
"expo-location": "~18.0.0",
|
|
33
|
+
"expo-sqlite": "~15.0.0",
|
|
34
|
+
"expo-status-bar": "~2.0.0",
|
|
35
|
+
"react": "18.3.1",
|
|
36
|
+
"react-native": "0.76.3",
|
|
37
|
+
"react-native-gesture-handler": "~2.20.0",
|
|
38
|
+
"react-native-reanimated": "~3.16.0",
|
|
39
|
+
"react-native-safe-area-context": "4.12.0",
|
|
40
|
+
"react-native-screens": "~4.3.0",
|
|
41
|
+
"react-redux": "^9.1.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@babel/core": "^7.25.0"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App.tsx — root component.
|
|
3
|
+
*
|
|
4
|
+
* Wires up:
|
|
5
|
+
* - Redux Provider (store from @expotesting/state)
|
|
6
|
+
* - ThemeProvider + AuthProvider (Context API)
|
|
7
|
+
* - GestureHandlerRootView (required by react-native-gesture-handler)
|
|
8
|
+
* - SafeAreaProvider
|
|
9
|
+
* - RootNavigator
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
14
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
15
|
+
import { Provider } from 'react-redux';
|
|
16
|
+
import { StatusBar } from 'expo-status-bar';
|
|
17
|
+
|
|
18
|
+
import { store } from '@expotesting/state';
|
|
19
|
+
import { ThemeProvider, AuthProvider } from '@expotesting/state';
|
|
20
|
+
import { RootNavigator } from './navigation/RootNavigator';
|
|
21
|
+
|
|
22
|
+
export default function App(){
|
|
23
|
+
return (
|
|
24
|
+
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
25
|
+
<SafeAreaProvider>
|
|
26
|
+
<Provider store={store}>
|
|
27
|
+
<ThemeProvider>
|
|
28
|
+
<AuthProvider>
|
|
29
|
+
<StatusBar style="light" />
|
|
30
|
+
<RootNavigator />
|
|
31
|
+
</AuthProvider>
|
|
32
|
+
</ThemeProvider>
|
|
33
|
+
</Provider>
|
|
34
|
+
</SafeAreaProvider>
|
|
35
|
+
</GestureHandlerRootView>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RootNavigator — top-level stack that hosts the catalogue
|
|
3
|
+
* and all experiment screens.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { NavigationContainer } from '@react-navigation/native';
|
|
8
|
+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
|
9
|
+
|
|
10
|
+
import { HomeScreen } from '../screens/HomeScreen';
|
|
11
|
+
import {
|
|
12
|
+
BasicAppScreen,
|
|
13
|
+
ComponentsStateScreen,
|
|
14
|
+
NavHomeScreen,
|
|
15
|
+
NavDetailsScreen,
|
|
16
|
+
NavProfileScreen,
|
|
17
|
+
StateManagementScreen,
|
|
18
|
+
NetworkStorageScreen,
|
|
19
|
+
AnimationsDeviceScreen } from '@expotesting/examples';
|
|
20
|
+
|
|
21
|
+
const Stack = createNativeStackNavigator();
|
|
22
|
+
|
|
23
|
+
export function RootNavigator(){
|
|
24
|
+
return (
|
|
25
|
+
<NavigationContainer>
|
|
26
|
+
<Stack.Navigator
|
|
27
|
+
initialRouteName="Home"
|
|
28
|
+
screenOptions={{
|
|
29
|
+
headerBackTitleVisible: false,
|
|
30
|
+
headerStyle: { backgroundColor: '#6200EE' },
|
|
31
|
+
headerTintColor: '#fff',
|
|
32
|
+
headerTitleStyle: { fontWeight: '700' } }}
|
|
33
|
+
>
|
|
34
|
+
<Stack.Screen
|
|
35
|
+
name="Home"
|
|
36
|
+
component={HomeScreen}
|
|
37
|
+
options={{ headerShown: false }}
|
|
38
|
+
/>
|
|
39
|
+
<Stack.Screen
|
|
40
|
+
name="Basic"
|
|
41
|
+
component={BasicAppScreen}
|
|
42
|
+
options={{ title: 'Exp 01 — Basic App', headerShown: false }}
|
|
43
|
+
/>
|
|
44
|
+
<Stack.Screen
|
|
45
|
+
name="State"
|
|
46
|
+
component={ComponentsStateScreen}
|
|
47
|
+
options={{ title: 'Exp 02 — Components & State', headerShown: false }}
|
|
48
|
+
/>
|
|
49
|
+
<Stack.Screen
|
|
50
|
+
name="NavHome"
|
|
51
|
+
component={NavHomeScreen}
|
|
52
|
+
options={{ title: 'Exp 03 — Navigation', headerShown: false }}
|
|
53
|
+
/>
|
|
54
|
+
<Stack.Screen
|
|
55
|
+
name="NavDetails"
|
|
56
|
+
component={NavDetailsScreen}
|
|
57
|
+
options={{ title: 'Details', headerShown: true }}
|
|
58
|
+
/>
|
|
59
|
+
<Stack.Screen
|
|
60
|
+
name="NavProfile"
|
|
61
|
+
component={NavProfileScreen}
|
|
62
|
+
options={{ title: 'Profile', headerShown: true }}
|
|
63
|
+
/>
|
|
64
|
+
<Stack.Screen
|
|
65
|
+
name="StateMan"
|
|
66
|
+
component={StateManagementScreen}
|
|
67
|
+
options={{ title: 'Exp 04 — State Management', headerShown: false }}
|
|
68
|
+
/>
|
|
69
|
+
<Stack.Screen
|
|
70
|
+
name="NetStore"
|
|
71
|
+
component={NetworkStorageScreen}
|
|
72
|
+
options={{ title: 'Exp 05 — Networking & Storage', headerShown: false }}
|
|
73
|
+
/>
|
|
74
|
+
<Stack.Screen
|
|
75
|
+
name="AnimDevice"
|
|
76
|
+
component={AnimationsDeviceScreen}
|
|
77
|
+
options={{ title: 'Exp 06 — Animations & Device', headerShown: false }}
|
|
78
|
+
/>
|
|
79
|
+
</Stack.Navigator>
|
|
80
|
+
</NavigationContainer>
|
|
81
|
+
);
|
|
82
|
+
}
|