@umituz/react-native-ai-generation-content 1.20.17 → 1.20.19

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.20.17",
3
+ "version": "1.20.19",
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",
@@ -29,5 +29,6 @@ export interface ICreationsRepository {
29
29
  userId: string,
30
30
  creationId: string,
31
31
  rating: number,
32
+ description?: string,
32
33
  ): Promise<boolean>;
33
34
  }
@@ -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"));
@@ -219,6 +219,7 @@ export function CreationsGalleryScreen({
219
219
  title={t("result.rateTitle")}
220
220
  submitLabel={t("common.submit")}
221
221
  cancelLabel={t("common.cancel")}
222
+ descriptionPlaceholder={t("result.feedbackPlaceholder")}
222
223
  />
223
224
  </>
224
225
  );
@@ -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: 320,
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.xl,
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
- <View style={styles.buttonsContainer}>
129
- <View style={styles.button}>
130
- <AtomicButton
131
- title={cancelLabel}
132
- variant="secondary"
133
- onPress={handleClose}
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
- <View style={styles.button}>
137
- <AtomicButton
138
- title={submitLabel}
139
- variant="primary"
140
- onPress={handleSubmit}
141
- disabled={selectedRating === 0}
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>