@umituz/react-native-ai-generation-content 1.36.2 → 1.37.0
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 +1 -1
- package/src/domains/creations/infrastructure/repositories/CreationsWriter.ts +124 -199
- package/src/domains/creations/presentation/components/GalleryResultPreview.tsx +88 -0
- package/src/domains/creations/presentation/hooks/useGalleryCallbacks.ts +127 -0
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +37 -123
- package/src/domains/generation/wizard/presentation/components/GenericWizardFlow.tsx +5 -231
- package/src/domains/generation/wizard/presentation/components/WizardContinueButton.tsx +73 -0
- package/src/domains/generation/wizard/presentation/components/WizardFlowContent.tsx +181 -0
- package/src/domains/generation/wizard/presentation/components/WizardStepRenderer.tsx +18 -134
- package/src/domains/generation/wizard/presentation/components/step-renderers/renderPhotoUploadStep.tsx +52 -0
- package/src/domains/generation/wizard/presentation/components/step-renderers/renderSelectionStep.tsx +59 -0
- package/src/domains/generation/wizard/presentation/components/step-renderers/renderTextInputStep.tsx +62 -0
- package/src/domains/generation/wizard/presentation/hooks/useWizardFlowHandlers.ts +133 -0
- package/src/domains/generation/wizard/presentation/screens/GenericPhotoUploadScreen.tsx +15 -83
- package/src/domains/generation/wizard/presentation/screens/SelectionScreen.tsx +55 -134
- package/src/domains/result-preview/presentation/components/GenerationErrorScreen.tsx +19 -122
- package/src/domains/result-preview/presentation/hooks/useResultActions.ts +16 -131
- package/src/domains/result-preview/presentation/utils/media-file-utils.ts +78 -0
- package/src/domains/scenarios/presentation/components/ScenarioContinueButton.tsx +76 -0
- package/src/domains/scenarios/presentation/hooks/useHierarchicalScenarios.ts +70 -0
- package/src/domains/scenarios/presentation/screens/HierarchicalScenarioListScreen.tsx +37 -170
- package/src/presentation/hooks/generation/moderation-handler.ts +77 -0
- package/src/presentation/hooks/generation/orchestrator.ts +33 -125
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.37.0",
|
|
4
4
|
"description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -5,216 +5,141 @@ import type { Creation, CreationDocument } from "../../domain/entities/Creation"
|
|
|
5
5
|
|
|
6
6
|
declare const __DEV__: boolean;
|
|
7
7
|
|
|
8
|
+
const UPDATABLE_FIELDS = [
|
|
9
|
+
"metadata", "isShared", "uri", "type", "prompt", "status",
|
|
10
|
+
"output", "rating", "ratedAt", "isFavorite", "deletedAt",
|
|
11
|
+
] as const;
|
|
12
|
+
|
|
8
13
|
/**
|
|
9
14
|
* Handles write operations for creations
|
|
10
|
-
* Single Responsibility: Write operations
|
|
11
15
|
*/
|
|
12
16
|
export class CreationsWriter {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
async create(userId: string, creation: Creation): Promise<void> {
|
|
16
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
17
|
-
|
|
18
|
-
console.log("[CreationsWriter] create() start", { userId, creationId: creation.id });
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const docRef = this.pathResolver.getDocRef(userId, creation.id);
|
|
22
|
-
if (!docRef) throw new Error("Firestore not initialized");
|
|
23
|
-
|
|
24
|
-
const data: CreationDocument = {
|
|
25
|
-
type: creation.type,
|
|
26
|
-
uri: creation.uri,
|
|
27
|
-
createdAt: creation.createdAt,
|
|
28
|
-
metadata: creation.metadata || {},
|
|
29
|
-
isShared: creation.isShared || false,
|
|
30
|
-
isFavorite: creation.isFavorite || false,
|
|
31
|
-
...(creation.status !== undefined && { status: creation.status }),
|
|
32
|
-
...(creation.output !== undefined && { output: creation.output }),
|
|
33
|
-
...(creation.prompt !== undefined && { prompt: creation.prompt }),
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
await setDoc(docRef, data);
|
|
38
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
39
|
-
|
|
40
|
-
console.log("[CreationsWriter] create() success", { creationId: creation.id });
|
|
41
|
-
}
|
|
42
|
-
} catch (error) {
|
|
43
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
44
|
-
|
|
45
|
-
console.error("[CreationsWriter] create() error", error);
|
|
46
|
-
}
|
|
47
|
-
throw error;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
17
|
+
constructor(private readonly pathResolver: FirestorePathResolver) {}
|
|
50
18
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
updates: Partial<Creation>,
|
|
55
|
-
): Promise<boolean> {
|
|
56
|
-
if (__DEV__) {
|
|
57
|
-
|
|
58
|
-
console.log("[CreationsRepository] update()", { userId, id, updates });
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const docRef = this.pathResolver.getDocRef(userId, id);
|
|
62
|
-
if (!docRef) return false;
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
const updateData: Record<string, unknown> = {};
|
|
66
|
-
|
|
67
|
-
if (updates.metadata !== undefined) {
|
|
68
|
-
updateData.metadata = updates.metadata;
|
|
69
|
-
}
|
|
70
|
-
if (updates.isShared !== undefined) {
|
|
71
|
-
updateData.isShared = updates.isShared;
|
|
72
|
-
}
|
|
73
|
-
if (updates.uri !== undefined) {
|
|
74
|
-
updateData.uri = updates.uri;
|
|
75
|
-
}
|
|
76
|
-
if (updates.type !== undefined) {
|
|
77
|
-
updateData.type = updates.type;
|
|
78
|
-
}
|
|
79
|
-
if (updates.prompt !== undefined) {
|
|
80
|
-
updateData.prompt = updates.prompt;
|
|
81
|
-
}
|
|
82
|
-
if (updates.status !== undefined) {
|
|
83
|
-
updateData.status = updates.status;
|
|
84
|
-
}
|
|
85
|
-
if (updates.output !== undefined) {
|
|
86
|
-
updateData.output = updates.output;
|
|
87
|
-
}
|
|
88
|
-
if (updates.rating !== undefined) {
|
|
89
|
-
updateData.rating = updates.rating;
|
|
90
|
-
}
|
|
91
|
-
if (updates.ratedAt !== undefined) {
|
|
92
|
-
updateData.ratedAt = updates.ratedAt;
|
|
93
|
-
}
|
|
94
|
-
if (updates.isFavorite !== undefined) {
|
|
95
|
-
updateData.isFavorite = updates.isFavorite;
|
|
96
|
-
}
|
|
97
|
-
if (updates.deletedAt !== undefined) {
|
|
98
|
-
updateData.deletedAt = updates.deletedAt;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
await updateDoc(docRef, updateData);
|
|
102
|
-
return true;
|
|
103
|
-
} catch (error) {
|
|
104
|
-
if (__DEV__) {
|
|
105
|
-
|
|
106
|
-
console.error("[CreationsRepository] update() ERROR", error);
|
|
107
|
-
}
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
19
|
+
async create(userId: string, creation: Creation): Promise<void> {
|
|
20
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
21
|
+
console.log("[CreationsWriter] create()", { userId, creationId: creation.id });
|
|
110
22
|
}
|
|
111
23
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
24
|
+
const docRef = this.pathResolver.getDocRef(userId, creation.id);
|
|
25
|
+
if (!docRef) throw new Error("Firestore not initialized");
|
|
26
|
+
|
|
27
|
+
const data: CreationDocument = {
|
|
28
|
+
type: creation.type,
|
|
29
|
+
uri: creation.uri,
|
|
30
|
+
createdAt: creation.createdAt,
|
|
31
|
+
metadata: creation.metadata || {},
|
|
32
|
+
isShared: creation.isShared || false,
|
|
33
|
+
isFavorite: creation.isFavorite || false,
|
|
34
|
+
...(creation.status !== undefined && { status: creation.status }),
|
|
35
|
+
...(creation.output !== undefined && { output: creation.output }),
|
|
36
|
+
...(creation.prompt !== undefined && { prompt: creation.prompt }),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
await setDoc(docRef, data);
|
|
41
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[CreationsWriter] create() success");
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) console.error("[CreationsWriter] create() error", error);
|
|
44
|
+
throw error;
|
|
123
45
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async update(userId: string, id: string, updates: Partial<Creation>): Promise<boolean> {
|
|
49
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) console.log("[CreationsWriter] update()", { userId, id });
|
|
50
|
+
|
|
51
|
+
const docRef = this.pathResolver.getDocRef(userId, id);
|
|
52
|
+
if (!docRef) return false;
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const updateData: Record<string, unknown> = {};
|
|
56
|
+
for (const field of UPDATABLE_FIELDS) {
|
|
57
|
+
if (updates[field] !== undefined) updateData[field] = updates[field];
|
|
58
|
+
}
|
|
59
|
+
await updateDoc(docRef, updateData);
|
|
60
|
+
return true;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) console.error("[CreationsWriter] update() error", error);
|
|
63
|
+
return false;
|
|
135
64
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async delete(userId: string, creationId: string): Promise<boolean> {
|
|
68
|
+
const docRef = this.pathResolver.getDocRef(userId, creationId);
|
|
69
|
+
if (!docRef) return false;
|
|
70
|
+
try {
|
|
71
|
+
await updateDoc(docRef, { deletedAt: new Date() });
|
|
72
|
+
return true;
|
|
73
|
+
} catch {
|
|
74
|
+
return false;
|
|
148
75
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
await updateDoc(docRef, { isShared });
|
|
160
|
-
return true;
|
|
161
|
-
} catch {
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async hardDelete(userId: string, creationId: string): Promise<boolean> {
|
|
79
|
+
const docRef = this.pathResolver.getDocRef(userId, creationId);
|
|
80
|
+
if (!docRef) return false;
|
|
81
|
+
try {
|
|
82
|
+
await deleteDoc(docRef);
|
|
83
|
+
return true;
|
|
84
|
+
} catch {
|
|
85
|
+
return false;
|
|
164
86
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
await updateDoc(docRef, { isFavorite });
|
|
176
|
-
return true;
|
|
177
|
-
} catch {
|
|
178
|
-
return false;
|
|
179
|
-
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async restore(userId: string, creationId: string): Promise<boolean> {
|
|
90
|
+
const docRef = this.pathResolver.getDocRef(userId, creationId);
|
|
91
|
+
if (!docRef) return false;
|
|
92
|
+
try {
|
|
93
|
+
await updateDoc(docRef, { deletedAt: null });
|
|
94
|
+
return true;
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
180
97
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async updateShared(userId: string, creationId: string, isShared: boolean): Promise<boolean> {
|
|
101
|
+
const docRef = this.pathResolver.getDocRef(userId, creationId);
|
|
102
|
+
if (!docRef) return false;
|
|
103
|
+
try {
|
|
104
|
+
await updateDoc(docRef, { isShared });
|
|
105
|
+
return true;
|
|
106
|
+
} catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async updateFavorite(userId: string, creationId: string, isFavorite: boolean): Promise<boolean> {
|
|
112
|
+
const docRef = this.pathResolver.getDocRef(userId, creationId);
|
|
113
|
+
if (!docRef) return false;
|
|
114
|
+
try {
|
|
115
|
+
await updateDoc(docRef, { isFavorite });
|
|
116
|
+
return true;
|
|
117
|
+
} catch {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async rate(userId: string, creationId: string, rating: number, description?: string): Promise<boolean> {
|
|
123
|
+
const docRef = this.pathResolver.getDocRef(userId, creationId);
|
|
124
|
+
if (!docRef) return false;
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
await updateDoc(docRef, { rating, ratedAt: new Date() });
|
|
128
|
+
if (description || rating) {
|
|
129
|
+
await submitFeedback({
|
|
130
|
+
userId,
|
|
131
|
+
userEmail: null,
|
|
132
|
+
type: "creation_rating",
|
|
133
|
+
title: `Creation Rating: ${rating} Stars`,
|
|
134
|
+
description: description || `User rated creation ${rating} stars`,
|
|
135
|
+
rating,
|
|
136
|
+
status: "pending",
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) console.error("[CreationsWriter] rate() error", error);
|
|
142
|
+
return false;
|
|
219
143
|
}
|
|
144
|
+
}
|
|
220
145
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gallery Result Preview Component
|
|
3
|
+
* Displays result preview when a creation is selected in gallery
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { useAlert, AlertType, AlertMode } from "@umituz/react-native-design-system";
|
|
8
|
+
import { ResultPreviewScreen } from "../../../result-preview/presentation/components/ResultPreviewScreen";
|
|
9
|
+
import { StarRatingPicker } from "../../../result-preview/presentation/components/StarRatingPicker";
|
|
10
|
+
import { useResultActions } from "../../../result-preview/presentation/hooks/useResultActions";
|
|
11
|
+
import type { Creation } from "../../domain/entities/Creation";
|
|
12
|
+
import type { CreationsConfig } from "../../domain/value-objects/CreationsConfig";
|
|
13
|
+
|
|
14
|
+
interface GalleryResultPreviewProps {
|
|
15
|
+
readonly selectedCreation: Creation;
|
|
16
|
+
readonly imageUrl?: string;
|
|
17
|
+
readonly videoUrl?: string;
|
|
18
|
+
readonly showRatingPicker: boolean;
|
|
19
|
+
readonly config: CreationsConfig;
|
|
20
|
+
readonly t: (key: string) => string;
|
|
21
|
+
readonly onBack: () => void;
|
|
22
|
+
readonly onTryAgain: () => void;
|
|
23
|
+
readonly onRate: () => void;
|
|
24
|
+
readonly onSubmitRating: (rating: number, description: string) => void;
|
|
25
|
+
readonly onCloseRating: () => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function GalleryResultPreview({
|
|
29
|
+
selectedCreation,
|
|
30
|
+
imageUrl,
|
|
31
|
+
videoUrl,
|
|
32
|
+
showRatingPicker,
|
|
33
|
+
config,
|
|
34
|
+
t,
|
|
35
|
+
onBack,
|
|
36
|
+
onTryAgain,
|
|
37
|
+
onRate,
|
|
38
|
+
onSubmitRating,
|
|
39
|
+
onCloseRating,
|
|
40
|
+
}: GalleryResultPreviewProps) {
|
|
41
|
+
const alert = useAlert();
|
|
42
|
+
|
|
43
|
+
const { isSaving, isSharing, handleDownload, handleShare } = useResultActions({
|
|
44
|
+
imageUrl,
|
|
45
|
+
videoUrl,
|
|
46
|
+
onSaveSuccess: () => alert.show(AlertType.SUCCESS, AlertMode.TOAST, t("result.saveSuccess"), t("result.saveSuccessMessage")),
|
|
47
|
+
onSaveError: () => alert.show(AlertType.ERROR, AlertMode.TOAST, t("common.error"), t("result.saveError")),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const hasRating = selectedCreation.rating !== undefined && selectedCreation.rating !== null;
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
<ResultPreviewScreen
|
|
55
|
+
imageUrl={videoUrl ? undefined : imageUrl}
|
|
56
|
+
videoUrl={videoUrl}
|
|
57
|
+
isSaving={isSaving}
|
|
58
|
+
isSharing={isSharing}
|
|
59
|
+
onDownload={handleDownload}
|
|
60
|
+
onShare={handleShare}
|
|
61
|
+
onTryAgain={onTryAgain}
|
|
62
|
+
onNavigateBack={onBack}
|
|
63
|
+
onRate={onRate}
|
|
64
|
+
hideLabel
|
|
65
|
+
iconOnly
|
|
66
|
+
showTryAgain
|
|
67
|
+
showRating={!hasRating}
|
|
68
|
+
translations={{
|
|
69
|
+
title: t(config.translations.title),
|
|
70
|
+
saveButton: t("result.saveButton"),
|
|
71
|
+
saving: t("result.saving"),
|
|
72
|
+
shareButton: t("result.shareButton"),
|
|
73
|
+
sharing: t("result.sharing"),
|
|
74
|
+
tryAnother: t("result.tryAnother"),
|
|
75
|
+
}}
|
|
76
|
+
/>
|
|
77
|
+
<StarRatingPicker
|
|
78
|
+
visible={showRatingPicker}
|
|
79
|
+
onClose={onCloseRating}
|
|
80
|
+
onRate={onSubmitRating}
|
|
81
|
+
title={t("result.rateTitle")}
|
|
82
|
+
submitLabel={t("common.submit")}
|
|
83
|
+
cancelLabel={t("common.cancel")}
|
|
84
|
+
descriptionPlaceholder={t("result.feedbackPlaceholder")}
|
|
85
|
+
/>
|
|
86
|
+
</>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gallery Callbacks Hook
|
|
3
|
+
* Extracts callback handlers from CreationsGalleryScreen
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useCallback } from "react";
|
|
7
|
+
import { useAlert, AlertType, AlertMode, useSharing } from "@umituz/react-native-design-system";
|
|
8
|
+
import type { Creation } from "../../domain/entities/Creation";
|
|
9
|
+
import type { CreationsConfig } from "../../domain/value-objects/CreationsConfig";
|
|
10
|
+
import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
|
|
11
|
+
|
|
12
|
+
export interface UseGalleryCallbacksProps {
|
|
13
|
+
readonly userId: string | null;
|
|
14
|
+
readonly repository: ICreationsRepository;
|
|
15
|
+
readonly config: CreationsConfig;
|
|
16
|
+
readonly t: (key: string) => string;
|
|
17
|
+
readonly deleteMutation: { mutateAsync: (id: string) => Promise<boolean> };
|
|
18
|
+
readonly refetch: () => Promise<void>;
|
|
19
|
+
readonly setSelectedCreation: (creation: Creation | null) => void;
|
|
20
|
+
readonly setShowRatingPicker: (show: boolean) => void;
|
|
21
|
+
readonly selectedCreation: Creation | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function useGalleryCallbacks(props: UseGalleryCallbacksProps) {
|
|
25
|
+
const {
|
|
26
|
+
userId,
|
|
27
|
+
repository,
|
|
28
|
+
config,
|
|
29
|
+
t,
|
|
30
|
+
deleteMutation,
|
|
31
|
+
refetch,
|
|
32
|
+
setSelectedCreation,
|
|
33
|
+
setShowRatingPicker,
|
|
34
|
+
selectedCreation,
|
|
35
|
+
} = props;
|
|
36
|
+
|
|
37
|
+
const { share } = useSharing();
|
|
38
|
+
const alert = useAlert();
|
|
39
|
+
|
|
40
|
+
const handleShareCard = useCallback(
|
|
41
|
+
(c: Creation) => {
|
|
42
|
+
void share(c.uri, { dialogTitle: t("common.share") });
|
|
43
|
+
},
|
|
44
|
+
[share, t],
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const handleDelete = useCallback(
|
|
48
|
+
(c: Creation) => {
|
|
49
|
+
alert.show(
|
|
50
|
+
AlertType.WARNING,
|
|
51
|
+
AlertMode.MODAL,
|
|
52
|
+
t(config.translations.deleteTitle),
|
|
53
|
+
t(config.translations.deleteMessage),
|
|
54
|
+
{
|
|
55
|
+
actions: [
|
|
56
|
+
{ id: "cancel", label: t("common.cancel"), onPress: () => {} },
|
|
57
|
+
{
|
|
58
|
+
id: "delete",
|
|
59
|
+
label: t("common.delete"),
|
|
60
|
+
style: "destructive",
|
|
61
|
+
onPress: async () => {
|
|
62
|
+
await deleteMutation.mutateAsync(c.id);
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
);
|
|
68
|
+
},
|
|
69
|
+
[alert, config, deleteMutation, t],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const handleFavorite = useCallback(
|
|
73
|
+
(c: Creation, isFavorite: boolean) => {
|
|
74
|
+
void (async () => {
|
|
75
|
+
if (!userId) return;
|
|
76
|
+
const success = await repository.updateFavorite(userId, c.id, isFavorite);
|
|
77
|
+
if (success) void refetch();
|
|
78
|
+
})();
|
|
79
|
+
},
|
|
80
|
+
[userId, repository, refetch],
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const handleCardPress = useCallback(
|
|
84
|
+
(item: Creation) => {
|
|
85
|
+
setSelectedCreation(item);
|
|
86
|
+
},
|
|
87
|
+
[setSelectedCreation],
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const handleBack = useCallback(() => {
|
|
91
|
+
setSelectedCreation(null);
|
|
92
|
+
}, [setSelectedCreation]);
|
|
93
|
+
|
|
94
|
+
const handleTryAgain = useCallback(() => {
|
|
95
|
+
setSelectedCreation(null);
|
|
96
|
+
}, [setSelectedCreation]);
|
|
97
|
+
|
|
98
|
+
const handleOpenRatingPicker = useCallback(() => {
|
|
99
|
+
setShowRatingPicker(true);
|
|
100
|
+
}, [setShowRatingPicker]);
|
|
101
|
+
|
|
102
|
+
const handleSubmitRating = useCallback(
|
|
103
|
+
(rating: number, description: string) => {
|
|
104
|
+
if (!userId || !selectedCreation) return;
|
|
105
|
+
void (async () => {
|
|
106
|
+
const success = await repository.rate(userId, selectedCreation.id, rating, description);
|
|
107
|
+
if (success) {
|
|
108
|
+
setSelectedCreation({ ...selectedCreation, rating, ratedAt: new Date() });
|
|
109
|
+
alert.show(AlertType.SUCCESS, AlertMode.TOAST, t("result.rateSuccessTitle"), t("result.rateSuccessMessage"));
|
|
110
|
+
void refetch();
|
|
111
|
+
}
|
|
112
|
+
})();
|
|
113
|
+
},
|
|
114
|
+
[userId, selectedCreation, repository, alert, t, refetch, setSelectedCreation],
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
handleShareCard,
|
|
119
|
+
handleDelete,
|
|
120
|
+
handleFavorite,
|
|
121
|
+
handleCardPress,
|
|
122
|
+
handleBack,
|
|
123
|
+
handleTryAgain,
|
|
124
|
+
handleOpenRatingPicker,
|
|
125
|
+
handleSubmitRating,
|
|
126
|
+
};
|
|
127
|
+
}
|