@umituz/react-native-ai-generation-content 1.37.27 → 1.37.29
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/utils/preview-helpers.ts +19 -0
- package/src/domains/creations/presentation/components/CreationCard.tsx +5 -0
- package/src/domains/creations/presentation/components/CreationPreview.tsx +10 -13
- package/src/domains/creations/presentation/components/CreationVideoPreview.tsx +15 -11
- package/src/domains/creations/presentation/components/GalleryHeader.tsx +0 -18
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.37.
|
|
3
|
+
"version": "1.37.29",
|
|
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",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Utility functions for creation preview/thumbnail extraction
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { isImageUrl } from "@umituz/react-native-design-system";
|
|
6
7
|
import type { CreationOutput } from "../entities/Creation";
|
|
7
8
|
|
|
8
9
|
// Re-export for convenience
|
|
@@ -82,3 +83,21 @@ export function getPrimaryMediaUrl(output?: CreationOutput): string | null {
|
|
|
82
83
|
null
|
|
83
84
|
);
|
|
84
85
|
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Determine if thumbnail URL is valid and should be displayed
|
|
89
|
+
* - Must exist
|
|
90
|
+
* - Must be an image URL (not a video URL)
|
|
91
|
+
* - Content must not be in progress
|
|
92
|
+
*/
|
|
93
|
+
export function shouldShowThumbnail(
|
|
94
|
+
thumbnailUrl?: string | null,
|
|
95
|
+
inProgress?: boolean
|
|
96
|
+
): boolean {
|
|
97
|
+
if (!thumbnailUrl || inProgress) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Thumbnail must be an image, not a video
|
|
102
|
+
return isImageUrl(thumbnailUrl);
|
|
103
|
+
}
|
|
@@ -40,6 +40,11 @@ export function CreationCard({
|
|
|
40
40
|
creationId: creation.id,
|
|
41
41
|
hasOnPress: !!callbacks.onPress,
|
|
42
42
|
callbacksKeys: Object.keys(callbacks),
|
|
43
|
+
status: creation.status,
|
|
44
|
+
type: creation.type,
|
|
45
|
+
output: creation.output,
|
|
46
|
+
hasThumbnailUrl: !!creation.output?.thumbnailUrl,
|
|
47
|
+
thumbnailUrl: creation.output?.thumbnailUrl,
|
|
43
48
|
});
|
|
44
49
|
}
|
|
45
50
|
|
|
@@ -1,22 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CreationPreview Component
|
|
3
3
|
* Smart wrapper that delegates to CreationImagePreview or CreationVideoPreview
|
|
4
|
-
* based on creation type
|
|
4
|
+
* based on output content type (not creation type)
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import React from "react";
|
|
8
|
+
import { isVideoUrl } from "@umituz/react-native-design-system";
|
|
8
9
|
import type { CreationStatus, CreationTypeId } from "../../domain/types";
|
|
9
10
|
import { CreationImagePreview } from "./CreationImagePreview";
|
|
10
11
|
import { CreationVideoPreview } from "./CreationVideoPreview";
|
|
11
12
|
|
|
12
|
-
/** Video creation types */
|
|
13
|
-
const VIDEO_TYPES: CreationTypeId[] = ["text-to-video", "image-to-video"];
|
|
14
|
-
|
|
15
|
-
/** Check if creation type is a video type */
|
|
16
|
-
function isVideoType(type?: CreationTypeId | string): boolean {
|
|
17
|
-
return VIDEO_TYPES.includes(type as CreationTypeId);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
13
|
interface CreationPreviewProps {
|
|
21
14
|
/** Preview image/thumbnail URL */
|
|
22
15
|
readonly uri?: string | null;
|
|
@@ -43,11 +36,15 @@ export function CreationPreview({
|
|
|
43
36
|
height,
|
|
44
37
|
showLoadingIndicator = true,
|
|
45
38
|
}: CreationPreviewProps) {
|
|
46
|
-
//
|
|
47
|
-
|
|
39
|
+
// Determine preview type based on URI content, not creation type
|
|
40
|
+
// This handles scenario-based videos (solo_martial_artist, ski_resort, etc.)
|
|
41
|
+
const hasVideoContent = uri && isVideoUrl(uri);
|
|
42
|
+
|
|
43
|
+
// For video content, use CreationVideoPreview
|
|
44
|
+
if (hasVideoContent) {
|
|
48
45
|
return (
|
|
49
46
|
<CreationVideoPreview
|
|
50
|
-
thumbnailUrl={thumbnailUrl
|
|
47
|
+
thumbnailUrl={thumbnailUrl}
|
|
51
48
|
videoUrl={uri}
|
|
52
49
|
status={status}
|
|
53
50
|
type={type as CreationTypeId}
|
|
@@ -58,7 +55,7 @@ export function CreationPreview({
|
|
|
58
55
|
);
|
|
59
56
|
}
|
|
60
57
|
|
|
61
|
-
// For image
|
|
58
|
+
// For image content, use CreationImagePreview
|
|
62
59
|
return (
|
|
63
60
|
<CreationImagePreview
|
|
64
61
|
uri={uri}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "@umituz/react-native-design-system";
|
|
13
13
|
import type { CreationStatus, CreationTypeId } from "../../domain/types";
|
|
14
14
|
import { isInProgress } from "../../domain/utils";
|
|
15
|
+
import { shouldShowThumbnail } from "../../domain/utils/preview-helpers";
|
|
15
16
|
|
|
16
17
|
export interface CreationVideoPreviewProps {
|
|
17
18
|
/** Thumbnail image URL (optional) */
|
|
@@ -30,14 +31,6 @@ export interface CreationVideoPreviewProps {
|
|
|
30
31
|
readonly showLoadingIndicator?: boolean;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
/** Check if URL is a video URL (mp4, mov, etc.) */
|
|
34
|
-
function isVideoUrl(url?: string | null): boolean {
|
|
35
|
-
if (!url) return false;
|
|
36
|
-
const videoExtensions = [".mp4", ".mov", ".avi", ".webm", ".mkv"];
|
|
37
|
-
const lowerUrl = url.toLowerCase();
|
|
38
|
-
return videoExtensions.some((ext) => lowerUrl.includes(ext));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
34
|
export function CreationVideoPreview({
|
|
42
35
|
thumbnailUrl,
|
|
43
36
|
videoUrl: _videoUrl,
|
|
@@ -48,9 +41,20 @@ export function CreationVideoPreview({
|
|
|
48
41
|
}: CreationVideoPreviewProps) {
|
|
49
42
|
const tokens = useAppDesignTokens();
|
|
50
43
|
const inProgress = isInProgress(status);
|
|
44
|
+
const hasThumbnail = shouldShowThumbnail(thumbnailUrl, inProgress);
|
|
51
45
|
|
|
52
|
-
//
|
|
53
|
-
|
|
46
|
+
// Debug logging
|
|
47
|
+
if (__DEV__) {
|
|
48
|
+
console.log("[CreationVideoPreview]", {
|
|
49
|
+
thumbnailUrl,
|
|
50
|
+
status,
|
|
51
|
+
inProgress,
|
|
52
|
+
hasThumbnail,
|
|
53
|
+
willShowSpinner: inProgress && showLoadingIndicator,
|
|
54
|
+
willShowThumbnail: hasThumbnail,
|
|
55
|
+
willShowPlaceholder: !inProgress && !hasThumbnail,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
54
58
|
|
|
55
59
|
const styles = useMemo(
|
|
56
60
|
() =>
|
|
@@ -119,7 +123,7 @@ export function CreationVideoPreview({
|
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
// Show thumbnail with play icon overlay
|
|
122
|
-
if (hasThumbnail) {
|
|
126
|
+
if (hasThumbnail && thumbnailUrl) {
|
|
123
127
|
return (
|
|
124
128
|
<View style={styles.container}>
|
|
125
129
|
<Image
|
|
@@ -19,10 +19,6 @@ interface GalleryHeaderProps {
|
|
|
19
19
|
readonly filterButtons?: FilterButtonConfig[];
|
|
20
20
|
readonly showFilter?: boolean;
|
|
21
21
|
readonly style?: ViewStyle;
|
|
22
|
-
/** Number of pending/processing jobs to show as badge */
|
|
23
|
-
readonly pendingCount?: number;
|
|
24
|
-
/** Label for pending badge tooltip */
|
|
25
|
-
readonly pendingLabel?: string;
|
|
26
22
|
}
|
|
27
23
|
|
|
28
24
|
export const GalleryHeader: React.FC<GalleryHeaderProps> = ({
|
|
@@ -32,8 +28,6 @@ export const GalleryHeader: React.FC<GalleryHeaderProps> = ({
|
|
|
32
28
|
filterButtons = [],
|
|
33
29
|
showFilter = true,
|
|
34
30
|
style,
|
|
35
|
-
pendingCount = 0,
|
|
36
|
-
pendingLabel,
|
|
37
31
|
}) => {
|
|
38
32
|
const tokens = useAppDesignTokens();
|
|
39
33
|
const styles = useStyles(tokens);
|
|
@@ -102,18 +96,6 @@ const useStyles = (tokens: DesignTokens) =>
|
|
|
102
96
|
fontWeight: "700",
|
|
103
97
|
color: tokens.colors.textPrimary,
|
|
104
98
|
},
|
|
105
|
-
pendingBadge: {
|
|
106
|
-
flexDirection: "row",
|
|
107
|
-
alignItems: "center",
|
|
108
|
-
gap: 4,
|
|
109
|
-
paddingHorizontal: 8,
|
|
110
|
-
paddingVertical: 4,
|
|
111
|
-
borderRadius: 12,
|
|
112
|
-
},
|
|
113
|
-
pendingBadgeText: {
|
|
114
|
-
fontSize: 12,
|
|
115
|
-
fontWeight: "600",
|
|
116
|
-
},
|
|
117
99
|
subtitle: {
|
|
118
100
|
fontSize: 14,
|
|
119
101
|
color: tokens.colors.textSecondary,
|
|
@@ -120,8 +120,6 @@ export function CreationsGalleryScreen({
|
|
|
120
120
|
countLabel={t(config.translations.photoCount)}
|
|
121
121
|
showFilter={showFilter}
|
|
122
122
|
filterButtons={filterButtons}
|
|
123
|
-
pendingCount={filters.processingCount}
|
|
124
|
-
pendingLabel={t("creations.processing")}
|
|
125
123
|
/>
|
|
126
124
|
</View>
|
|
127
125
|
);
|