@umituz/react-native-settings 5.3.52 → 5.3.53

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.
@@ -2,6 +2,10 @@
2
2
  * useFeatureRequests Hook
3
3
  * Internal hook — fetches, votes, submits feature requests via Firestore
4
4
  * Works with both authenticated and anonymous users
5
+ *
6
+ * FIREBASE LAZY LOADING:
7
+ * Firebase is lazy-loaded to avoid hard dependency.
8
+ * If @umituz/react-native-firebase is not installed, features are disabled gracefully.
5
9
  */
6
10
  import type { FeatureRequestItem, VoteType } from "../domain/entities/FeatureRequestEntity";
7
11
  export interface UseFeatureRequestsResult {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-settings",
3
- "version": "5.3.52",
3
+ "version": "5.3.53",
4
4
  "description": "Complete settings hub for React Native apps - consolidated package with settings, localization, about, legal, appearance, feedback, FAQs, rating, and gamification",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./dist/index.d.ts",
@@ -140,6 +140,9 @@
140
140
  "peerDependenciesMeta": {
141
141
  "@umituz/react-native-auth": {
142
142
  "optional": true
143
+ },
144
+ "@umituz/react-native-firebase": {
145
+ "optional": true
143
146
  }
144
147
  },
145
148
  "devDependencies": {
@@ -2,27 +2,16 @@
2
2
  * useFeatureRequests Hook
3
3
  * Internal hook — fetches, votes, submits feature requests via Firestore
4
4
  * Works with both authenticated and anonymous users
5
+ *
6
+ * FIREBASE LAZY LOADING:
7
+ * Firebase is lazy-loaded to avoid hard dependency.
8
+ * If @umituz/react-native-firebase is not installed, features are disabled gracefully.
5
9
  */
6
10
 
7
11
  import { useState, useEffect, useCallback, useRef } from "react";
8
12
  import { Platform } from "react-native";
9
13
  import * as Crypto from "expo-crypto";
10
14
  import AsyncStorage from "@react-native-async-storage/async-storage";
11
- import {
12
- collection,
13
- doc,
14
- getDocs,
15
- getDoc,
16
- setDoc,
17
- addDoc,
18
- updateDoc,
19
- deleteDoc,
20
- increment,
21
- orderBy,
22
- query,
23
- serverTimestamp,
24
- } from "firebase/firestore";
25
- import { getFirestore } from "@umituz/react-native-firebase";
26
15
  import type {
27
16
  FeatureRequestItem,
28
17
  FeatureRequestStatus,
@@ -32,6 +21,59 @@ import type {
32
21
  const COLLECTION = "feature_requests";
33
22
  const ANONYMOUS_USER_ID_KEY = "@anonymous_user_id";
34
23
 
24
+ // Lazy-loaded Firebase modules
25
+ let firebaseModules: {
26
+ getFirestore: () => any;
27
+ collection: any;
28
+ doc: any;
29
+ getDocs: any;
30
+ getDoc: any;
31
+ setDoc: any;
32
+ addDoc: any;
33
+ updateDoc: any;
34
+ deleteDoc: any;
35
+ increment: any;
36
+ orderBy: any;
37
+ query: any;
38
+ serverTimestamp: any;
39
+ } | null = null;
40
+
41
+ function loadFirebase(): typeof firebaseModules {
42
+ if (firebaseModules !== null) return firebaseModules;
43
+
44
+ try {
45
+ // Lazy load Firebase only when needed
46
+ const firebaseModule = require("@umituz/react-native-firebase");
47
+ const firestoreModule = require("firebase/firestore");
48
+
49
+ firebaseModules = {
50
+ getFirestore: firebaseModule.getFirestore,
51
+ collection: firestoreModule.collection,
52
+ doc: firestoreModule.doc,
53
+ getDocs: firestoreModule.getDocs,
54
+ getDoc: firestoreModule.getDoc,
55
+ setDoc: firestoreModule.setDoc,
56
+ addDoc: firestoreModule.addDoc,
57
+ updateDoc: firestoreModule.updateDoc,
58
+ deleteDoc: firestoreModule.deleteDoc,
59
+ increment: firestoreModule.increment,
60
+ orderBy: firestoreModule.orderBy,
61
+ query: firestoreModule.query,
62
+ serverTimestamp: firestoreModule.serverTimestamp,
63
+ };
64
+ return firebaseModules;
65
+ } catch (error) {
66
+ // Firebase not installed - return null to disable features
67
+ if (__DEV__) {
68
+ console.warn(
69
+ "[useFeatureRequests] @umituz/react-native-firebase not installed. Feature requests disabled.",
70
+ );
71
+ }
72
+ firebaseModules = null;
73
+ return null;
74
+ }
75
+ }
76
+
35
77
  /**
36
78
  * Get or create a persistent anonymous user ID
37
79
  */
@@ -88,7 +130,13 @@ export function useFeatureRequests(): UseFeatureRequestsResult {
88
130
  const votingInProgress = useRef(new Set<string>());
89
131
 
90
132
  const fetchAll = useCallback(async () => {
91
- const db = getFirestore();
133
+ const fb = loadFirebase();
134
+ if (!fb) {
135
+ setIsLoading(false);
136
+ return;
137
+ }
138
+
139
+ const db = fb.getFirestore();
92
140
  if (!db) {
93
141
  setIsLoading(false);
94
142
  return;
@@ -97,8 +145,8 @@ export function useFeatureRequests(): UseFeatureRequestsResult {
97
145
  try {
98
146
  setIsLoading(true);
99
147
 
100
- const q = query(collection(db, COLLECTION), orderBy("votes", "desc"));
101
- const snapshot = await getDocs(q);
148
+ const q = fb.query(fb.collection(db, COLLECTION), fb.orderBy("votes", "desc"));
149
+ const snapshot = await fb.getDocs(q);
102
150
 
103
151
  const items: FeatureRequestItem[] = snapshot.docs.map((d) => {
104
152
  const data = d.data();
@@ -123,8 +171,8 @@ export function useFeatureRequests(): UseFeatureRequestsResult {
123
171
  // Fetch user votes in parallel (not N+1 sequential)
124
172
  if (userId && snapshot.docs.length > 0) {
125
173
  const votePromises = snapshot.docs.map(async (reqDoc) => {
126
- const voteRef = doc(db, COLLECTION, reqDoc.id, "votes", userId);
127
- const voteSnap = await getDoc(voteRef);
174
+ const voteRef = fb.doc(db, COLLECTION, reqDoc.id, "votes", userId);
175
+ const voteSnap = await fb.getDoc(voteRef);
128
176
  if (voteSnap.exists()) {
129
177
  return [reqDoc.id, voteSnap.data().type as VoteType] as const;
130
178
  }
@@ -152,13 +200,14 @@ export function useFeatureRequests(): UseFeatureRequestsResult {
152
200
  }, [fetchAll]);
153
201
 
154
202
  const vote = useCallback(async (requestId: string, type: VoteType) => {
155
- if (!userId) return;
203
+ const fb = loadFirebase();
204
+ if (!fb || !userId) return;
156
205
 
157
206
  // Prevent rapid double-tap on the same request
158
207
  if (votingInProgress.current.has(requestId)) return;
159
208
  votingInProgress.current.add(requestId);
160
209
 
161
- const db = getFirestore();
210
+ const db = fb.getFirestore();
162
211
  if (!db) {
163
212
  votingInProgress.current.delete(requestId);
164
213
  return;
@@ -186,22 +235,22 @@ export function useFeatureRequests(): UseFeatureRequestsResult {
186
235
  );
187
236
 
188
237
  try {
189
- const voteRef = doc(db, COLLECTION, requestId, "votes", userId);
190
- const requestRef = doc(db, COLLECTION, requestId);
191
- const existing = await getDoc(voteRef);
238
+ const voteRef = fb.doc(db, COLLECTION, requestId, "votes", userId);
239
+ const requestRef = fb.doc(db, COLLECTION, requestId);
240
+ const existing = await fb.getDoc(voteRef);
192
241
 
193
242
  if (existing.exists()) {
194
243
  const prev = existing.data().type as VoteType;
195
244
  if (prev === type) {
196
- await deleteDoc(voteRef);
197
- await updateDoc(requestRef, { votes: increment(type === "up" ? -1 : 1) });
245
+ await fb.deleteDoc(voteRef);
246
+ await fb.updateDoc(requestRef, { votes: fb.increment(type === "up" ? -1 : 1) });
198
247
  } else {
199
- await setDoc(voteRef, { type, votedAt: serverTimestamp() });
200
- await updateDoc(requestRef, { votes: increment(type === "up" ? 2 : -2) });
248
+ await fb.setDoc(voteRef, { type, votedAt: fb.serverTimestamp() });
249
+ await fb.updateDoc(requestRef, { votes: fb.increment(type === "up" ? 2 : -2) });
201
250
  }
202
251
  } else {
203
- await setDoc(voteRef, { type, votedAt: serverTimestamp() });
204
- await updateDoc(requestRef, { votes: increment(type === "up" ? 1 : -1) });
252
+ await fb.setDoc(voteRef, { type, votedAt: fb.serverTimestamp() });
253
+ await fb.updateDoc(requestRef, { votes: fb.increment(type === "up" ? 1 : -1) });
205
254
  }
206
255
  } catch (error) {
207
256
  if (__DEV__) console.warn("[useFeatureRequests] Vote failed:", error);
@@ -218,12 +267,14 @@ export function useFeatureRequests(): UseFeatureRequestsResult {
218
267
  }, [userId, fetchAll]);
219
268
 
220
269
  const submitRequest = useCallback(async (data: { title: string; description: string; type: string; rating?: number }) => {
221
- const db = getFirestore();
270
+ const fb = loadFirebase();
271
+ if (!fb) throw new Error("Firestore not available");
272
+ const db = fb.getFirestore();
222
273
  if (!db) throw new Error("Firestore not available");
223
274
  if (!userId) throw new Error("User ID not available");
224
275
 
225
276
  // Create the feature request
226
- const docRef = await addDoc(collection(db, COLLECTION), {
277
+ const docRef = await fb.addDoc(fb.collection(db, COLLECTION), {
227
278
  title: data.title,
228
279
  description: data.description,
229
280
  type: data.type || "feature_request",
@@ -234,14 +285,14 @@ export function useFeatureRequests(): UseFeatureRequestsResult {
234
285
  isAnonymous: true,
235
286
  platform: Platform.OS,
236
287
  rating: data.rating ?? null,
237
- createdAt: serverTimestamp(),
238
- updatedAt: serverTimestamp(),
288
+ createdAt: fb.serverTimestamp(),
289
+ updatedAt: fb.serverTimestamp(),
239
290
  });
240
291
 
241
292
  // Create the creator's upvote doc so votes count matches reality
242
- await setDoc(doc(db, COLLECTION, docRef.id, "votes", userId), {
293
+ await fb.setDoc(fb.doc(db, COLLECTION, docRef.id, "votes", userId), {
243
294
  type: "up" as VoteType,
244
- votedAt: serverTimestamp(),
295
+ votedAt: fb.serverTimestamp(),
245
296
  });
246
297
 
247
298
  await fetchAll();