@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 +1 -1
- package/src/domains/creations/domain/value-objects/CreationsConfig.ts +2 -0
- package/src/domains/creations/presentation/components/CreationActions.tsx +4 -2
- package/src/domains/creations/presentation/components/CreationCard.tsx +29 -7
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +53 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.17.
|
|
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={
|
|
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
|
|
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={
|
|
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
|
|
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 () =>
|
|
132
|
+
onShare: async () => handleShareCard(item),
|
|
122
133
|
onDelete: () => handleDelete(item),
|
|
123
134
|
onFavorite: () => handleFavorite(item, !item.isFavorite),
|
|
124
135
|
}}
|
|
125
136
|
/>
|
|
126
|
-
), [
|
|
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 },
|