@umituz/react-native-ai-generation-content 1.20.45 → 1.20.47

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.45",
3
+ "version": "1.20.47",
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",
package/src/index.ts CHANGED
@@ -56,11 +56,13 @@ export {
56
56
  extractAudioUrl, extractImageUrls, cleanBase64, addBase64Prefix, preparePhoto, preparePhotos,
57
57
  isValidBase64, getBase64Size, getBase64SizeMB, prepareImage, createDevCallbacks, createFeatureUtils,
58
58
  showVideoGenerationSuccess, handleGenerationError, showContentModerationWarning,
59
+ saveMediaToGallery, shareMedia, createSaveHandler, createShareHandler, createMediaHandlers,
59
60
  } from "./infrastructure/utils";
60
61
 
61
62
  export type {
62
63
  IntervalOptions, ProgressOptions, StatusCheckResult, ResultValidation, ValidateResultOptions,
63
64
  PhotoInput, PreparedImage, ImageSelector, VideoSaver, AlertFunction, FeatureUtilsConfig, VideoAlertFunction,
65
+ MediaActionResult, MediaActionTranslations, ToastConfig,
64
66
  } from "./infrastructure/utils";
65
67
 
66
68
  export { enhancePromptWithLanguage, getSupportedLanguages, getLanguageName, ModerationWrapper, generateSynchronously } from "./infrastructure/wrappers";
@@ -72,6 +74,7 @@ export {
72
74
  useAIGenerateState, AIGenerateStep,
73
75
  useGenerationOrchestrator, useImageGeneration, useVideoGeneration, useAIFeatureGeneration,
74
76
  createGenerationError, getAlertMessage, parseError,
77
+ useAIGenerateWizard, createWizardTranslations,
75
78
  } from "./presentation/hooks";
76
79
 
77
80
  export type {
@@ -84,6 +87,7 @@ export type {
84
87
  SingleImageInput, DualImageInput, ImageGenerationInput, ImageGenerationConfig,
85
88
  DualImageVideoInput, VideoGenerationConfig,
86
89
  UploadedImage,
90
+ AIGenerateWizardConfig, AIGenerateWizardTranslations, UseAIGenerateWizardReturn,
87
91
  } from "./presentation/hooks";
88
92
 
89
93
  export {
@@ -12,3 +12,4 @@ export * from "./photo-generation";
12
12
  export * from "./feature-utils";
13
13
  export * from "./video-helpers";
14
14
  export * from "./video-result-extractor.util";
15
+ export * from "./media-actions.util";
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Media Actions Utilities
3
+ * Provides save to gallery and share functionality for generated media
4
+ */
5
+
6
+ export interface MediaActionResult {
7
+ readonly success: boolean;
8
+ readonly error?: string;
9
+ }
10
+
11
+ export interface MediaActionTranslations {
12
+ readonly success?: string;
13
+ readonly error?: string;
14
+ readonly permissionDenied?: string;
15
+ readonly saveFailed?: string;
16
+ readonly shareFailed?: string;
17
+ readonly shareNotAvailable?: string;
18
+ }
19
+
20
+ export interface ToastConfig {
21
+ readonly show: (config: {
22
+ type: "success" | "error";
23
+ text1: string;
24
+ text2?: string;
25
+ }) => void;
26
+ }
27
+
28
+ /**
29
+ * Downloads a file from URL to local storage
30
+ * @param uri - URL or local path
31
+ * @param prefix - File name prefix
32
+ * @returns Local file path
33
+ */
34
+ const downloadToLocal = async (
35
+ uri: string,
36
+ prefix: string = "media"
37
+ ): Promise<string> => {
38
+ if (!uri.startsWith("http")) {
39
+ return uri;
40
+ }
41
+
42
+ const FileSystem = require("expo-file-system");
43
+ const fileExt = uri.split(".").pop()?.split("?")[0] || "jpg";
44
+ const fileRef =
45
+ FileSystem.documentDirectory + `${prefix}_${Date.now()}.${fileExt}`;
46
+ const { uri: downloadedUri } = await FileSystem.downloadAsync(uri, fileRef);
47
+ return downloadedUri;
48
+ };
49
+
50
+ /**
51
+ * Saves media to device gallery
52
+ * @param uri - URL or local path of the media
53
+ * @param translations - Optional translated messages
54
+ * @param toast - Optional toast instance for notifications
55
+ * @returns Result with success status
56
+ */
57
+ export const saveMediaToGallery = async (
58
+ uri: string,
59
+ translations?: MediaActionTranslations,
60
+ toast?: ToastConfig
61
+ ): Promise<MediaActionResult> => {
62
+ try {
63
+ const MediaLibrary = require("expo-media-library");
64
+
65
+ const { status } = await MediaLibrary.requestPermissionsAsync();
66
+ if (status !== "granted") {
67
+ const errorMsg = translations?.permissionDenied || "Permission denied";
68
+ toast?.show({
69
+ type: "error",
70
+ text1: translations?.error || "Error",
71
+ text2: errorMsg,
72
+ });
73
+ return { success: false, error: errorMsg };
74
+ }
75
+
76
+ const localUri = await downloadToLocal(uri, "download");
77
+ await MediaLibrary.saveToLibraryAsync(localUri);
78
+
79
+ toast?.show({
80
+ type: "success",
81
+ text1: translations?.success || "Success",
82
+ text2: "Saved to gallery",
83
+ });
84
+
85
+ return { success: true };
86
+ } catch (error) {
87
+ // Debug logging in development
88
+ try {
89
+ if ((global as Record<string, unknown>).__DEV__) {
90
+ console.error("[MediaActions] Save failed:", error);
91
+ }
92
+ } catch {
93
+ // Ignore if __DEV__ check fails
94
+ }
95
+ const errorMsg = translations?.saveFailed || "Failed to save media";
96
+ toast?.show({
97
+ type: "error",
98
+ text1: translations?.error || "Error",
99
+ text2: errorMsg,
100
+ });
101
+ return {
102
+ success: false,
103
+ error: error instanceof Error ? error.message : errorMsg,
104
+ };
105
+ }
106
+ };
107
+
108
+ /**
109
+ * Shares media using device share sheet
110
+ * @param uri - URL or local path of the media
111
+ * @param translations - Optional translated messages
112
+ * @param toast - Optional toast instance for notifications
113
+ * @returns Result with success status
114
+ */
115
+ export const shareMedia = async (
116
+ uri: string,
117
+ translations?: MediaActionTranslations,
118
+ toast?: ToastConfig
119
+ ): Promise<MediaActionResult> => {
120
+ try {
121
+ const Sharing = require("expo-sharing");
122
+
123
+ const isAvailable = await Sharing.isAvailableAsync();
124
+ if (!isAvailable) {
125
+ const errorMsg =
126
+ translations?.shareNotAvailable ||
127
+ "Sharing is not available on this device";
128
+ toast?.show({
129
+ type: "error",
130
+ text1: translations?.error || "Error",
131
+ text2: errorMsg,
132
+ });
133
+ return { success: false, error: errorMsg };
134
+ }
135
+
136
+ const localUri = await downloadToLocal(uri, "share");
137
+ await Sharing.shareAsync(localUri);
138
+
139
+ return { success: true };
140
+ } catch (error) {
141
+ // Debug logging in development
142
+ try {
143
+ if ((global as Record<string, unknown>).__DEV__) {
144
+ console.error("[MediaActions] Share failed:", error);
145
+ }
146
+ } catch {
147
+ // Ignore if __DEV__ check fails
148
+ }
149
+ const errorMsg = translations?.shareFailed || "Failed to share media";
150
+ toast?.show({
151
+ type: "error",
152
+ text1: translations?.error || "Error",
153
+ text2: errorMsg,
154
+ });
155
+ return {
156
+ success: false,
157
+ error: error instanceof Error ? error.message : errorMsg,
158
+ };
159
+ }
160
+ };
161
+
162
+ /**
163
+ * Creates save handler for use in wizard flows
164
+ * @param translations - Translated messages
165
+ * @param toast - Toast instance for notifications
166
+ * @returns Save handler function
167
+ */
168
+ export const createSaveHandler = (
169
+ translations?: MediaActionTranslations,
170
+ toast?: ToastConfig
171
+ ) => {
172
+ return async (uri: string): Promise<void> => {
173
+ await saveMediaToGallery(uri, translations, toast);
174
+ };
175
+ };
176
+
177
+ /**
178
+ * Creates share handler for use in wizard flows
179
+ * @param translations - Translated messages
180
+ * @param toast - Toast instance for notifications
181
+ * @returns Share handler function
182
+ */
183
+ export const createShareHandler = (
184
+ translations?: MediaActionTranslations,
185
+ toast?: ToastConfig
186
+ ) => {
187
+ return async (uri: string): Promise<void> => {
188
+ await shareMedia(uri, translations, toast);
189
+ };
190
+ };
191
+
192
+ /**
193
+ * Creates both save and share handlers
194
+ * @param translations - Translated messages
195
+ * @param toast - Toast instance for notifications
196
+ * @returns Object with onSave and onShare handlers
197
+ */
198
+ export const createMediaHandlers = (
199
+ translations?: MediaActionTranslations,
200
+ toast?: ToastConfig
201
+ ) => {
202
+ return {
203
+ onSave: createSaveHandler(translations, toast),
204
+ onShare: createShareHandler(translations, toast),
205
+ };
206
+ };
@@ -63,3 +63,10 @@ export type {
63
63
 
64
64
  export { useAIGenerateState, AIGenerateStep } from "./generation/useAIGenerateState";
65
65
  export type { UploadedImage } from "./generation/useAIGenerateState";
66
+
67
+ export { useAIGenerateWizard, createWizardTranslations } from "./useAIGenerateWizard";
68
+ export type {
69
+ AIGenerateWizardConfig,
70
+ AIGenerateWizardTranslations,
71
+ UseAIGenerateWizardReturn,
72
+ } from "./useAIGenerateWizard";
@@ -0,0 +1,164 @@
1
+ /**
2
+ * useAIGenerateWizard Hook
3
+ * Provides all necessary logic for AI generation wizard screens
4
+ * Centralizes save, share, and generation handling
5
+ */
6
+
7
+ import { useCallback, useMemo } from "react";
8
+ import {
9
+ saveMediaToGallery,
10
+ shareMedia,
11
+ type MediaActionTranslations,
12
+ type ToastConfig,
13
+ } from "../../infrastructure/utils/media-actions.util";
14
+ import { useAIFeatureGeneration } from "./generation";
15
+ import type { AlertMessages } from "./generation";
16
+
17
+ export interface AIGenerateWizardConfig {
18
+ readonly featureType: string;
19
+ readonly userId?: string;
20
+ readonly alertMessages?: AlertMessages;
21
+ }
22
+
23
+ export interface AIGenerateWizardTranslations {
24
+ readonly headerTitle: string;
25
+ readonly uploadSubtitle: string;
26
+ readonly uploadSubtitle2: string;
27
+ readonly continue: string;
28
+ readonly tapToUpload: string;
29
+ readonly selectPhoto: string;
30
+ readonly change: string;
31
+ readonly analyzing: string;
32
+ readonly error: string;
33
+ readonly uploadFailed: string;
34
+ readonly aiDisclosure: string;
35
+ readonly heroTitle: string;
36
+ readonly heroSubtitle: string;
37
+ readonly presetsTitle: string;
38
+ readonly showAdvancedLabel: string;
39
+ readonly hideAdvancedLabel: string;
40
+ readonly promptTitle: string;
41
+ readonly promptPlaceholder: string;
42
+ readonly styleTitle: string;
43
+ readonly durationTitle: string;
44
+ readonly generateButton: string;
45
+ readonly generatingButton: string;
46
+ readonly processingTitle: string;
47
+ readonly processingMessage: string;
48
+ readonly processingHint: string;
49
+ readonly successTitle: string;
50
+ readonly saveButton: string;
51
+ readonly shareButton: string;
52
+ readonly tryAgainButton: string;
53
+ readonly fileTooLarge: string;
54
+ readonly maxFileSize: string;
55
+ }
56
+
57
+ export interface UseAIGenerateWizardReturn {
58
+ readonly generate: (data: {
59
+ prompt: string;
60
+ style: string;
61
+ duration: number;
62
+ images: { uri: string }[];
63
+ }) => Promise<string | null | void>;
64
+ readonly handleSave: (uri: string) => Promise<void>;
65
+ readonly handleShare: (uri: string) => Promise<void>;
66
+ }
67
+
68
+ /**
69
+ * Hook that provides all AI generation wizard functionality
70
+ * Handles generation, save, and share operations
71
+ */
72
+ export function useAIGenerateWizard(
73
+ config: AIGenerateWizardConfig,
74
+ translations: { error?: string; success?: string },
75
+ toast?: ToastConfig
76
+ ): UseAIGenerateWizardReturn {
77
+ const { generate } = useAIFeatureGeneration({
78
+ featureType: config.featureType as never,
79
+ alertMessages: config.alertMessages ?? {
80
+ success: translations.success ?? "Generation successful",
81
+ error: translations.error ?? "Error",
82
+ creditLimit: "Insufficient credits",
83
+ },
84
+ userId: config.userId,
85
+ });
86
+
87
+ const mediaTranslations: MediaActionTranslations = useMemo(
88
+ () => ({
89
+ success: translations.success,
90
+ error: translations.error,
91
+ permissionDenied: "Permission denied",
92
+ saveFailed: "Failed to save media",
93
+ shareFailed: "Failed to share media",
94
+ shareNotAvailable: "Sharing is not available on this device",
95
+ }),
96
+ [translations]
97
+ );
98
+
99
+ const handleSave = useCallback(
100
+ async (uri: string): Promise<void> => {
101
+ await saveMediaToGallery(uri, mediaTranslations, toast);
102
+ },
103
+ [mediaTranslations, toast]
104
+ );
105
+
106
+ const handleShare = useCallback(
107
+ async (uri: string): Promise<void> => {
108
+ await shareMedia(uri, mediaTranslations, toast);
109
+ },
110
+ [mediaTranslations, toast]
111
+ );
112
+
113
+ return {
114
+ generate,
115
+ handleSave,
116
+ handleShare,
117
+ };
118
+ }
119
+
120
+ /**
121
+ * Creates translations object from t function
122
+ * @param t - Translation function
123
+ * @param featureType - Feature type for feature-specific keys
124
+ * @returns Translations object for AIGenerateWizardFlow
125
+ */
126
+ export function createWizardTranslations(
127
+ t: (key: string) => string,
128
+ featureType: string
129
+ ): AIGenerateWizardTranslations {
130
+ const featureKey = featureType.replace(/-/g, "_");
131
+ return {
132
+ headerTitle: t(`home.ai_features.${featureKey}.title`),
133
+ uploadSubtitle: t("imageProcessing.upload.subtitle"),
134
+ uploadSubtitle2: t("imageProcessing.upload.subtitle_2"),
135
+ continue: t("common.actions.continue"),
136
+ tapToUpload: t("imageProcessing.upload.tapToUpload"),
137
+ selectPhoto: t("imageProcessing.upload.selectPhoto"),
138
+ change: t("imageProcessing.upload.change"),
139
+ analyzing: t("imageProcessing.upload.analyzing"),
140
+ error: t("common.error"),
141
+ uploadFailed: t("common.errors.upload_failed"),
142
+ aiDisclosure: t("imageProcessing.upload.aiDisclosure"),
143
+ heroTitle: "",
144
+ heroSubtitle: "",
145
+ presetsTitle: t("common.generation.presets.title"),
146
+ showAdvancedLabel: t("common.generation.advanced.show"),
147
+ hideAdvancedLabel: t("common.generation.advanced.hide"),
148
+ promptTitle: t("common.prompts.custom_prompt"),
149
+ promptPlaceholder: t("common.prompts.placeholder"),
150
+ styleTitle: t("common.generation.style_selector.video_title"),
151
+ durationTitle: t("common.generation.duration_selector.title"),
152
+ generateButton: t("common.actions.generate"),
153
+ generatingButton: t("common.actions.generating"),
154
+ processingTitle: t("common.generation.progress.title"),
155
+ processingMessage: t("common.generation.progress.hint"),
156
+ processingHint: t("common.generation.progress.backgroundHint"),
157
+ successTitle: t("common.generation.success"),
158
+ saveButton: t("common.actions.save"),
159
+ shareButton: t("common.actions.share"),
160
+ tryAgainButton: t("common.actions.try_again"),
161
+ fileTooLarge: t("common.errors.file_too_large"),
162
+ maxFileSize: t("common.errors.max_file_size"),
163
+ };
164
+ }