@umituz/react-native-ai-generation-content 1.12.25 → 1.12.26

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.
Files changed (50) hide show
  1. package/package.json +3 -2
  2. package/src/domains/creations/application/services/CreationsService.ts +73 -0
  3. package/src/domains/creations/domain/entities/Creation.ts +60 -0
  4. package/src/domains/creations/domain/entities/index.ts +6 -0
  5. package/src/domains/creations/domain/repositories/ICreationsRepository.ts +23 -0
  6. package/src/domains/creations/domain/repositories/index.ts +5 -0
  7. package/src/domains/creations/domain/services/ICreationsStorageService.ts +13 -0
  8. package/src/domains/creations/domain/value-objects/CreationsConfig.ts +75 -0
  9. package/src/domains/creations/domain/value-objects/index.ts +12 -0
  10. package/src/domains/creations/index.ts +84 -0
  11. package/src/domains/creations/infrastructure/adapters/createRepository.ts +54 -0
  12. package/src/domains/creations/infrastructure/adapters/index.ts +5 -0
  13. package/src/domains/creations/infrastructure/repositories/CreationsRepository.ts +241 -0
  14. package/src/domains/creations/infrastructure/repositories/index.ts +8 -0
  15. package/src/domains/creations/infrastructure/services/CreationsStorageService.ts +49 -0
  16. package/src/domains/creations/presentation/components/CreationCard.tsx +136 -0
  17. package/src/domains/creations/presentation/components/CreationDetail/DetailActions.tsx +76 -0
  18. package/src/domains/creations/presentation/components/CreationDetail/DetailHeader.tsx +81 -0
  19. package/src/domains/creations/presentation/components/CreationDetail/DetailImage.tsx +41 -0
  20. package/src/domains/creations/presentation/components/CreationDetail/DetailStory.tsx +67 -0
  21. package/src/domains/creations/presentation/components/CreationDetail/index.ts +4 -0
  22. package/src/domains/creations/presentation/components/CreationImageViewer.tsx +43 -0
  23. package/src/domains/creations/presentation/components/CreationThumbnail.tsx +63 -0
  24. package/src/domains/creations/presentation/components/CreationsGrid.tsx +75 -0
  25. package/src/domains/creations/presentation/components/CreationsHomeCard.tsx +176 -0
  26. package/src/domains/creations/presentation/components/EmptyState.tsx +82 -0
  27. package/src/domains/creations/presentation/components/FilterBottomSheet.tsx +160 -0
  28. package/src/domains/creations/presentation/components/FilterChips.tsx +105 -0
  29. package/src/domains/creations/presentation/components/GalleryHeader.tsx +106 -0
  30. package/src/domains/creations/presentation/components/index.ts +19 -0
  31. package/src/domains/creations/presentation/hooks/index.ts +7 -0
  32. package/src/domains/creations/presentation/hooks/useCreations.ts +38 -0
  33. package/src/domains/creations/presentation/hooks/useCreationsFilter.ts +77 -0
  34. package/src/domains/creations/presentation/hooks/useDeleteCreation.ts +51 -0
  35. package/src/domains/creations/presentation/screens/CreationDetailScreen.tsx +78 -0
  36. package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +217 -0
  37. package/src/domains/creations/presentation/screens/index.ts +5 -0
  38. package/src/domains/creations/presentation/utils/filterUtils.ts +52 -0
  39. package/src/domains/creations/types.d.ts +42 -0
  40. package/src/features/background/presentation/components/ComparisonSlider.tsx +2 -2
  41. package/src/features/background/presentation/components/ErrorDisplay.tsx +2 -2
  42. package/src/features/background/presentation/components/GenerateButton.tsx +0 -2
  43. package/src/features/background/presentation/components/ImagePicker.tsx +2 -2
  44. package/src/features/background/presentation/components/ResultDisplay.tsx +2 -2
  45. package/src/index.ts +6 -0
  46. package/src/presentation/components/GenerationProgressContent.tsx +4 -4
  47. package/src/presentation/components/PendingJobCard.tsx +2 -2
  48. package/src/presentation/components/PendingJobCardActions.tsx +2 -2
  49. package/src/presentation/components/result/GenerationResultContent.tsx +2 -3
  50. package/src/presentation/hooks/usePhotoGeneration.ts +7 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.12.25",
3
+ "version": "1.12.26",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -8,6 +8,7 @@
8
8
  ".": "./src/index.ts",
9
9
  "./prompts": "./src/domains/prompts/index.ts",
10
10
  "./content-moderation": "./src/domains/content-moderation/index.ts",
11
+ "./creations": "./src/domains/creations/index.ts",
11
12
  "./face-detection": "./src/domains/face-detection/index.ts",
12
13
  "./feature-background": "./src/domains/feature-background/index.ts"
13
14
  },
@@ -65,7 +66,7 @@
65
66
  "@typescript-eslint/eslint-plugin": "^7.0.0",
66
67
  "@typescript-eslint/parser": "^7.0.0",
67
68
  "@umituz/react-native-animation": "*",
68
- "@umituz/react-native-design-system": "^2.3.31",
69
+ "@umituz/react-native-design-system": "^2.3.33",
69
70
  "@umituz/react-native-firebase": "*",
70
71
  "@umituz/react-native-haptics": "^1.0.2",
71
72
  "@umituz/react-native-image": "*",
@@ -0,0 +1,73 @@
1
+
2
+ import { generateUUID } from "@umituz/react-native-uuid";
3
+ import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
4
+ import type { ICreationsStorageService } from "../../domain/services/ICreationsStorageService";
5
+ import type { CreationType } from "../../domain/value-objects";
6
+ import { BaseRepository } from "@umituz/react-native-firebase";
7
+ import type { Creation } from "../../domain/entities/Creation";
8
+
9
+ export interface CreateCreationDTO {
10
+ userId: string;
11
+ type: CreationType;
12
+ prompt: string;
13
+ metadata?: Record<string, unknown>;
14
+ imageUri: string; // can be local file uri or base64
15
+ aspectRatio?: number;
16
+ }
17
+
18
+ export class CreationsService extends BaseRepository {
19
+ constructor(
20
+ private readonly repository: ICreationsRepository,
21
+ private readonly storageService: ICreationsStorageService,
22
+ private readonly _collectionName: string = "creations" // Default to generic name, app can override via repo
23
+ ) {
24
+ super();
25
+ }
26
+
27
+ async saveCreation(dto: CreateCreationDTO): Promise<string> {
28
+ const db = this.getDb();
29
+ if (!db) throw new Error("Firestore not initialized");
30
+
31
+ try {
32
+ const creationId = generateUUID();
33
+
34
+ const imageUrl = await this.storageService.uploadCreationImage(
35
+ dto.userId,
36
+ creationId,
37
+ dto.imageUri
38
+ );
39
+
40
+ await this.repository.create(dto.userId, {
41
+ id: creationId,
42
+ uri: imageUrl,
43
+ type: dto.type.id,
44
+ prompt: dto.prompt,
45
+ metadata: dto.metadata || {},
46
+ createdAt: new Date(),
47
+ isShared: false,
48
+ });
49
+
50
+ return creationId;
51
+ } catch (error) {
52
+ // eslint-disable-next-line no-console
53
+ console.error(error);
54
+ throw error;
55
+ }
56
+ }
57
+
58
+ async getCreation(userId: string, id: string): Promise<Creation | null> {
59
+ return this.repository.getById(userId, id);
60
+ }
61
+
62
+ async updateCreation(
63
+ userId: string,
64
+ id: string,
65
+ updates: { metadata?: Record<string, unknown> },
66
+ ): Promise<boolean> {
67
+ return this.repository.update(userId, id, updates);
68
+ }
69
+
70
+ async deleteCreation(userId: string, id: string): Promise<boolean> {
71
+ return this.repository.delete(userId, id);
72
+ }
73
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Creation Entity
3
+ * Represents an AI-generated creation
4
+ */
5
+
6
+ export interface Creation {
7
+ readonly id: string;
8
+ readonly uri: string;
9
+ readonly type: string;
10
+ readonly prompt?: string;
11
+ readonly metadata?: Record<string, unknown>;
12
+ readonly originalUri?: string;
13
+ readonly createdAt: Date;
14
+ readonly isShared: boolean;
15
+ }
16
+
17
+ export interface CreationDocument {
18
+ readonly uri?: string;
19
+ readonly prompt?: string;
20
+ readonly metadata?: Record<string, unknown>;
21
+ readonly originalImage?: string;
22
+ readonly originalImageUrl?: string;
23
+ readonly transformedImage?: string;
24
+ readonly transformedImageUrl?: string;
25
+ readonly transformationType?: string;
26
+ readonly type?: string;
27
+ readonly status?: string;
28
+ readonly isShared: boolean;
29
+ readonly createdAt: FirebaseTimestamp | Date; // Allow Date for writing
30
+ readonly completedAt?: FirebaseTimestamp | Date;
31
+ }
32
+
33
+ interface FirebaseTimestamp {
34
+ toDate: () => Date;
35
+ }
36
+
37
+ export function mapDocumentToCreation(
38
+ id: string,
39
+ data: CreationDocument,
40
+ ): Creation {
41
+ let creationDate: Date;
42
+ if (data.createdAt instanceof Date) {
43
+ creationDate = data.createdAt;
44
+ } else if (data.createdAt && typeof data.createdAt === "object" && "toDate" in data.createdAt && typeof data.createdAt.toDate === "function") {
45
+ creationDate = data.createdAt.toDate();
46
+ } else {
47
+ creationDate = new Date();
48
+ }
49
+
50
+ return {
51
+ id,
52
+ uri: data.transformedImageUrl || data.transformedImage || data.uri || "",
53
+ type: data.transformationType || data.type || "unknown",
54
+ prompt: data.prompt,
55
+ metadata: data.metadata,
56
+ originalUri: data.originalImageUrl || data.originalImage,
57
+ createdAt: creationDate,
58
+ isShared: data.isShared ?? false,
59
+ };
60
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Domain Entities
3
+ */
4
+
5
+ export type { Creation, CreationDocument } from "./Creation";
6
+ export { mapDocumentToCreation } from "./Creation";
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Creations Repository Interface
3
+ * Defines the contract for creations data access
4
+ */
5
+
6
+ import type { Creation } from "../entities/Creation";
7
+
8
+ export interface ICreationsRepository {
9
+ getAll(userId: string): Promise<Creation[]>;
10
+ getById(userId: string, id: string): Promise<Creation | null>;
11
+ create(userId: string, creation: Creation): Promise<void>;
12
+ update(
13
+ userId: string,
14
+ id: string,
15
+ updates: Partial<Creation>,
16
+ ): Promise<boolean>;
17
+ delete(userId: string, creationId: string): Promise<boolean>;
18
+ updateShared(
19
+ userId: string,
20
+ creationId: string,
21
+ isShared: boolean,
22
+ ): Promise<boolean>;
23
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Domain Repository Interfaces
3
+ */
4
+
5
+ export type { ICreationsRepository } from "./ICreationsRepository";
@@ -0,0 +1,13 @@
1
+ export interface ICreationsStorageService {
2
+ uploadCreationImage(
3
+ userId: string,
4
+ creationId: string,
5
+ imageUri: string,
6
+ mimeType?: string
7
+ ): Promise<string>;
8
+
9
+ deleteCreationImage(
10
+ userId: string,
11
+ creationId: string
12
+ ): Promise<boolean>;
13
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Creations Configuration Value Object
3
+ * Defines the configuration for creations feature
4
+ */
5
+
6
+ import type { Creation, CreationDocument } from "../entities/Creation";
7
+ import type { FilterCategory } from "../../presentation/components/FilterBottomSheet";
8
+
9
+ export interface CreationType {
10
+ readonly id: string;
11
+ readonly labelKey: string;
12
+ readonly icon: string;
13
+ }
14
+
15
+ export interface CreationsTranslations {
16
+ readonly title: string;
17
+ readonly subtitle: string;
18
+ readonly empty: string;
19
+ readonly emptyDescription: string;
20
+ readonly deleteTitle: string;
21
+ readonly deleteMessage: string;
22
+ readonly photoCount: string;
23
+ readonly filterAll: string;
24
+ readonly filterLabel: string;
25
+ readonly filterTitle: string;
26
+ }
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
+ /**
40
+ * Document mapper function type
41
+ * Allows apps to map their specific document structure to Creation
42
+ */
43
+ export type DocumentMapper = (id: string, data: CreationDocument) => Creation;
44
+
45
+ export interface CreationsConfig {
46
+ readonly collectionName: string;
47
+ readonly types: readonly CreationType[];
48
+ readonly filterCategories?: readonly FilterCategory[];
49
+ readonly translations: CreationsTranslations;
50
+ readonly maxThumbnails?: number;
51
+ readonly gridColumns?: number;
52
+ readonly pathBuilder?: PathBuilder;
53
+ readonly documentMapper?: DocumentMapper;
54
+ }
55
+
56
+ export const DEFAULT_TRANSLATIONS: CreationsTranslations = {
57
+ title: "creations.title",
58
+ subtitle: "creations.subtitle",
59
+ empty: "creations.empty",
60
+ emptyDescription: "creations.emptyDescription",
61
+ deleteTitle: "creations.deleteTitle",
62
+ deleteMessage: "creations.deleteMessage",
63
+ photoCount: "creations.photoCount",
64
+ filterAll: "creations.filterAll",
65
+ filterLabel: "common.filter",
66
+ filterTitle: "common.filter",
67
+ };
68
+
69
+ export const DEFAULT_CONFIG: CreationsConfig = {
70
+ collectionName: "creations",
71
+ types: [],
72
+ translations: DEFAULT_TRANSLATIONS,
73
+ maxThumbnails: 4,
74
+ gridColumns: 2,
75
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Domain Value Objects
3
+ */
4
+
5
+ export type {
6
+ CreationType,
7
+ CreationsTranslations,
8
+ CreationsConfig,
9
+ PathBuilder,
10
+ DocumentMapper,
11
+ } from "./CreationsConfig";
12
+ export { DEFAULT_TRANSLATIONS, DEFAULT_CONFIG } from "./CreationsConfig";
@@ -0,0 +1,84 @@
1
+ /**
2
+ * @umituz/react-native-ai-creations
3
+ *
4
+ * AI-generated creations gallery with filtering, sharing, and management
5
+ *
6
+ * Architecture:
7
+ * - Extends BaseRepository from @umituz/react-native-firestore
8
+ * - Fully dynamic path structure (configurable per app)
9
+ * - Fully dynamic document mapping (configurable per app)
10
+ * - App-agnostic: Works with any app, no app-specific code
11
+ *
12
+ * This package is designed to be used across hundreds of apps.
13
+ *
14
+ * Usage:
15
+ * import {
16
+ * CreationsGalleryScreen,
17
+ * CreationsHomeCard,
18
+ * useCreations,
19
+ * createCreationsRepository,
20
+ * } from '@umituz/react-native-ai-creations';
21
+ */
22
+
23
+ // =============================================================================
24
+ // DOMAIN LAYER - Entities
25
+ // =============================================================================
26
+
27
+ export type { Creation, CreationDocument } from "./domain/entities";
28
+ export { mapDocumentToCreation } from "./domain/entities";
29
+
30
+ // =============================================================================
31
+ // DOMAIN LAYER - Value Objects
32
+ // =============================================================================
33
+
34
+ export type {
35
+ CreationType,
36
+ CreationsTranslations,
37
+ CreationsConfig,
38
+ PathBuilder,
39
+ DocumentMapper,
40
+ } from "./domain/value-objects";
41
+ export { DEFAULT_TRANSLATIONS, DEFAULT_CONFIG } from "./domain/value-objects";
42
+
43
+ // =============================================================================
44
+ // DOMAIN LAYER - Repository Interface
45
+ // =============================================================================
46
+
47
+ export type { ICreationsRepository } from "./domain/repositories";
48
+
49
+ // =============================================================================
50
+ // INFRASTRUCTURE LAYER
51
+ // =============================================================================
52
+
53
+ export {
54
+ CreationsRepository,
55
+ type RepositoryOptions,
56
+ } from "./infrastructure/repositories";
57
+ export { CreationsStorageService } from "./infrastructure/services/CreationsStorageService";
58
+ export { createCreationsRepository } from "./infrastructure/adapters";
59
+ export { CreationsService } from "./application/services/CreationsService";
60
+ export type { ICreationsStorageService } from "./domain/services/ICreationsStorageService";
61
+
62
+ // =============================================================================
63
+ // PRESENTATION LAYER - Hooks
64
+ // =============================================================================
65
+
66
+ export { useCreations } from "./presentation/hooks/useCreations";
67
+ export { useDeleteCreation } from "./presentation/hooks/useDeleteCreation";
68
+ export { useCreationsFilter } from "./presentation/hooks/useCreationsFilter";
69
+
70
+ // =============================================================================
71
+ // PRESENTATION LAYER - Components
72
+ // =============================================================================
73
+
74
+ export { CreationThumbnail } from "./presentation/components/CreationThumbnail";
75
+ export { CreationCard } from "./presentation/components/CreationCard";
76
+ export { CreationsHomeCard } from "./presentation/components/CreationsHomeCard";
77
+ export { FilterChips } from "./presentation/components/FilterChips";
78
+ export { EmptyState } from "./presentation/components/EmptyState";
79
+
80
+ // =============================================================================
81
+ // PRESENTATION LAYER - Screens
82
+ // =============================================================================
83
+
84
+ export { CreationsGalleryScreen } from "./presentation/screens/CreationsGalleryScreen";
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Repository Factory
3
+ * Creates repository instance with given configuration
4
+ *
5
+ * Architecture:
6
+ * - Factory pattern for repository creation
7
+ * - Supports dynamic path structure per app
8
+ * - Supports custom document mapping per app
9
+ * - App-agnostic: No Firestore instance needed (BaseRepository handles it)
10
+ *
11
+ * This factory is designed to be used across hundreds of apps.
12
+ */
13
+
14
+ import {
15
+ CreationsRepository,
16
+ type RepositoryOptions,
17
+ } from "../repositories/CreationsRepository";
18
+ import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
19
+
20
+ /**
21
+ * Creates a new CreationsRepository instance
22
+ *
23
+ * @param collectionName - Firestore collection name
24
+ * @param options - Optional repository configuration
25
+ * @returns ICreationsRepository instance
26
+ *
27
+ * @example
28
+ * // Basic usage with default path (users/{userId}/photos)
29
+ * const repo = createCreationsRepository("photos");
30
+ *
31
+ * @example
32
+ * // Custom path structure
33
+ * const repo = createCreationsRepository("creations", {
34
+ * pathBuilder: (userId) => ["gallery", userId, "items"],
35
+ * });
36
+ *
37
+ * @example
38
+ * // Custom document mapper
39
+ * const repo = createCreationsRepository("photos", {
40
+ * documentMapper: (id, data) => ({
41
+ * id,
42
+ * uri: data.imageUrl,
43
+ * type: data.category,
44
+ * createdAt: data.timestamp?.toDate() || new Date(),
45
+ * isShared: data.public ?? false,
46
+ * }),
47
+ * });
48
+ */
49
+ export function createCreationsRepository(
50
+ collectionName: string,
51
+ options?: RepositoryOptions,
52
+ ): ICreationsRepository {
53
+ return new CreationsRepository(collectionName, options);
54
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Infrastructure Adapters
3
+ */
4
+
5
+ export { createCreationsRepository } from "./createRepository";
@@ -0,0 +1,241 @@
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";
28
+ import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
29
+ import type { Creation, CreationDocument } from "../../domain/entities/Creation";
30
+ import { mapDocumentToCreation } from "../../domain/entities/Creation";
31
+ import type {
32
+ PathBuilder,
33
+ DocumentMapper,
34
+ } from "../../domain/value-objects/CreationsConfig";
35
+
36
+ /**
37
+ * Repository options for dynamic configuration
38
+ * Apps can customize path structure and document mapping
39
+ */
40
+ 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
+ readonly documentMapper?: DocumentMapper;
53
+ }
54
+
55
+ /**
56
+ * Default path builder: users/{userId}/{collectionName}
57
+ */
58
+ const createDefaultPathBuilder =
59
+ (collectionName: string): PathBuilder =>
60
+ (userId: string) =>
61
+ ["users", userId, collectionName];
62
+
63
+ export class CreationsRepository
64
+ extends BaseRepository
65
+ implements ICreationsRepository {
66
+ private readonly pathBuilder: PathBuilder;
67
+ private readonly documentMapper: DocumentMapper;
68
+
69
+ constructor(
70
+ private readonly _collectionName: string,
71
+ options?: RepositoryOptions,
72
+ ) {
73
+ super();
74
+ this.pathBuilder =
75
+ options?.pathBuilder ?? createDefaultPathBuilder(_collectionName);
76
+ this.documentMapper = options?.documentMapper ?? mapDocumentToCreation;
77
+ }
78
+
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
+ }
85
+
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);
91
+ }
92
+
93
+ 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
+ }
122
+ }
123
+
124
+ 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
+ }
153
+ }
154
+
155
+ 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);
169
+ }
170
+
171
+ async update(
172
+ userId: string,
173
+ id: string,
174
+ updates: Partial<Creation>,
175
+ ): 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
+ }
212
+ }
213
+
214
+ 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
+ }
224
+ }
225
+
226
+ async updateShared(
227
+ userId: string,
228
+ creationId: string,
229
+ isShared: boolean,
230
+ ): 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
+ }
240
+ }
241
+ }