@umituz/react-native-ai-generation-content 1.12.36 → 1.12.37

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.12.36",
3
+ "version": "1.12.37",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -0,0 +1,79 @@
1
+ import { getDocs, getDoc, query, orderBy } from "firebase/firestore";
2
+ import type { DocumentMapper } from "../../domain/value-objects/CreationsConfig";
3
+ import type { Creation, CreationDocument } from "../../domain/entities/Creation";
4
+ import type { FirestorePathResolver } from "./FirestorePathResolver";
5
+
6
+ declare const __DEV__: boolean;
7
+
8
+ /**
9
+ * Handles fetching creations from Firestore
10
+ * Single Responsibility: Read operations
11
+ */
12
+ export class CreationsFetcher {
13
+ constructor(
14
+ private readonly pathResolver: FirestorePathResolver,
15
+ private readonly documentMapper: DocumentMapper,
16
+ ) { }
17
+
18
+ async getAll(userId: string): Promise<Creation[]> {
19
+ if (__DEV__) {
20
+ // eslint-disable-next-line no-console
21
+ console.log("[CreationsRepository] getAll()", { userId });
22
+ }
23
+
24
+ const userCollection = this.pathResolver.getUserCollection(userId);
25
+ if (!userCollection) return [];
26
+
27
+ try {
28
+ const q = query(userCollection, orderBy("createdAt", "desc"));
29
+ const snapshot = await getDocs(q);
30
+
31
+ if (__DEV__) {
32
+ // eslint-disable-next-line no-console
33
+ console.log("[CreationsRepository] Fetched:", snapshot.docs.length);
34
+ }
35
+
36
+ return snapshot.docs.map((docSnap) => {
37
+ const data = docSnap.data() as CreationDocument;
38
+ return this.documentMapper(docSnap.id, data);
39
+ });
40
+ } catch (error) {
41
+ if (__DEV__) {
42
+ // eslint-disable-next-line no-console
43
+ console.error("[CreationsRepository] getAll() ERROR", error);
44
+ }
45
+ return [];
46
+ }
47
+ }
48
+
49
+ async getById(userId: string, id: string): Promise<Creation | null> {
50
+ if (__DEV__) {
51
+ // eslint-disable-next-line no-console
52
+ console.log("[CreationsRepository] getById()", { userId, id });
53
+ }
54
+
55
+ const docRef = this.pathResolver.getDocRef(userId, id);
56
+ if (!docRef) return null;
57
+
58
+ try {
59
+ const docSnap = await getDoc(docRef);
60
+
61
+ if (!docSnap.exists()) {
62
+ if (__DEV__) {
63
+ // eslint-disable-next-line no-console
64
+ console.log("[CreationsRepository] Document not found");
65
+ }
66
+ return null;
67
+ }
68
+
69
+ const data = docSnap.data() as CreationDocument;
70
+ return this.documentMapper(docSnap.id, data);
71
+ } catch (error) {
72
+ if (__DEV__) {
73
+ // eslint-disable-next-line no-console
74
+ console.error("[CreationsRepository] getById() ERROR", error);
75
+ }
76
+ return null;
77
+ }
78
+ }
79
+ }
@@ -1,54 +1,21 @@
1
- /**
2
- * Creations Repository Implementation
3
- * Extends BaseRepository from @umituz/react-native-firestore
4
- *
5
- * Architecture:
6
- * - Extends BaseRepository for centralized database access
7
- * - Fully dynamic path structure (configurable per app)
8
- * - Fully dynamic document mapping (configurable per app)
9
- * - App-agnostic: Works with any app, no app-specific code
10
- *
11
- * This class is designed to be used across hundreds of apps.
12
- */
13
-
14
- declare const __DEV__: boolean;
15
-
16
- import {
17
- collection,
18
- doc,
19
- getDocs,
20
- getDoc,
21
- deleteDoc,
22
- updateDoc,
23
- query,
24
- orderBy,
25
- setDoc,
26
- } from "firebase/firestore";
27
1
  import { BaseRepository } from "@umituz/react-native-firebase";
28
2
  import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
29
- import type { Creation, CreationDocument } from "../../domain/entities/Creation";
3
+ import type { Creation } from "../../domain/entities/Creation";
30
4
  import { mapDocumentToCreation } from "../../domain/entities/Creation";
31
5
  import type {
32
6
  PathBuilder,
33
7
  DocumentMapper,
34
8
  } from "../../domain/value-objects/CreationsConfig";
9
+ import { FirestorePathResolver } from "./FirestorePathResolver";
10
+ import { CreationsFetcher } from "./CreationsFetcher";
11
+ import { CreationsWriter } from "./CreationsWriter";
35
12
 
36
13
  /**
37
14
  * Repository options for dynamic configuration
38
15
  * Apps can customize path structure and document mapping
39
16
  */
40
17
  export interface RepositoryOptions {
41
- /**
42
- * Custom path builder function
43
- * @example (userId) => ["users", userId, "photos"]
44
- * @example (userId) => ["creations", userId, "items"]
45
- */
46
18
  readonly pathBuilder?: PathBuilder;
47
-
48
- /**
49
- * Custom document mapper function
50
- * Maps Firestore documents to Creation entity
51
- */
52
19
  readonly documentMapper?: DocumentMapper;
53
20
  }
54
21
 
@@ -60,112 +27,48 @@ const createDefaultPathBuilder =
60
27
  (userId: string) =>
61
28
  ["users", userId, collectionName];
62
29
 
30
+ /**
31
+ * Creations Repository Implementation
32
+ * Delegates to specialized classes for different responsibilities
33
+ *
34
+ * Architecture:
35
+ * - Extends BaseRepository for centralized database access
36
+ * - Uses FirestorePathResolver for path resolution
37
+ * - Uses CreationsFetcher for read operations
38
+ * - Uses CreationsWriter for write operations
39
+ * - Fully dynamic and configurable per app
40
+ */
63
41
  export class CreationsRepository
64
42
  extends BaseRepository
65
43
  implements ICreationsRepository {
66
- private readonly pathBuilder: PathBuilder;
67
- private readonly documentMapper: DocumentMapper;
44
+ private readonly pathResolver: FirestorePathResolver;
45
+ private readonly fetcher: CreationsFetcher;
46
+ private readonly writer: CreationsWriter;
68
47
 
69
48
  constructor(
70
49
  private readonly _collectionName: string,
71
50
  options?: RepositoryOptions,
72
51
  ) {
73
52
  super();
74
- this.pathBuilder =
75
- options?.pathBuilder ?? createDefaultPathBuilder(_collectionName);
76
- this.documentMapper = options?.documentMapper ?? mapDocumentToCreation;
77
- }
78
53
 
79
- private getUserCollection(userId: string) {
80
- const db = this.getDb();
81
- if (!db) return null;
82
- const pathSegments = this.pathBuilder(userId);
83
- return collection(db, pathSegments[0], ...pathSegments.slice(1));
84
- }
54
+ const pathBuilder = options?.pathBuilder ?? createDefaultPathBuilder(_collectionName);
55
+ const documentMapper = options?.documentMapper ?? mapDocumentToCreation;
85
56
 
86
- private getDocRef(userId: string, creationId: string) {
87
- const db = this.getDb();
88
- if (!db) return null;
89
- const pathSegments = this.pathBuilder(userId);
90
- return doc(db, pathSegments[0], ...pathSegments.slice(1), creationId);
57
+ this.pathResolver = new FirestorePathResolver(pathBuilder, this.getDb());
58
+ this.fetcher = new CreationsFetcher(this.pathResolver, documentMapper);
59
+ this.writer = new CreationsWriter(this.pathResolver);
91
60
  }
92
61
 
93
62
  async getAll(userId: string): Promise<Creation[]> {
94
- if (__DEV__) {
95
- // eslint-disable-next-line no-console
96
- console.log("[CreationsRepository] getAll()", { userId });
97
- }
98
-
99
- const userCollection = this.getUserCollection(userId);
100
- if (!userCollection) return [];
101
-
102
- try {
103
- const q = query(userCollection, orderBy("createdAt", "desc"));
104
- const snapshot = await getDocs(q);
105
-
106
- if (__DEV__) {
107
- // eslint-disable-next-line no-console
108
- console.log("[CreationsRepository] Fetched:", snapshot.docs.length);
109
- }
110
-
111
- return snapshot.docs.map((docSnap) => {
112
- const data = docSnap.data() as CreationDocument;
113
- return this.documentMapper(docSnap.id, data);
114
- });
115
- } catch (error) {
116
- if (__DEV__) {
117
- // eslint-disable-next-line no-console
118
- console.error("[CreationsRepository] getAll() ERROR", error);
119
- }
120
- return [];
121
- }
63
+ return this.fetcher.getAll(userId);
122
64
  }
123
65
 
124
66
  async getById(userId: string, id: string): Promise<Creation | null> {
125
- if (__DEV__) {
126
- // eslint-disable-next-line no-console
127
- console.log("[CreationsRepository] getById()", { userId, id });
128
- }
129
-
130
- const docRef = this.getDocRef(userId, id);
131
- if (!docRef) return null;
132
-
133
- try {
134
- const docSnap = await getDoc(docRef);
135
-
136
- if (!docSnap.exists()) {
137
- if (__DEV__) {
138
- // eslint-disable-next-line no-console
139
- console.log("[CreationsRepository] Document not found");
140
- }
141
- return null;
142
- }
143
-
144
- const data = docSnap.data() as CreationDocument;
145
- return this.documentMapper(docSnap.id, data);
146
- } catch (error) {
147
- if (__DEV__) {
148
- // eslint-disable-next-line no-console
149
- console.error("[CreationsRepository] getById() ERROR", error);
150
- }
151
- return null;
152
- }
67
+ return this.fetcher.getById(userId, id);
153
68
  }
154
69
 
155
70
  async create(userId: string, creation: Creation): Promise<void> {
156
- const docRef = this.getDocRef(userId, creation.id);
157
- if (!docRef) throw new Error("Firestore not initialized");
158
-
159
- const data: CreationDocument = {
160
- type: creation.type,
161
- prompt: creation.prompt,
162
- uri: creation.uri, // Use uri
163
- createdAt: creation.createdAt,
164
- metadata: creation.metadata || {},
165
- isShared: creation.isShared || false,
166
- };
167
-
168
- await setDoc(docRef, data);
71
+ return this.writer.create(userId, creation);
169
72
  }
170
73
 
171
74
  async update(
@@ -173,54 +76,11 @@ export class CreationsRepository
173
76
  id: string,
174
77
  updates: Partial<Creation>,
175
78
  ): Promise<boolean> {
176
- if (__DEV__) {
177
- // eslint-disable-next-line no-console
178
- console.log("[CreationsRepository] update()", { userId, id, updates });
179
- }
180
-
181
- const docRef = this.getDocRef(userId, id);
182
- if (!docRef) return false;
183
-
184
- try {
185
- const updateData: Record<string, unknown> = {};
186
-
187
- if (updates.metadata !== undefined) {
188
- updateData.metadata = updates.metadata;
189
- }
190
- if (updates.isShared !== undefined) {
191
- updateData.isShared = updates.isShared;
192
- }
193
- if (updates.uri !== undefined) {
194
- updateData.uri = updates.uri;
195
- }
196
- if (updates.type !== undefined) {
197
- updateData.type = updates.type;
198
- }
199
- if (updates.prompt !== undefined) {
200
- updateData.prompt = updates.prompt;
201
- }
202
-
203
- await updateDoc(docRef, updateData);
204
- return true;
205
- } catch (error) {
206
- if (__DEV__) {
207
- // eslint-disable-next-line no-console
208
- console.error("[CreationsRepository] update() ERROR", error);
209
- }
210
- return false;
211
- }
79
+ return this.writer.update(userId, id, updates);
212
80
  }
213
81
 
214
82
  async delete(userId: string, creationId: string): Promise<boolean> {
215
- const docRef = this.getDocRef(userId, creationId);
216
- if (!docRef) return false;
217
-
218
- try {
219
- await deleteDoc(docRef);
220
- return true;
221
- } catch {
222
- return false;
223
- }
83
+ return this.writer.delete(userId, creationId);
224
84
  }
225
85
 
226
86
  async updateShared(
@@ -228,15 +88,7 @@ export class CreationsRepository
228
88
  creationId: string,
229
89
  isShared: boolean,
230
90
  ): Promise<boolean> {
231
- const docRef = this.getDocRef(userId, creationId);
232
- if (!docRef) return false;
233
-
234
- try {
235
- await updateDoc(docRef, { isShared });
236
- return true;
237
- } catch {
238
- return false;
239
- }
91
+ return this.writer.updateShared(userId, creationId, isShared);
240
92
  }
241
93
 
242
94
  async updateFavorite(
@@ -244,14 +96,6 @@ export class CreationsRepository
244
96
  creationId: string,
245
97
  isFavorite: boolean,
246
98
  ): Promise<boolean> {
247
- const docRef = this.getDocRef(userId, creationId);
248
- if (!docRef) return false;
249
-
250
- try {
251
- await updateDoc(docRef, { isFavorite });
252
- return true;
253
- } catch {
254
- return false;
255
- }
99
+ return this.writer.updateFavorite(userId, creationId, isFavorite);
256
100
  }
257
101
  }
@@ -0,0 +1,117 @@
1
+ import { setDoc, updateDoc, deleteDoc } from "firebase/firestore";
2
+ import type { Creation, CreationDocument } from "../../domain/entities/Creation";
3
+ import type { FirestorePathResolver } from "./FirestorePathResolver";
4
+
5
+ declare const __DEV__: boolean;
6
+
7
+ /**
8
+ * Handles write operations for creations
9
+ * Single Responsibility: Write operations
10
+ */
11
+ export class CreationsWriter {
12
+ constructor(private readonly pathResolver: FirestorePathResolver) { }
13
+
14
+ async create(userId: string, creation: Creation): Promise<void> {
15
+ const docRef = this.pathResolver.getDocRef(userId, creation.id);
16
+ if (!docRef) throw new Error("Firestore not initialized");
17
+
18
+ const data: CreationDocument = {
19
+ type: creation.type,
20
+ prompt: creation.prompt,
21
+ uri: creation.uri,
22
+ createdAt: creation.createdAt,
23
+ metadata: creation.metadata || {},
24
+ isShared: creation.isShared || false,
25
+ isFavorite: creation.isFavorite || false,
26
+ };
27
+
28
+ await setDoc(docRef, data);
29
+ }
30
+
31
+ async update(
32
+ userId: string,
33
+ id: string,
34
+ updates: Partial<Creation>,
35
+ ): Promise<boolean> {
36
+ if (__DEV__) {
37
+ // eslint-disable-next-line no-console
38
+ console.log("[CreationsRepository] update()", { userId, id, updates });
39
+ }
40
+
41
+ const docRef = this.pathResolver.getDocRef(userId, id);
42
+ if (!docRef) return false;
43
+
44
+ try {
45
+ const updateData: Record<string, unknown> = {};
46
+
47
+ if (updates.metadata !== undefined) {
48
+ updateData.metadata = updates.metadata;
49
+ }
50
+ if (updates.isShared !== undefined) {
51
+ updateData.isShared = updates.isShared;
52
+ }
53
+ if (updates.uri !== undefined) {
54
+ updateData.uri = updates.uri;
55
+ }
56
+ if (updates.type !== undefined) {
57
+ updateData.type = updates.type;
58
+ }
59
+ if (updates.prompt !== undefined) {
60
+ updateData.prompt = updates.prompt;
61
+ }
62
+
63
+ await updateDoc(docRef, updateData);
64
+ return true;
65
+ } catch (error) {
66
+ if (__DEV__) {
67
+ // eslint-disable-next-line no-console
68
+ console.error("[CreationsRepository] update() ERROR", error);
69
+ }
70
+ return false;
71
+ }
72
+ }
73
+
74
+ async delete(userId: string, creationId: string): Promise<boolean> {
75
+ const docRef = this.pathResolver.getDocRef(userId, creationId);
76
+ if (!docRef) return false;
77
+
78
+ try {
79
+ await deleteDoc(docRef);
80
+ return true;
81
+ } catch {
82
+ return false;
83
+ }
84
+ }
85
+
86
+ async updateShared(
87
+ userId: string,
88
+ creationId: string,
89
+ isShared: boolean,
90
+ ): Promise<boolean> {
91
+ const docRef = this.pathResolver.getDocRef(userId, creationId);
92
+ if (!docRef) return false;
93
+
94
+ try {
95
+ await updateDoc(docRef, { isShared });
96
+ return true;
97
+ } catch {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ async updateFavorite(
103
+ userId: string,
104
+ creationId: string,
105
+ isFavorite: boolean,
106
+ ): Promise<boolean> {
107
+ const docRef = this.pathResolver.getDocRef(userId, creationId);
108
+ if (!docRef) return false;
109
+
110
+ try {
111
+ await updateDoc(docRef, { isFavorite });
112
+ return true;
113
+ } catch {
114
+ return false;
115
+ }
116
+ }
117
+ }
@@ -0,0 +1,26 @@
1
+ import type { Firestore } from "firebase/firestore";
2
+ import { collection, doc } from "firebase/firestore";
3
+ import type { PathBuilder } from "../../domain/value-objects/CreationsConfig";
4
+
5
+ /**
6
+ * Resolves Firestore paths for creations
7
+ * Single Responsibility: Path resolution
8
+ */
9
+ export class FirestorePathResolver {
10
+ constructor(
11
+ private readonly pathBuilder: PathBuilder,
12
+ private readonly db: Firestore | null,
13
+ ) { }
14
+
15
+ getUserCollection(userId: string) {
16
+ if (!this.db) return null;
17
+ const pathSegments = this.pathBuilder(userId);
18
+ return collection(this.db, pathSegments[0], ...pathSegments.slice(1));
19
+ }
20
+
21
+ getDocRef(userId: string, creationId: string) {
22
+ if (!this.db) return null;
23
+ const pathSegments = this.pathBuilder(userId);
24
+ return doc(this.db, pathSegments[0], ...pathSegments.slice(1), creationId);
25
+ }
26
+ }
@@ -98,12 +98,14 @@ export function CreationsGalleryScreen({
98
98
  }, []);
99
99
 
100
100
  // Handle favorite toggle
101
- const handleFavorite = useCallback(async (creation: Creation, isFavorite: boolean) => {
102
- if (!userId) return;
103
- const success = await repository.updateFavorite(userId, creation.id, isFavorite);
104
- if (success) {
105
- void refetch();
106
- }
101
+ const handleFavorite = useCallback((creation: Creation, isFavorite: boolean) => {
102
+ void (async () => {
103
+ if (!userId) return;
104
+ const success = await repository.updateFavorite(userId, creation.id, isFavorite);
105
+ if (success) {
106
+ void refetch();
107
+ }
108
+ })();
107
109
  }, [userId, repository, refetch]);
108
110
 
109
111
  const styles = useStyles(tokens);