@umituz/react-native-ai-generation-content 1.17.274 → 1.17.276

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.17.274",
3
+ "version": "1.17.276",
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",
@@ -27,6 +27,8 @@ export interface CreationsTranslations {
27
27
  readonly statusFilterTitle?: string;
28
28
  readonly mediaFilterTitle?: string;
29
29
  readonly clearFilter?: string;
30
+ readonly resultTitle?: string;
31
+ readonly resultLabel?: string;
30
32
  }
31
33
 
32
34
  export interface CreationsFilterConfig {
@@ -97,10 +97,12 @@ export function CreationActions({
97
97
  action.filled && styles.actionButtonFilled,
98
98
  action.disabled && styles.actionButtonDisabled,
99
99
  ]}
100
- onPress={action.onPress}
100
+ onPress={(e) => {
101
+ e.stopPropagation();
102
+ action.onPress();
103
+ }}
101
104
  disabled={action.disabled || action.loading}
102
105
  activeOpacity={0.7}
103
- hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
104
106
  >
105
107
  {action.loading ? (
106
108
  <AtomicSpinner
@@ -33,6 +33,15 @@ export function CreationCard({
33
33
  }: CreationCardProps) {
34
34
  const tokens = useAppDesignTokens();
35
35
 
36
+ // Debug: Log at render time to verify callbacks
37
+ if (__DEV__) {
38
+ console.log("[CreationCard] RENDER", {
39
+ creationId: creation.id,
40
+ hasOnPress: !!callbacks.onPress,
41
+ callbacksKeys: Object.keys(callbacks),
42
+ });
43
+ }
44
+
36
45
  const previewUrl = getPreviewUrl(creation.output) || creation.uri;
37
46
  const title = getCreationTitle(creation.prompt, creation.type as CreationTypeId);
38
47
  const formattedDate = useCreationDateFormatter(creation.createdAt, formatDate);
@@ -46,9 +55,9 @@ export function CreationCard({
46
55
 
47
56
  const handlePress = useCallback(() => {
48
57
  if (__DEV__) {
49
- console.log("[CreationCard] handlePress called", {
58
+ console.log("[CreationCard] handlePress TRIGGERED", {
50
59
  creationId: creation.id,
51
- hasOnPress: !!callbacks.onPress
60
+ hasOnPress: !!callbacks.onPress,
52
61
  });
53
62
  }
54
63
  callbacks.onPress?.(creation);
@@ -86,14 +95,27 @@ export function CreationCard({
86
95
  [tokens]
87
96
  );
88
97
 
98
+ const handlePressIn = useCallback(() => {
99
+ if (__DEV__) {
100
+ console.log("[CreationCard] PRESS_IN detected", { creationId: creation.id });
101
+ }
102
+ }, [creation.id]);
103
+
104
+ const isDisabled = !callbacks.onPress;
105
+
106
+ if (__DEV__ && isDisabled) {
107
+ console.log("[CreationCard] WARNING: Card is DISABLED - onPress is falsy");
108
+ }
109
+
89
110
  return (
90
111
  <TouchableOpacity
91
112
  style={styles.card}
92
113
  onPress={handlePress}
114
+ onPressIn={handlePressIn}
93
115
  activeOpacity={callbacks.onPress ? 0.7 : 1}
94
- disabled={!callbacks.onPress}
116
+ disabled={isDisabled}
95
117
  >
96
- <View style={styles.previewContainer}>
118
+ <View style={styles.previewContainer} pointerEvents="box-none">
97
119
  <CreationPreview
98
120
  uri={previewUrl}
99
121
  status={creation.status || "completed"}
@@ -109,9 +131,9 @@ export function CreationCard({
109
131
  )}
110
132
  </View>
111
133
 
112
- <View style={styles.content}>
113
- <View style={styles.header}>
114
- <View style={styles.titleContainer}>
134
+ <View style={styles.content} pointerEvents="box-none">
135
+ <View style={styles.header} pointerEvents="box-none">
136
+ <View style={styles.titleContainer} pointerEvents="box-none">
115
137
  <AtomicText type="bodyMedium" style={styles.title} numberOfLines={2}>
116
138
  {title}
117
139
  </AtomicText>
@@ -1,4 +1,4 @@
1
- import React, { useMemo, useCallback } from "react";
1
+ import React, { useState, useMemo, useCallback } from "react";
2
2
  import { View, FlatList, RefreshControl, StyleSheet } from "react-native";
3
3
  import {
4
4
  useAppDesignTokens,
@@ -14,7 +14,10 @@ import { useCreations } from "../hooks/useCreations";
14
14
  import { useDeleteCreation } from "../hooks/useDeleteCreation";
15
15
  import { useGalleryFilters } from "../hooks/useGalleryFilters";
16
16
  import { GalleryHeader, CreationCard, GalleryEmptyStates } from "../components";
17
+ import { ResultPreviewScreen } from "../../../result-preview/presentation/components/ResultPreviewScreen";
18
+ import { useResultActions } from "../../../result-preview/presentation/hooks/useResultActions";
17
19
  import { MEDIA_FILTER_OPTIONS, STATUS_FILTER_OPTIONS } from "../../domain/types/creation-filter";
20
+ import { getPreviewUrl } from "../../domain/utils";
18
21
  import type { Creation } from "../../domain/entities/Creation";
19
22
  import type { CreationsConfig } from "../../domain/value-objects/CreationsConfig";
20
23
  import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
@@ -24,11 +27,9 @@ interface CreationsGalleryScreenProps {
24
27
  readonly repository: ICreationsRepository;
25
28
  readonly config: CreationsConfig;
26
29
  readonly t: (key: string) => string;
27
- readonly locale?: string;
28
30
  readonly onEmptyAction?: () => void;
29
31
  readonly emptyActionLabel?: string;
30
32
  readonly showFilter?: boolean;
31
- readonly onViewResult?: (creation: Creation) => void;
32
33
  }
33
34
 
34
35
  export function CreationsGalleryScreen({
@@ -39,15 +40,23 @@ export function CreationsGalleryScreen({
39
40
  onEmptyAction,
40
41
  emptyActionLabel,
41
42
  showFilter = config.showFilter ?? true,
42
- onViewResult,
43
43
  }: CreationsGalleryScreenProps) {
44
44
  const tokens = useAppDesignTokens();
45
45
  const { share } = useSharing();
46
46
  const alert = useAlert();
47
+ const [selectedCreation, setSelectedCreation] = useState<Creation | null>(null);
47
48
 
48
49
  const { data: creations, isLoading, refetch } = useCreations({ userId, repository });
49
50
  const deleteMutation = useDeleteCreation({ userId, repository });
50
51
 
52
+ const selectedImageUrl = selectedCreation ? (getPreviewUrl(selectedCreation.output) || selectedCreation.uri) : undefined;
53
+
54
+ const { isSharing, isSaving, handleDownload, handleShare } = useResultActions({
55
+ imageUrl: selectedImageUrl,
56
+ onSaveSuccess: () => alert.show(AlertType.SUCCESS, AlertMode.TOAST, t("common.success"), t("result.saved")),
57
+ onSaveError: () => alert.show(AlertType.ERROR, AlertMode.TOAST, t("common.error"), t("result.saveError")),
58
+ });
59
+
51
60
  const statusOptions = config.filterConfig?.statusOptions ?? STATUS_FILTER_OPTIONS;
52
61
  const mediaOptions = config.filterConfig?.mediaOptions ?? MEDIA_FILTER_OPTIONS;
53
62
  const showStatusFilter = config.filterConfig?.showStatusFilter ?? true;
@@ -57,7 +66,7 @@ export function CreationsGalleryScreen({
57
66
 
58
67
  useFocusEffect(useCallback(() => { void refetch(); }, [refetch]));
59
68
 
60
- const handleShare = useCallback((c: Creation) => {
69
+ const handleShareCard = useCallback((c: Creation) => {
61
70
  void share(c.uri, { dialogTitle: t("common.share") });
62
71
  }, [share, t]);
63
72
 
@@ -80,6 +89,18 @@ export function CreationsGalleryScreen({
80
89
  })();
81
90
  }, [userId, repository, refetch]);
82
91
 
92
+ const handleCardPress = useCallback((item: Creation) => {
93
+ setSelectedCreation(item);
94
+ }, []);
95
+
96
+ const handleBack = useCallback(() => {
97
+ setSelectedCreation(null);
98
+ }, []);
99
+
100
+ const handleTryAgain = useCallback(() => {
101
+ setSelectedCreation(null);
102
+ }, []);
103
+
83
104
  const filterButtons = useMemo(() => {
84
105
  const buttons = [];
85
106
  if (showStatusFilter) {
@@ -103,27 +124,17 @@ export function CreationsGalleryScreen({
103
124
  return buttons;
104
125
  }, [showStatusFilter, showMediaFilter, filters, t, config.translations]);
105
126
 
106
- const handleCardPress = useCallback((item: Creation) => {
107
- if (__DEV__) {
108
- console.log("[CreationsGalleryScreen] Card pressed", {
109
- creationId: item.id,
110
- hasOnViewResult: !!onViewResult
111
- });
112
- }
113
- onViewResult?.(item);
114
- }, [onViewResult]);
115
-
116
127
  const renderItem = useCallback(({ item }: { item: Creation }) => (
117
128
  <CreationCard
118
129
  creation={item}
119
130
  callbacks={{
120
131
  onPress: () => handleCardPress(item),
121
- onShare: async () => handleShare(item),
132
+ onShare: async () => handleShareCard(item),
122
133
  onDelete: () => handleDelete(item),
123
134
  onFavorite: () => handleFavorite(item, !item.isFavorite),
124
135
  }}
125
136
  />
126
- ), [handleShare, handleDelete, handleFavorite, handleCardPress]);
137
+ ), [handleShareCard, handleDelete, handleFavorite, handleCardPress]);
127
138
 
128
139
  const renderHeader = useMemo(() => {
129
140
  if ((!creations || creations.length === 0) && !isLoading) return null;
@@ -155,6 +166,30 @@ export function CreationsGalleryScreen({
155
166
  />
156
167
  ), [isLoading, creations, filters.isFiltered, tokens, t, config, emptyActionLabel, onEmptyAction, filters.clearAllFilters]);
157
168
 
169
+ // Show result preview when a creation is selected
170
+ if (selectedCreation && selectedImageUrl) {
171
+ return (
172
+ <ResultPreviewScreen
173
+ imageUrl={selectedImageUrl}
174
+ isSaving={isSaving}
175
+ isSharing={isSharing}
176
+ onDownload={handleDownload}
177
+ onShare={handleShare}
178
+ onTryAgain={handleTryAgain}
179
+ onNavigateBack={handleBack}
180
+ translations={{
181
+ title: t(config.translations.resultTitle ?? "result.title"),
182
+ yourResult: t(config.translations.resultLabel ?? "result.yourResult"),
183
+ saveButton: t("result.save"),
184
+ saving: t("result.saving"),
185
+ shareButton: t("result.share"),
186
+ sharing: t("result.sharing"),
187
+ tryAnother: t("result.tryAnother"),
188
+ }}
189
+ />
190
+ );
191
+ }
192
+
158
193
  return (
159
194
  <ScreenLayout scrollable={false}>
160
195
  <FlatList
@@ -163,10 +198,7 @@ export function CreationsGalleryScreen({
163
198
  keyExtractor={(item) => item.id}
164
199
  ListHeaderComponent={renderHeader}
165
200
  ListEmptyComponent={renderEmpty}
166
- contentContainerStyle={[
167
- styles.listContent,
168
- (!filters.filtered || filters.filtered.length === 0) && styles.emptyContent
169
- ]}
201
+ contentContainerStyle={[styles.listContent, (!filters.filtered || filters.filtered.length === 0) && styles.emptyContent]}
170
202
  showsVerticalScrollIndicator={false}
171
203
  refreshControl={<RefreshControl refreshing={isLoading} onRefresh={() => void refetch()} tintColor={tokens.colors.primary} />}
172
204
  />
@@ -177,7 +209,6 @@ export function CreationsGalleryScreen({
177
209
  }
178
210
 
179
211
  const styles = StyleSheet.create({
180
- container: { flex: 1 },
181
212
  header: { borderBottomWidth: 1 },
182
213
  listContent: { paddingHorizontal: 16, paddingTop: 16 },
183
214
  emptyContent: { flexGrow: 1 },