@umituz/react-native-ai-generation-content 1.20.16 → 1.20.18
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/domain/repositories/ICreationsRepository.ts +1 -0
- package/src/domains/creations/infrastructure/repositories/CreationsRepository.ts +2 -1
- package/src/domains/creations/infrastructure/repositories/CreationsWriter.ts +22 -1
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +2 -2
- package/src/domains/result-preview/presentation/components/StarRatingPicker.tsx +53 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.18",
|
|
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",
|
|
@@ -102,7 +102,8 @@ export class CreationsRepository
|
|
|
102
102
|
userId: string,
|
|
103
103
|
creationId: string,
|
|
104
104
|
rating: number,
|
|
105
|
+
description?: string,
|
|
105
106
|
): Promise<boolean> {
|
|
106
|
-
return this.writer.rate(userId, creationId, rating);
|
|
107
|
+
return this.writer.rate(userId, creationId, rating, description);
|
|
107
108
|
}
|
|
108
109
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { setDoc, updateDoc, deleteDoc } from "firebase/firestore";
|
|
2
2
|
import { type FirestorePathResolver } from "@umituz/react-native-firebase";
|
|
3
|
+
import { submitFeedback } from "@umituz/react-native-subscription";
|
|
3
4
|
import type { Creation, CreationDocument } from "../../domain/entities/Creation";
|
|
4
5
|
|
|
5
6
|
declare const __DEV__: boolean;
|
|
@@ -153,17 +154,37 @@ export class CreationsWriter {
|
|
|
153
154
|
userId: string,
|
|
154
155
|
creationId: string,
|
|
155
156
|
rating: number,
|
|
157
|
+
description?: string,
|
|
156
158
|
): Promise<boolean> {
|
|
157
159
|
const docRef = this.pathResolver.getDocRef(userId, creationId);
|
|
158
160
|
if (!docRef) return false;
|
|
159
161
|
|
|
160
162
|
try {
|
|
163
|
+
// Update creation document with rating
|
|
161
164
|
await updateDoc(docRef, {
|
|
162
165
|
rating,
|
|
163
166
|
ratedAt: new Date(),
|
|
164
167
|
});
|
|
168
|
+
|
|
169
|
+
// If description exists or we want to log every rating as feedback
|
|
170
|
+
// we submit to feedback collection
|
|
171
|
+
if (description || rating) {
|
|
172
|
+
await submitFeedback({
|
|
173
|
+
userId,
|
|
174
|
+
userEmail: null, // We don't have email here, but standard is userId
|
|
175
|
+
type: "creation_rating",
|
|
176
|
+
title: `Creation Rating: ${rating} Stars`,
|
|
177
|
+
description: description || `User rated creation ${rating} stars`,
|
|
178
|
+
rating,
|
|
179
|
+
status: "pending",
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
165
183
|
return true;
|
|
166
|
-
} catch {
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (__DEV__) {
|
|
186
|
+
console.error("[CreationsWriter] rate() error", error);
|
|
187
|
+
}
|
|
167
188
|
return false;
|
|
168
189
|
}
|
|
169
190
|
}
|
|
@@ -107,10 +107,10 @@ export function CreationsGalleryScreen({
|
|
|
107
107
|
setShowRatingPicker(true);
|
|
108
108
|
}, []);
|
|
109
109
|
|
|
110
|
-
const handleSubmitRating = useCallback((rating: number) => {
|
|
110
|
+
const handleSubmitRating = useCallback((rating: number, description: string) => {
|
|
111
111
|
if (!userId || !selectedCreation) return;
|
|
112
112
|
void (async () => {
|
|
113
|
-
const success = await repository.rate(userId, selectedCreation.id, rating);
|
|
113
|
+
const success = await repository.rate(userId, selectedCreation.id, rating, description);
|
|
114
114
|
if (success) {
|
|
115
115
|
setSelectedCreation({ ...selectedCreation, rating, ratedAt: new Date() });
|
|
116
116
|
alert.show(AlertType.SUCCESS, AlertMode.TOAST, t("result.rateSuccessTitle"), t("result.rateSuccessMessage"));
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React, { useState, useMemo } from "react";
|
|
7
|
-
import { StyleSheet, View, TouchableOpacity, Modal } from "react-native";
|
|
7
|
+
import { StyleSheet, View, TouchableOpacity, Modal, TextInput } from "react-native";
|
|
8
8
|
import {
|
|
9
9
|
AtomicIcon,
|
|
10
10
|
AtomicText,
|
|
@@ -15,10 +15,11 @@ import {
|
|
|
15
15
|
export interface StarRatingPickerProps {
|
|
16
16
|
visible: boolean;
|
|
17
17
|
onClose: () => void;
|
|
18
|
-
onRate: (rating: number) => void;
|
|
18
|
+
onRate: (rating: number, description: string) => void;
|
|
19
19
|
title?: string;
|
|
20
20
|
submitLabel?: string;
|
|
21
21
|
cancelLabel?: string;
|
|
22
|
+
descriptionPlaceholder?: string;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export const StarRatingPicker: React.FC<StarRatingPickerProps> = ({
|
|
@@ -28,9 +29,11 @@ export const StarRatingPicker: React.FC<StarRatingPickerProps> = ({
|
|
|
28
29
|
title = "Rate this creation",
|
|
29
30
|
submitLabel = "Submit",
|
|
30
31
|
cancelLabel = "Cancel",
|
|
32
|
+
descriptionPlaceholder = "Tell us what you think... (Optional)",
|
|
31
33
|
}) => {
|
|
32
34
|
const tokens = useAppDesignTokens();
|
|
33
35
|
const [selectedRating, setSelectedRating] = useState(0);
|
|
36
|
+
const [description, setDescription] = useState("");
|
|
34
37
|
|
|
35
38
|
const styles = useMemo(
|
|
36
39
|
() =>
|
|
@@ -47,7 +50,7 @@ export const StarRatingPicker: React.FC<StarRatingPickerProps> = ({
|
|
|
47
50
|
borderRadius: 20,
|
|
48
51
|
padding: tokens.spacing.xl,
|
|
49
52
|
width: "100%",
|
|
50
|
-
maxWidth:
|
|
53
|
+
maxWidth: 340,
|
|
51
54
|
alignItems: "center",
|
|
52
55
|
},
|
|
53
56
|
title: {
|
|
@@ -61,11 +64,27 @@ export const StarRatingPicker: React.FC<StarRatingPickerProps> = ({
|
|
|
61
64
|
flexDirection: "row",
|
|
62
65
|
justifyContent: "center",
|
|
63
66
|
gap: 8,
|
|
64
|
-
marginBottom: tokens.spacing.
|
|
67
|
+
marginBottom: tokens.spacing.lg,
|
|
65
68
|
},
|
|
66
69
|
starButton: {
|
|
67
70
|
padding: 4,
|
|
68
71
|
},
|
|
72
|
+
inputContainer: {
|
|
73
|
+
width: "100%",
|
|
74
|
+
marginBottom: tokens.spacing.lg,
|
|
75
|
+
},
|
|
76
|
+
input: {
|
|
77
|
+
width: "100%",
|
|
78
|
+
minHeight: 80,
|
|
79
|
+
borderWidth: 1,
|
|
80
|
+
borderColor: tokens.colors.border,
|
|
81
|
+
borderRadius: tokens.shapes.radius.md,
|
|
82
|
+
padding: tokens.spacing.md,
|
|
83
|
+
color: tokens.colors.textPrimary,
|
|
84
|
+
backgroundColor: tokens.colors.background,
|
|
85
|
+
textAlignVertical: "top",
|
|
86
|
+
fontSize: 14,
|
|
87
|
+
},
|
|
69
88
|
buttonsContainer: {
|
|
70
89
|
flexDirection: "row",
|
|
71
90
|
gap: tokens.spacing.md,
|
|
@@ -80,14 +99,16 @@ export const StarRatingPicker: React.FC<StarRatingPickerProps> = ({
|
|
|
80
99
|
|
|
81
100
|
const handleSubmit = () => {
|
|
82
101
|
if (selectedRating > 0) {
|
|
83
|
-
onRate(selectedRating);
|
|
102
|
+
onRate(selectedRating, description);
|
|
84
103
|
setSelectedRating(0);
|
|
104
|
+
setDescription("");
|
|
85
105
|
onClose();
|
|
86
106
|
}
|
|
87
107
|
};
|
|
88
108
|
|
|
89
109
|
const handleClose = () => {
|
|
90
110
|
setSelectedRating(0);
|
|
111
|
+
setDescription("");
|
|
91
112
|
onClose();
|
|
92
113
|
};
|
|
93
114
|
|
|
@@ -125,23 +146,36 @@ export const StarRatingPicker: React.FC<StarRatingPickerProps> = ({
|
|
|
125
146
|
</TouchableOpacity>
|
|
126
147
|
))}
|
|
127
148
|
</View>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
149
|
+
<View style={styles.inputContainer}>
|
|
150
|
+
<TextInput
|
|
151
|
+
style={styles.input}
|
|
152
|
+
placeholder={descriptionPlaceholder}
|
|
153
|
+
placeholderTextColor={tokens.colors.textTertiary}
|
|
154
|
+
multiline
|
|
155
|
+
numberOfLines={3}
|
|
156
|
+
value={description}
|
|
157
|
+
onChangeText={setDescription}
|
|
158
|
+
maxLength={500}
|
|
134
159
|
/>
|
|
135
160
|
</View>
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
161
|
+
|
|
162
|
+
<View style={styles.buttonsContainer}>
|
|
163
|
+
<View style={styles.button}>
|
|
164
|
+
<AtomicButton
|
|
165
|
+
title={cancelLabel}
|
|
166
|
+
variant="secondary"
|
|
167
|
+
onPress={handleClose}
|
|
168
|
+
/>
|
|
169
|
+
</View>
|
|
170
|
+
<View style={styles.button}>
|
|
171
|
+
<AtomicButton
|
|
172
|
+
title={submitLabel}
|
|
173
|
+
variant="primary"
|
|
174
|
+
onPress={handleSubmit}
|
|
175
|
+
disabled={selectedRating === 0}
|
|
176
|
+
/>
|
|
177
|
+
</View>
|
|
143
178
|
</View>
|
|
144
|
-
</View>
|
|
145
179
|
</TouchableOpacity>
|
|
146
180
|
</TouchableOpacity>
|
|
147
181
|
</Modal>
|