@umituz/react-native-ai-generation-content 1.12.36 → 1.12.38
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 +2 -2
- package/src/domains/creations/domain/value-objects/CreationsConfig.ts +0 -12
- package/src/domains/creations/infrastructure/adapters/createRepository.ts +3 -8
- package/src/domains/creations/infrastructure/repositories/CreationsFetcher.ts +79 -0
- package/src/domains/creations/infrastructure/repositories/CreationsRepository.ts +30 -200
- package/src/domains/creations/infrastructure/repositories/CreationsWriter.ts +117 -0
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +8 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.38",
|
|
4
4
|
"description": "Provider-agnostic AI generation orchestration for React Native",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"@typescript-eslint/parser": "^7.0.0",
|
|
69
69
|
"@umituz/react-native-animation": "*",
|
|
70
70
|
"@umituz/react-native-design-system": "^2.3.33",
|
|
71
|
-
"@umituz/react-native-firebase": "
|
|
71
|
+
"@umituz/react-native-firebase": "^1.13.20",
|
|
72
72
|
"@umituz/react-native-haptics": "^1.0.2",
|
|
73
73
|
"@umituz/react-native-image": "*",
|
|
74
74
|
"@umituz/react-native-offline": "*",
|
|
@@ -25,17 +25,6 @@ export interface CreationsTranslations {
|
|
|
25
25
|
readonly filterTitle: string;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
/**
|
|
29
|
-
* Path builder function type
|
|
30
|
-
* Allows apps to define custom Firestore path structures
|
|
31
|
-
* @example
|
|
32
|
-
* // Default: users/{userId}/creations
|
|
33
|
-
* pathBuilder: (userId) => ["users", userId, "creations"]
|
|
34
|
-
* // Alternative: creations/{userId}/items
|
|
35
|
-
* pathBuilder: (userId) => ["creations", userId, "items"]
|
|
36
|
-
*/
|
|
37
|
-
export type PathBuilder = (userId: string) => string[];
|
|
38
|
-
|
|
39
28
|
/**
|
|
40
29
|
* Document mapper function type
|
|
41
30
|
* Allows apps to map their specific document structure to Creation
|
|
@@ -49,7 +38,6 @@ export interface CreationsConfig {
|
|
|
49
38
|
readonly translations: CreationsTranslations;
|
|
50
39
|
readonly maxThumbnails?: number;
|
|
51
40
|
readonly gridColumns?: number;
|
|
52
|
-
readonly pathBuilder?: PathBuilder;
|
|
53
41
|
readonly documentMapper?: DocumentMapper;
|
|
54
42
|
}
|
|
55
43
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Architecture:
|
|
6
6
|
* - Factory pattern for repository creation
|
|
7
|
-
* -
|
|
7
|
+
* - Standard path: users/{userId}/{collectionName}
|
|
8
8
|
* - Supports custom document mapping per app
|
|
9
9
|
* - App-agnostic: No Firestore instance needed (BaseRepository handles it)
|
|
10
10
|
*
|
|
@@ -25,16 +25,10 @@ import type { ICreationsRepository } from "../../domain/repositories/ICreationsR
|
|
|
25
25
|
* @returns ICreationsRepository instance
|
|
26
26
|
*
|
|
27
27
|
* @example
|
|
28
|
-
* // Basic usage
|
|
28
|
+
* // Basic usage (path: users/{userId}/photos)
|
|
29
29
|
* const repo = createCreationsRepository("photos");
|
|
30
30
|
*
|
|
31
31
|
* @example
|
|
32
|
-
* // Custom path structure
|
|
33
|
-
* const repo = createCreationsRepository("creations", {
|
|
34
|
-
* pathBuilder: (userId) => ["gallery", userId, "items"],
|
|
35
|
-
* });
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
32
|
* // Custom document mapper
|
|
39
33
|
* const repo = createCreationsRepository("photos", {
|
|
40
34
|
* documentMapper: (id, data) => ({
|
|
@@ -43,6 +37,7 @@ import type { ICreationsRepository } from "../../domain/repositories/ICreationsR
|
|
|
43
37
|
* type: data.category,
|
|
44
38
|
* createdAt: data.timestamp?.toDate() || new Date(),
|
|
45
39
|
* isShared: data.public ?? false,
|
|
40
|
+
* isFavorite: data.favorite ?? false,
|
|
46
41
|
* }),
|
|
47
42
|
* });
|
|
48
43
|
*/
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { getDocs, getDoc, query, orderBy } from "firebase/firestore";
|
|
2
|
+
import { type FirestorePathResolver } from "@umituz/react-native-firebase";
|
|
3
|
+
import type { DocumentMapper } from "../../domain/value-objects/CreationsConfig";
|
|
4
|
+
import type { Creation, CreationDocument } from "../../domain/entities/Creation";
|
|
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,171 +1,60 @@
|
|
|
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
|
-
import { BaseRepository } from "@umituz/react-native-firebase";
|
|
1
|
+
import { BaseRepository, FirestorePathResolver } from "@umituz/react-native-firebase";
|
|
28
2
|
import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
|
|
29
|
-
import type { Creation
|
|
3
|
+
import type { Creation } from "../../domain/entities/Creation";
|
|
30
4
|
import { mapDocumentToCreation } from "../../domain/entities/Creation";
|
|
31
|
-
import type {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
} from "../../domain/value-objects/CreationsConfig";
|
|
5
|
+
import type { DocumentMapper } from "../../domain/value-objects/CreationsConfig";
|
|
6
|
+
import { CreationsFetcher } from "./CreationsFetcher";
|
|
7
|
+
import { CreationsWriter } from "./CreationsWriter";
|
|
35
8
|
|
|
36
9
|
/**
|
|
37
10
|
* Repository options for dynamic configuration
|
|
38
|
-
* Apps can customize
|
|
11
|
+
* Apps can customize document mapping
|
|
39
12
|
*/
|
|
40
13
|
export interface RepositoryOptions {
|
|
41
|
-
/**
|
|
42
|
-
* Custom path builder function
|
|
43
|
-
* @example (userId) => ["users", userId, "photos"]
|
|
44
|
-
* @example (userId) => ["creations", userId, "items"]
|
|
45
|
-
*/
|
|
46
|
-
readonly pathBuilder?: PathBuilder;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Custom document mapper function
|
|
50
|
-
* Maps Firestore documents to Creation entity
|
|
51
|
-
*/
|
|
52
14
|
readonly documentMapper?: DocumentMapper;
|
|
53
15
|
}
|
|
54
16
|
|
|
55
17
|
/**
|
|
56
|
-
*
|
|
18
|
+
* Creations Repository Implementation
|
|
19
|
+
* Delegates to specialized classes for different responsibilities
|
|
20
|
+
*
|
|
21
|
+
* Architecture:
|
|
22
|
+
* - Extends BaseRepository for centralized database access
|
|
23
|
+
* - Uses FirestorePathResolver for path resolution
|
|
24
|
+
* - Uses CreationsFetcher for read operations
|
|
25
|
+
* - Uses CreationsWriter for write operations
|
|
26
|
+
* - Standard path: users/{userId}/{collectionName}
|
|
57
27
|
*/
|
|
58
|
-
const createDefaultPathBuilder =
|
|
59
|
-
(collectionName: string): PathBuilder =>
|
|
60
|
-
(userId: string) =>
|
|
61
|
-
["users", userId, collectionName];
|
|
62
|
-
|
|
63
28
|
export class CreationsRepository
|
|
64
29
|
extends BaseRepository
|
|
65
30
|
implements ICreationsRepository {
|
|
66
|
-
private readonly
|
|
67
|
-
private readonly
|
|
31
|
+
private readonly pathResolver: FirestorePathResolver;
|
|
32
|
+
private readonly fetcher: CreationsFetcher;
|
|
33
|
+
private readonly writer: CreationsWriter;
|
|
68
34
|
|
|
69
35
|
constructor(
|
|
70
|
-
|
|
36
|
+
collectionName: string,
|
|
71
37
|
options?: RepositoryOptions,
|
|
72
38
|
) {
|
|
73
39
|
super();
|
|
74
|
-
this.pathBuilder =
|
|
75
|
-
options?.pathBuilder ?? createDefaultPathBuilder(_collectionName);
|
|
76
|
-
this.documentMapper = options?.documentMapper ?? mapDocumentToCreation;
|
|
77
|
-
}
|
|
78
40
|
|
|
79
|
-
|
|
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
|
-
}
|
|
41
|
+
const documentMapper = options?.documentMapper ?? mapDocumentToCreation;
|
|
85
42
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const pathSegments = this.pathBuilder(userId);
|
|
90
|
-
return doc(db, pathSegments[0], ...pathSegments.slice(1), creationId);
|
|
43
|
+
this.pathResolver = new FirestorePathResolver(collectionName, this.getDb());
|
|
44
|
+
this.fetcher = new CreationsFetcher(this.pathResolver, documentMapper);
|
|
45
|
+
this.writer = new CreationsWriter(this.pathResolver);
|
|
91
46
|
}
|
|
92
47
|
|
|
93
48
|
async getAll(userId: string): Promise<Creation[]> {
|
|
94
|
-
|
|
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
|
-
}
|
|
49
|
+
return this.fetcher.getAll(userId);
|
|
122
50
|
}
|
|
123
51
|
|
|
124
52
|
async getById(userId: string, id: string): Promise<Creation | null> {
|
|
125
|
-
|
|
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
|
-
}
|
|
53
|
+
return this.fetcher.getById(userId, id);
|
|
153
54
|
}
|
|
154
55
|
|
|
155
56
|
async create(userId: string, creation: Creation): Promise<void> {
|
|
156
|
-
|
|
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);
|
|
57
|
+
return this.writer.create(userId, creation);
|
|
169
58
|
}
|
|
170
59
|
|
|
171
60
|
async update(
|
|
@@ -173,54 +62,11 @@ export class CreationsRepository
|
|
|
173
62
|
id: string,
|
|
174
63
|
updates: Partial<Creation>,
|
|
175
64
|
): Promise<boolean> {
|
|
176
|
-
|
|
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
|
-
}
|
|
65
|
+
return this.writer.update(userId, id, updates);
|
|
212
66
|
}
|
|
213
67
|
|
|
214
68
|
async delete(userId: string, creationId: string): Promise<boolean> {
|
|
215
|
-
|
|
216
|
-
if (!docRef) return false;
|
|
217
|
-
|
|
218
|
-
try {
|
|
219
|
-
await deleteDoc(docRef);
|
|
220
|
-
return true;
|
|
221
|
-
} catch {
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
69
|
+
return this.writer.delete(userId, creationId);
|
|
224
70
|
}
|
|
225
71
|
|
|
226
72
|
async updateShared(
|
|
@@ -228,15 +74,7 @@ export class CreationsRepository
|
|
|
228
74
|
creationId: string,
|
|
229
75
|
isShared: boolean,
|
|
230
76
|
): Promise<boolean> {
|
|
231
|
-
|
|
232
|
-
if (!docRef) return false;
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
await updateDoc(docRef, { isShared });
|
|
236
|
-
return true;
|
|
237
|
-
} catch {
|
|
238
|
-
return false;
|
|
239
|
-
}
|
|
77
|
+
return this.writer.updateShared(userId, creationId, isShared);
|
|
240
78
|
}
|
|
241
79
|
|
|
242
80
|
async updateFavorite(
|
|
@@ -244,14 +82,6 @@ export class CreationsRepository
|
|
|
244
82
|
creationId: string,
|
|
245
83
|
isFavorite: boolean,
|
|
246
84
|
): Promise<boolean> {
|
|
247
|
-
|
|
248
|
-
if (!docRef) return false;
|
|
249
|
-
|
|
250
|
-
try {
|
|
251
|
-
await updateDoc(docRef, { isFavorite });
|
|
252
|
-
return true;
|
|
253
|
-
} catch {
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
85
|
+
return this.writer.updateFavorite(userId, creationId, isFavorite);
|
|
256
86
|
}
|
|
257
87
|
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { setDoc, updateDoc, deleteDoc } from "firebase/firestore";
|
|
2
|
+
import { type FirestorePathResolver } from "@umituz/react-native-firebase";
|
|
3
|
+
import type { Creation, CreationDocument } from "../../domain/entities/Creation";
|
|
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
|
+
}
|
|
@@ -98,12 +98,14 @@ export function CreationsGalleryScreen({
|
|
|
98
98
|
}, []);
|
|
99
99
|
|
|
100
100
|
// Handle favorite toggle
|
|
101
|
-
const handleFavorite = useCallback(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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);
|