@umituz/react-native-firebase 2.4.86 → 2.4.87
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/.agents/workflows/setup-firebase.md +161 -27
- package/package.json +1 -1
- package/src/domains/firestore/index.ts +1 -0
- package/src/domains/firestore/infrastructure/middleware/QueryDeduplicationMiddleware.ts +17 -5
- package/src/domains/firestore/presentation/hooks/useSmartFirestoreSnapshot.ts +17 -7
|
@@ -2,43 +2,177 @@
|
|
|
2
2
|
description: Sets up or updates the @umituz/react-native-firebase package in a React Native app.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
# Firebase Infrastructure Setup
|
|
5
|
+
# Firebase Infrastructure Setup Workflow
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This workflow provides automated setup for `@umituz/react-native-firebase` integration.
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
Just invoke this workflow when you want to:
|
|
12
|
+
- Install @umituz/react-native-firebase in a new project
|
|
13
|
+
- Update existing installation to latest version
|
|
14
|
+
- Configure Firebase credentials and initialization
|
|
15
|
+
- Set up optimal cost-saving configurations
|
|
8
16
|
|
|
9
17
|
## Step 1: Check and Update `package.json`
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
|
|
19
|
+
Analyze the project's `package.json`:
|
|
20
|
+
- Check if `@umituz/react-native-firebase` exists in dependencies
|
|
21
|
+
- Check version (current: 2.4.86)
|
|
22
|
+
- If missing: Run `npm install @umituz/react-native-firebase`
|
|
23
|
+
- If outdated: Run `npm install @umituz/react-native-firebase@latest`
|
|
14
24
|
|
|
15
25
|
## Step 2: Install Peer Dependencies
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
|
|
27
|
+
Install required peer dependencies:
|
|
28
|
+
|
|
29
|
+
### Core Dependencies
|
|
30
|
+
```bash
|
|
31
|
+
# Firebase SDK
|
|
32
|
+
npm install firebase
|
|
33
|
+
|
|
34
|
+
# State Management
|
|
35
|
+
npm install @umituz/react-native-design-system
|
|
36
|
+
|
|
37
|
+
# Query Library
|
|
38
|
+
npm install @tanstack/react-query
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### React Navigation (if using)
|
|
42
|
+
```bash
|
|
43
|
+
npm install @gorhom/portal
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Authentication Dependencies (if using social auth)
|
|
47
|
+
```bash
|
|
48
|
+
# For Expo projects
|
|
49
|
+
npx expo install expo-apple-authentication expo-auth-session expo-crypto expo-web-browser
|
|
50
|
+
|
|
51
|
+
# For bare React Native
|
|
52
|
+
npm install @react-native-firebase/app @react-native-firebase/auth
|
|
53
|
+
```
|
|
22
54
|
|
|
23
55
|
## Step 3: Check Environment Variables
|
|
24
|
-
|
|
56
|
+
|
|
57
|
+
Verify Firebase credentials are configured. Check for these environment variables:
|
|
58
|
+
|
|
59
|
+
**Required:**
|
|
60
|
+
- `EXPO_PUBLIC_FIREBASE_API_KEY`
|
|
61
|
+
- `EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN`
|
|
62
|
+
- `EXPO_PUBLIC_FIREBASE_PROJECT_ID`
|
|
63
|
+
|
|
64
|
+
**Optional:**
|
|
65
|
+
- `EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET`
|
|
66
|
+
- `EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID`
|
|
67
|
+
- `EXPO_PUBLIC_FIREBASE_APP_ID`
|
|
68
|
+
|
|
69
|
+
Check if `.env` or `.env.example` exists. If not, create `.env.example`:
|
|
70
|
+
```env
|
|
71
|
+
# Firebase Configuration
|
|
72
|
+
EXPO_PUBLIC_FIREBASE_API_KEY=your_api_key_here
|
|
73
|
+
EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
|
|
74
|
+
EXPO_PUBLIC_FIREBASE_PROJECT_ID=your-project-id
|
|
75
|
+
EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET=your-project.appspot.com
|
|
76
|
+
EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
|
|
77
|
+
EXPO_PUBLIC_FIREBASE_APP_ID=your_app_id
|
|
78
|
+
```
|
|
25
79
|
|
|
26
80
|
## Step 4: Setup Initialization Logic
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
81
|
+
|
|
82
|
+
Locate the main entry point (usually `App.tsx`, `index.js`, `app/_layout.tsx` for Expo Router).
|
|
83
|
+
|
|
84
|
+
Check if Firebase is initialized. If not, add initialization:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { autoInitializeFirebase } from '@umituz/react-native-firebase';
|
|
88
|
+
|
|
89
|
+
// Call initialization early in app lifecycle
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
autoInitializeFirebase();
|
|
92
|
+
}, []);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
For Expo Router (app/_layout.tsx):
|
|
96
|
+
```typescript
|
|
97
|
+
import { autoInitializeFirebase } from '@umituz/react-native-firebase';
|
|
98
|
+
|
|
99
|
+
export default function RootLayout() {
|
|
100
|
+
// Initialize Firebase when app starts
|
|
101
|
+
autoInitializeFirebase();
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<Stack>
|
|
105
|
+
<Stack.Screen name="(tabs)" options={{ headerShown: false }}>
|
|
106
|
+
{/* your screens */}
|
|
107
|
+
</Stack.Screen>
|
|
108
|
+
</Stack>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Step 5: Native Setup (Bare React Native Only)
|
|
114
|
+
|
|
115
|
+
If the project has an `ios/` folder (bare React Native):
|
|
39
116
|
```bash
|
|
40
117
|
cd ios && pod install
|
|
118
|
+
cd ..
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
For Android, no additional setup needed beyond Step 4.
|
|
122
|
+
|
|
123
|
+
## Step 6: Verify Setup
|
|
124
|
+
|
|
125
|
+
Run the app and verify:
|
|
126
|
+
- No Firebase initialization errors
|
|
127
|
+
- Firestore queries work
|
|
128
|
+
- Authentication works (if configured)
|
|
129
|
+
- Quota tracking is active (check __DEV__ logs)
|
|
130
|
+
|
|
131
|
+
## Step 7: Enable Cost Optimizations (Recommended)
|
|
132
|
+
|
|
133
|
+
For production apps, enable smart cost-saving features:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { useSmartFirestoreSnapshot } from '@umituz/react-native-firebase';
|
|
137
|
+
|
|
138
|
+
// Instead of useFirestoreSnapshot, use the smart version
|
|
139
|
+
const { data } = useSmartFirestoreSnapshot({
|
|
140
|
+
queryKey: ['my-data'],
|
|
141
|
+
subscribe: (onData) => onSnapshot(collection(db, 'data'), (snap) => {
|
|
142
|
+
onData(snap.docs.map(d => d.data()));
|
|
143
|
+
}),
|
|
144
|
+
backgroundStrategy: 'suspend', // Saves battery and data when app backgrounds
|
|
145
|
+
});
|
|
41
146
|
```
|
|
42
147
|
|
|
43
|
-
##
|
|
44
|
-
|
|
148
|
+
## Troubleshooting
|
|
149
|
+
|
|
150
|
+
**Issue:** "Firebase not initialized"
|
|
151
|
+
- **Solution:** Make sure `autoInitializeFirebase()` is called in app entry point
|
|
152
|
+
- **Solution:** Verify environment variables are set correctly
|
|
153
|
+
|
|
154
|
+
**Issue:** "Module not found: @umituz/react-native-design-system"
|
|
155
|
+
- **Solution:** Run `npm install @umituz/react-native-design-system`
|
|
156
|
+
|
|
157
|
+
**Issue:** "Expo router not found"
|
|
158
|
+
- **Solution:** This package works with any navigation, adjust import paths as needed
|
|
159
|
+
|
|
160
|
+
## Step 8: Summary
|
|
161
|
+
|
|
162
|
+
After setup, provide user with:
|
|
163
|
+
1. ✅ Packages installed/updated: [list versions]
|
|
164
|
+
2. ✅ Environment variables configured: [list keys]
|
|
165
|
+
3. ✅ Initialization added to: [file path]
|
|
166
|
+
4. ✅ Cost optimizations enabled: [smart snapshot, persistent cache, etc.]
|
|
167
|
+
5. ✅ Next steps: [initialize auth, setup Firestore, etc.]
|
|
168
|
+
|
|
169
|
+
## Additional Resources
|
|
170
|
+
|
|
171
|
+
- Documentation: See README.md for detailed API reference
|
|
172
|
+
- Examples: Check `/examples` folder (if exists)
|
|
173
|
+
- Support: Report issues on GitHub
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
**Last Updated:** 2025-03-18
|
|
177
|
+
**Package Version:** 2.4.86
|
|
178
|
+
**Platform:** React Native (Expo & Bare)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-firebase",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.87",
|
|
4
4
|
"description": "Unified Firebase package for React Native apps - Auth and Firestore services using Firebase JS SDK (no native modules).",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -30,9 +30,9 @@ const DEFAULT_CLEANUP_INTERVAL_MS = 15000; // 15s (was 3s)
|
|
|
30
30
|
* Quota-based window adjustment thresholds
|
|
31
31
|
*/
|
|
32
32
|
const QUOTA_THRESHOLDS = {
|
|
33
|
-
HIGH_USAGE: 0.80, // 80% - extend window to
|
|
33
|
+
HIGH_USAGE: 0.80, // 80% - extend window to 60s (1 min)
|
|
34
34
|
MEDIUM_USAGE: 0.60, // 60% - extend window to 20s
|
|
35
|
-
NORMAL: 0.
|
|
35
|
+
NORMAL: 0.50, // < 50% - use default 10s
|
|
36
36
|
} as const;
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -280,15 +280,21 @@ export const queryDeduplicationMiddleware = new QueryDeduplicationMiddleware({
|
|
|
280
280
|
});
|
|
281
281
|
|
|
282
282
|
/**
|
|
283
|
-
*
|
|
283
|
+
* Helper function to integrate deduplication with quota tracking
|
|
284
284
|
* Automatically adjusts window based on quota usage
|
|
285
285
|
*
|
|
286
|
+
* Note: This is NOT a React hook, but a helper function.
|
|
287
|
+
* Call this from your own hook or effect as needed.
|
|
288
|
+
*
|
|
286
289
|
* @example
|
|
287
290
|
* ```typescript
|
|
288
|
-
*
|
|
291
|
+
* // In your own hook or component:
|
|
292
|
+
* useEffect(() => {
|
|
293
|
+
* syncDeduplicationWithQuota(queryDeduplicationMiddleware, quotaMiddleware, quotaLimits);
|
|
294
|
+
* }, [quotaMiddleware.getCounts().reads]);
|
|
289
295
|
* ```
|
|
290
296
|
*/
|
|
291
|
-
export function
|
|
297
|
+
export function syncDeduplicationWithQuota(
|
|
292
298
|
deduplication: QueryDeduplicationMiddleware,
|
|
293
299
|
quotaMiddleware: { getCounts: () => { reads: number; writes: number; deletes: number } },
|
|
294
300
|
quotaLimits: { dailyReadLimit: number }
|
|
@@ -298,3 +304,9 @@ export function useDeduplicationWithQuota(
|
|
|
298
304
|
const quotaPercentage = counts.reads / quotaLimits.dailyReadLimit;
|
|
299
305
|
deduplication.adjustWindowForQuota(quotaPercentage);
|
|
300
306
|
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* @deprecated Use syncDeduplicationWithQuota instead (not a hook)
|
|
310
|
+
* This will be removed in a future version
|
|
311
|
+
*/
|
|
312
|
+
export const useDeduplicationWithQuota = syncDeduplicationWithQuota;
|
|
@@ -173,6 +173,8 @@ export function useSmartFirestoreSnapshot<TData>(
|
|
|
173
173
|
* Handle app state changes (foreground/background)
|
|
174
174
|
*/
|
|
175
175
|
useEffect(() => {
|
|
176
|
+
const timers: ReturnType<typeof setTimeout>[] = [];
|
|
177
|
+
|
|
176
178
|
const handleAppStateChange = (nextAppState: AppStateStatus) => {
|
|
177
179
|
const isBackgrounded = nextAppState.match(/inactive|background/);
|
|
178
180
|
|
|
@@ -190,12 +192,13 @@ export function useSmartFirestoreSnapshot<TData>(
|
|
|
190
192
|
|
|
191
193
|
switch (backgroundStrategy) {
|
|
192
194
|
case 'suspend':
|
|
193
|
-
// Suspend immediately
|
|
194
|
-
setTimeout(() => suspendListener(), 0);
|
|
195
|
+
// Suspend immediately - track timer for cleanup
|
|
196
|
+
const suspendTimer = setTimeout(() => suspendListener(), 0);
|
|
197
|
+
timers.push(suspendTimer);
|
|
195
198
|
break;
|
|
196
199
|
|
|
197
200
|
case 'timeout':
|
|
198
|
-
// Suspend after timeout
|
|
201
|
+
// Suspend after timeout - timer stored in state for cleanup
|
|
199
202
|
const timer = setTimeout(() => {
|
|
200
203
|
suspendListener();
|
|
201
204
|
}, backgroundTimeout);
|
|
@@ -215,10 +218,11 @@ export function useSmartFirestoreSnapshot<TData>(
|
|
|
215
218
|
console.log(`[SmartSnapshot] App entering foreground for query:`, queryKey);
|
|
216
219
|
}
|
|
217
220
|
|
|
218
|
-
// Resume listener (with optional delay)
|
|
219
|
-
setTimeout(() => {
|
|
221
|
+
// Resume listener (with optional delay) - track timer for cleanup
|
|
222
|
+
const resumeTimer = setTimeout(() => {
|
|
220
223
|
resumeListener();
|
|
221
224
|
}, resumeDelay);
|
|
225
|
+
timers.push(resumeTimer);
|
|
222
226
|
|
|
223
227
|
return { ...prev, isBackgrounded: false, suspendTimer: null };
|
|
224
228
|
}
|
|
@@ -231,6 +235,8 @@ export function useSmartFirestoreSnapshot<TData>(
|
|
|
231
235
|
|
|
232
236
|
return () => {
|
|
233
237
|
subscription.remove();
|
|
238
|
+
// Clean up any pending timers
|
|
239
|
+
timers.forEach(timer => clearTimeout(timer));
|
|
234
240
|
};
|
|
235
241
|
}, [queryKey, backgroundStrategy, backgroundTimeout, resumeDelay, suspendListener, resumeListener]);
|
|
236
242
|
|
|
@@ -343,9 +349,13 @@ export function useSmartListenerControl() {
|
|
|
343
349
|
return () => subscription.remove();
|
|
344
350
|
}, []);
|
|
345
351
|
|
|
352
|
+
// Compute background status once to avoid duplicate regex matching
|
|
353
|
+
const isBackgrounded = appState.match(/inactive|background/);
|
|
354
|
+
const isForegrounded = !isBackgrounded;
|
|
355
|
+
|
|
346
356
|
return {
|
|
347
|
-
isBackgrounded
|
|
348
|
-
isForegrounded
|
|
357
|
+
isBackgrounded,
|
|
358
|
+
isForegrounded,
|
|
349
359
|
appState,
|
|
350
360
|
};
|
|
351
361
|
}
|