@umituz/react-native-ai-generation-content 1.15.0 → 1.15.2
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/features/audio-generation/index.ts +0 -1
- package/src/features/colorization/index.ts +0 -1
- package/src/features/face-swap/index.ts +0 -1
- package/src/features/future-prediction/index.ts +0 -1
- package/src/features/image-captioning/index.ts +0 -1
- package/src/features/inpainting/index.ts +0 -1
- package/src/features/photo-restoration/index.ts +0 -1
- package/src/features/sketch-to-image/index.ts +0 -1
- package/src/features/style-transfer/index.ts +0 -1
- package/src/features/text-to-image/index.ts +0 -1
- package/src/features/text-to-video/index.ts +0 -1
- package/src/features/upscaling/domain/index.ts +1 -0
- package/src/features/upscaling/domain/types/index.ts +2 -0
- package/src/features/upscaling/domain/types/provider.types.ts +23 -0
- package/src/features/upscaling/domain/types/upscale.types.ts +56 -0
- package/src/features/upscaling/index.ts +40 -2
- package/src/features/upscaling/infrastructure/index.ts +1 -0
- package/src/features/upscaling/infrastructure/services/index.ts +7 -0
- package/src/features/upscaling/infrastructure/services/upscale-executor.ts +64 -0
- package/src/features/upscaling/infrastructure/services/upscale-provider-registry.ts +77 -0
- package/src/features/upscaling/presentation/components/UpscaleFeature.tsx +168 -0
- package/src/features/upscaling/presentation/components/UpscaleResultView.tsx +94 -0
- package/src/features/upscaling/presentation/components/index.ts +4 -0
- package/src/features/upscaling/presentation/hooks/index.ts +5 -0
- package/src/features/upscaling/presentation/hooks/useUpscaleFeature.ts +132 -0
- package/src/features/upscaling/presentation/index.ts +2 -0
- package/src/index.ts +6 -1
- package/src/presentation/hooks/base/index.ts +9 -0
- package/src/presentation/hooks/base/types.ts +47 -0
- package/src/presentation/hooks/base/use-dual-image-feature.ts +178 -0
- package/src/presentation/hooks/base/use-image-with-prompt-feature.ts +170 -0
- package/src/presentation/hooks/base/use-single-image-feature.ts +154 -0
- package/src/presentation/hooks/index.ts +3 -0
- package/src/features/audio-generation/presentation/hooks.ts +0 -39
- package/src/features/colorization/presentation/hooks.ts +0 -39
- package/src/features/face-swap/presentation/hooks.ts +0 -41
- package/src/features/future-prediction/presentation/hooks.ts +0 -39
- package/src/features/image-captioning/presentation/hooks.ts +0 -39
- package/src/features/inpainting/presentation/hooks.ts +0 -39
- package/src/features/photo-restoration/presentation/hooks.ts +0 -39
- package/src/features/sketch-to-image/presentation/hooks.ts +0 -39
- package/src/features/style-transfer/presentation/hooks.ts +0 -39
- package/src/features/text-to-image/presentation/hooks.ts +0 -39
- package/src/features/text-to-video/presentation/hooks.ts +0 -39
- package/src/features/upscaling/domain/entities.ts +0 -42
- package/src/features/upscaling/presentation/hooks.ts +0 -39
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./types";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upscale Provider Types
|
|
3
|
+
* Interface for provider implementations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { UpscaleRequest, UpscaleResult } from "./upscale.types";
|
|
7
|
+
|
|
8
|
+
export interface IUpscaleProvider {
|
|
9
|
+
readonly providerId: string;
|
|
10
|
+
readonly providerName: string;
|
|
11
|
+
|
|
12
|
+
isAvailable(): boolean;
|
|
13
|
+
|
|
14
|
+
upscale(
|
|
15
|
+
request: UpscaleRequest,
|
|
16
|
+
onProgress?: (progress: number) => void,
|
|
17
|
+
): Promise<UpscaleResult>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface UpscaleProviderEntry {
|
|
21
|
+
provider: IUpscaleProvider;
|
|
22
|
+
priority: number;
|
|
23
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upscale Feature Types
|
|
3
|
+
* Request, Result, Config types for upscaling
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type UpscaleScaleFactor = 2 | 4 | 8;
|
|
7
|
+
|
|
8
|
+
export interface UpscaleOptions {
|
|
9
|
+
scaleFactor?: UpscaleScaleFactor;
|
|
10
|
+
enhanceFaces?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UpscaleRequest {
|
|
14
|
+
imageUri: string;
|
|
15
|
+
userId: string;
|
|
16
|
+
options?: UpscaleOptions;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface UpscaleResult {
|
|
20
|
+
success: boolean;
|
|
21
|
+
imageUrl?: string;
|
|
22
|
+
imageBase64?: string;
|
|
23
|
+
error?: string;
|
|
24
|
+
requestId?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface UpscaleFeatureState {
|
|
28
|
+
imageUri: string | null;
|
|
29
|
+
processedUrl: string | null;
|
|
30
|
+
isProcessing: boolean;
|
|
31
|
+
progress: number;
|
|
32
|
+
error: string | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface UpscaleTranslations {
|
|
36
|
+
uploadTitle: string;
|
|
37
|
+
uploadSubtitle: string;
|
|
38
|
+
uploadChange: string;
|
|
39
|
+
uploadAnalyzing: string;
|
|
40
|
+
description: string;
|
|
41
|
+
processingText: string;
|
|
42
|
+
processButtonText: string;
|
|
43
|
+
successText: string;
|
|
44
|
+
saveButtonText: string;
|
|
45
|
+
tryAnotherText: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface UpscaleFeatureConfig {
|
|
49
|
+
providerId?: string;
|
|
50
|
+
defaultScaleFactor?: UpscaleScaleFactor;
|
|
51
|
+
creditCost?: number;
|
|
52
|
+
onImageSelect?: (uri: string) => void;
|
|
53
|
+
onProcessingStart?: () => void;
|
|
54
|
+
onProcessingComplete?: (result: UpscaleResult) => void;
|
|
55
|
+
onError?: (error: string) => void;
|
|
56
|
+
}
|
|
@@ -1,2 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Upscaling Feature
|
|
3
|
+
* Provider-agnostic image upscaling feature
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Domain Types
|
|
7
|
+
export type {
|
|
8
|
+
UpscaleScaleFactor,
|
|
9
|
+
UpscaleOptions,
|
|
10
|
+
UpscaleRequest,
|
|
11
|
+
UpscaleResult,
|
|
12
|
+
UpscaleFeatureState,
|
|
13
|
+
UpscaleTranslations,
|
|
14
|
+
UpscaleFeatureConfig,
|
|
15
|
+
IUpscaleProvider,
|
|
16
|
+
UpscaleProviderEntry,
|
|
17
|
+
} from "./domain";
|
|
18
|
+
|
|
19
|
+
// Infrastructure Services
|
|
20
|
+
export {
|
|
21
|
+
upscaleProviderRegistry,
|
|
22
|
+
executeUpscale,
|
|
23
|
+
getAvailableProvider,
|
|
24
|
+
hasUpscaleProvider,
|
|
25
|
+
} from "./infrastructure";
|
|
26
|
+
export type { ExecuteUpscaleOptions } from "./infrastructure";
|
|
27
|
+
|
|
28
|
+
// Presentation Hooks
|
|
29
|
+
export { useUpscaleFeature } from "./presentation";
|
|
30
|
+
export type {
|
|
31
|
+
UseUpscaleFeatureProps,
|
|
32
|
+
UseUpscaleFeatureReturn,
|
|
33
|
+
} from "./presentation";
|
|
34
|
+
|
|
35
|
+
// Presentation Components
|
|
36
|
+
export { UpscaleFeature, UpscaleResultView } from "./presentation";
|
|
37
|
+
export type {
|
|
38
|
+
UpscaleFeatureProps,
|
|
39
|
+
UpscaleResultViewProps,
|
|
40
|
+
} from "./presentation";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./services";
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upscale Executor
|
|
3
|
+
* Executes upscale operations using registered providers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { upscaleProviderRegistry } from "./upscale-provider-registry";
|
|
7
|
+
import type { UpscaleRequest, UpscaleResult } from "../../domain/types";
|
|
8
|
+
|
|
9
|
+
declare const __DEV__: boolean;
|
|
10
|
+
|
|
11
|
+
export interface ExecuteUpscaleOptions {
|
|
12
|
+
providerId?: string;
|
|
13
|
+
onProgress?: (progress: number) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function executeUpscale(
|
|
17
|
+
request: UpscaleRequest,
|
|
18
|
+
options?: ExecuteUpscaleOptions,
|
|
19
|
+
): Promise<UpscaleResult> {
|
|
20
|
+
const provider = upscaleProviderRegistry.get(options?.providerId);
|
|
21
|
+
|
|
22
|
+
if (!provider) {
|
|
23
|
+
return {
|
|
24
|
+
success: false,
|
|
25
|
+
error: "No upscale provider available",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!provider.isAvailable()) {
|
|
30
|
+
return {
|
|
31
|
+
success: false,
|
|
32
|
+
error: `Provider ${provider.providerId} is not available`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (__DEV__) {
|
|
37
|
+
// eslint-disable-next-line no-console
|
|
38
|
+
console.log(`[UpscaleExecutor] Using provider: ${provider.providerId}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
return await provider.upscale(request, options?.onProgress);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
45
|
+
|
|
46
|
+
if (__DEV__) {
|
|
47
|
+
// eslint-disable-next-line no-console
|
|
48
|
+
console.error("[UpscaleExecutor] Error:", message);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
success: false,
|
|
53
|
+
error: message,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function getAvailableProvider(providerId?: string) {
|
|
59
|
+
return upscaleProviderRegistry.get(providerId);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function hasUpscaleProvider(providerId: string): boolean {
|
|
63
|
+
return upscaleProviderRegistry.hasProvider(providerId);
|
|
64
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upscale Provider Registry
|
|
3
|
+
* Manages registered upscale providers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
IUpscaleProvider,
|
|
8
|
+
UpscaleProviderEntry,
|
|
9
|
+
} from "../../domain/types";
|
|
10
|
+
|
|
11
|
+
declare const __DEV__: boolean;
|
|
12
|
+
|
|
13
|
+
class UpscaleProviderRegistry {
|
|
14
|
+
private providers: Map<string, UpscaleProviderEntry> = new Map();
|
|
15
|
+
private defaultProviderId: string | null = null;
|
|
16
|
+
|
|
17
|
+
register(provider: IUpscaleProvider, priority = 0): void {
|
|
18
|
+
this.providers.set(provider.providerId, { provider, priority });
|
|
19
|
+
|
|
20
|
+
if (__DEV__) {
|
|
21
|
+
// eslint-disable-next-line no-console
|
|
22
|
+
console.log(
|
|
23
|
+
`[UpscaleRegistry] Registered provider: ${provider.providerId}`,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
unregister(providerId: string): void {
|
|
29
|
+
this.providers.delete(providerId);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
setDefault(providerId: string): void {
|
|
33
|
+
if (!this.providers.has(providerId)) {
|
|
34
|
+
throw new Error(`Provider ${providerId} not registered`);
|
|
35
|
+
}
|
|
36
|
+
this.defaultProviderId = providerId;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get(providerId?: string): IUpscaleProvider | null {
|
|
40
|
+
const id = providerId || this.defaultProviderId;
|
|
41
|
+
if (!id) return this.getHighestPriority();
|
|
42
|
+
|
|
43
|
+
const entry = this.providers.get(id);
|
|
44
|
+
return entry?.provider || null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getAvailable(): IUpscaleProvider | null {
|
|
48
|
+
const sorted = this.getSortedProviders();
|
|
49
|
+
return sorted.find((p) => p.isAvailable()) || null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
hasProvider(providerId: string): boolean {
|
|
53
|
+
return this.providers.has(providerId);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
getProviderIds(): string[] {
|
|
57
|
+
return Array.from(this.providers.keys());
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private getHighestPriority(): IUpscaleProvider | null {
|
|
61
|
+
const sorted = this.getSortedProviders();
|
|
62
|
+
return sorted[0] || null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private getSortedProviders(): IUpscaleProvider[] {
|
|
66
|
+
return Array.from(this.providers.values())
|
|
67
|
+
.sort((a, b) => b.priority - a.priority)
|
|
68
|
+
.map((entry) => entry.provider);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
reset(): void {
|
|
72
|
+
this.providers.clear();
|
|
73
|
+
this.defaultProviderId = null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const upscaleProviderRegistry = new UpscaleProviderRegistry();
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UpscaleFeature Component
|
|
3
|
+
* Main upscale feature UI component
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useCallback, useMemo } from "react";
|
|
7
|
+
import { View, ScrollView, StyleSheet } from "react-native";
|
|
8
|
+
import {
|
|
9
|
+
useAppDesignTokens,
|
|
10
|
+
PhotoUploadCard,
|
|
11
|
+
AtomicText,
|
|
12
|
+
AtomicButton,
|
|
13
|
+
} from "@umituz/react-native-design-system";
|
|
14
|
+
import { UpscaleResultView } from "./UpscaleResultView";
|
|
15
|
+
import type {
|
|
16
|
+
UpscaleTranslations,
|
|
17
|
+
UpscaleFeatureConfig,
|
|
18
|
+
} from "../../domain/types";
|
|
19
|
+
|
|
20
|
+
export interface UpscaleFeatureProps {
|
|
21
|
+
imageUri: string | null;
|
|
22
|
+
processedUrl: string | null;
|
|
23
|
+
isProcessing: boolean;
|
|
24
|
+
progress: number;
|
|
25
|
+
error: string | null;
|
|
26
|
+
translations: UpscaleTranslations;
|
|
27
|
+
config?: UpscaleFeatureConfig;
|
|
28
|
+
onSelectImage: () => void;
|
|
29
|
+
onProcess: () => void;
|
|
30
|
+
onSave: () => void;
|
|
31
|
+
onReset: () => void;
|
|
32
|
+
renderProcessingModal?: (props: {
|
|
33
|
+
visible: boolean;
|
|
34
|
+
progress: number;
|
|
35
|
+
}) => React.ReactNode;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const UpscaleFeature: React.FC<UpscaleFeatureProps> = ({
|
|
39
|
+
imageUri,
|
|
40
|
+
processedUrl,
|
|
41
|
+
isProcessing,
|
|
42
|
+
progress,
|
|
43
|
+
error,
|
|
44
|
+
translations,
|
|
45
|
+
onSelectImage,
|
|
46
|
+
onProcess,
|
|
47
|
+
onSave,
|
|
48
|
+
onReset,
|
|
49
|
+
renderProcessingModal,
|
|
50
|
+
}) => {
|
|
51
|
+
const tokens = useAppDesignTokens();
|
|
52
|
+
|
|
53
|
+
const photoTranslations = useMemo(
|
|
54
|
+
() => ({
|
|
55
|
+
tapToUpload: translations.uploadTitle,
|
|
56
|
+
selectPhoto: translations.uploadSubtitle,
|
|
57
|
+
change: translations.uploadChange,
|
|
58
|
+
analyzing: translations.uploadAnalyzing,
|
|
59
|
+
}),
|
|
60
|
+
[translations],
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const handleProcess = useCallback(() => {
|
|
64
|
+
onProcess();
|
|
65
|
+
}, [onProcess]);
|
|
66
|
+
|
|
67
|
+
if (processedUrl) {
|
|
68
|
+
return (
|
|
69
|
+
<ScrollView
|
|
70
|
+
style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
|
|
71
|
+
contentContainerStyle={styles.content}
|
|
72
|
+
showsVerticalScrollIndicator={false}
|
|
73
|
+
>
|
|
74
|
+
<UpscaleResultView
|
|
75
|
+
imageUrl={processedUrl}
|
|
76
|
+
translations={translations}
|
|
77
|
+
onSave={onSave}
|
|
78
|
+
onReset={onReset}
|
|
79
|
+
/>
|
|
80
|
+
</ScrollView>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<>
|
|
86
|
+
<ScrollView
|
|
87
|
+
style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
|
|
88
|
+
contentContainerStyle={styles.content}
|
|
89
|
+
showsVerticalScrollIndicator={false}
|
|
90
|
+
>
|
|
91
|
+
<AtomicText
|
|
92
|
+
type="bodyLarge"
|
|
93
|
+
style={[styles.description, { color: tokens.colors.textSecondary }]}
|
|
94
|
+
>
|
|
95
|
+
{translations.description}
|
|
96
|
+
</AtomicText>
|
|
97
|
+
|
|
98
|
+
<PhotoUploadCard
|
|
99
|
+
imageUri={imageUri}
|
|
100
|
+
onPress={onSelectImage}
|
|
101
|
+
isValidating={isProcessing}
|
|
102
|
+
disabled={isProcessing}
|
|
103
|
+
translations={photoTranslations}
|
|
104
|
+
config={{
|
|
105
|
+
aspectRatio: 1,
|
|
106
|
+
borderRadius: 24,
|
|
107
|
+
showValidationStatus: false,
|
|
108
|
+
allowChange: true,
|
|
109
|
+
}}
|
|
110
|
+
/>
|
|
111
|
+
|
|
112
|
+
{error && (
|
|
113
|
+
<View
|
|
114
|
+
style={[
|
|
115
|
+
styles.errorContainer,
|
|
116
|
+
{ backgroundColor: `${tokens.colors.error}15` },
|
|
117
|
+
]}
|
|
118
|
+
>
|
|
119
|
+
<AtomicText type="bodyMedium" style={{ color: tokens.colors.error }}>
|
|
120
|
+
{error}
|
|
121
|
+
</AtomicText>
|
|
122
|
+
</View>
|
|
123
|
+
)}
|
|
124
|
+
|
|
125
|
+
<View style={styles.buttonContainer}>
|
|
126
|
+
<AtomicButton
|
|
127
|
+
title={
|
|
128
|
+
isProcessing
|
|
129
|
+
? translations.processingText
|
|
130
|
+
: translations.processButtonText
|
|
131
|
+
}
|
|
132
|
+
onPress={handleProcess}
|
|
133
|
+
disabled={!imageUri || isProcessing}
|
|
134
|
+
variant="primary"
|
|
135
|
+
size="lg"
|
|
136
|
+
/>
|
|
137
|
+
</View>
|
|
138
|
+
</ScrollView>
|
|
139
|
+
|
|
140
|
+
{renderProcessingModal?.({ visible: isProcessing, progress })}
|
|
141
|
+
</>
|
|
142
|
+
);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const styles = StyleSheet.create({
|
|
146
|
+
container: {
|
|
147
|
+
flex: 1,
|
|
148
|
+
},
|
|
149
|
+
content: {
|
|
150
|
+
paddingVertical: 16,
|
|
151
|
+
},
|
|
152
|
+
description: {
|
|
153
|
+
textAlign: "center",
|
|
154
|
+
marginHorizontal: 24,
|
|
155
|
+
marginBottom: 24,
|
|
156
|
+
lineHeight: 24,
|
|
157
|
+
},
|
|
158
|
+
errorContainer: {
|
|
159
|
+
marginHorizontal: 24,
|
|
160
|
+
marginBottom: 16,
|
|
161
|
+
padding: 16,
|
|
162
|
+
borderRadius: 12,
|
|
163
|
+
},
|
|
164
|
+
buttonContainer: {
|
|
165
|
+
marginHorizontal: 24,
|
|
166
|
+
marginTop: 8,
|
|
167
|
+
},
|
|
168
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UpscaleResultView Component
|
|
3
|
+
* Displays the upscaled image result
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { View, Image, StyleSheet } from "react-native";
|
|
8
|
+
import {
|
|
9
|
+
AtomicText,
|
|
10
|
+
AtomicButton,
|
|
11
|
+
useAppDesignTokens,
|
|
12
|
+
} from "@umituz/react-native-design-system";
|
|
13
|
+
import type { UpscaleTranslations } from "../../domain/types";
|
|
14
|
+
|
|
15
|
+
export interface UpscaleResultViewProps {
|
|
16
|
+
imageUrl: string;
|
|
17
|
+
translations: Pick<
|
|
18
|
+
UpscaleTranslations,
|
|
19
|
+
"successText" | "saveButtonText" | "tryAnotherText"
|
|
20
|
+
>;
|
|
21
|
+
onSave: () => void;
|
|
22
|
+
onReset: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const UpscaleResultView: React.FC<UpscaleResultViewProps> = ({
|
|
26
|
+
imageUrl,
|
|
27
|
+
translations,
|
|
28
|
+
onSave,
|
|
29
|
+
onReset,
|
|
30
|
+
}) => {
|
|
31
|
+
const tokens = useAppDesignTokens();
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<View style={styles.container}>
|
|
35
|
+
<AtomicText
|
|
36
|
+
type="headlineMedium"
|
|
37
|
+
style={[styles.title, { color: tokens.colors.success }]}
|
|
38
|
+
>
|
|
39
|
+
{translations.successText}
|
|
40
|
+
</AtomicText>
|
|
41
|
+
|
|
42
|
+
<View
|
|
43
|
+
style={[
|
|
44
|
+
styles.imageCard,
|
|
45
|
+
{ backgroundColor: tokens.colors.surfaceSecondary },
|
|
46
|
+
]}
|
|
47
|
+
>
|
|
48
|
+
<Image
|
|
49
|
+
source={{ uri: imageUrl }}
|
|
50
|
+
style={styles.image}
|
|
51
|
+
resizeMode="contain"
|
|
52
|
+
/>
|
|
53
|
+
</View>
|
|
54
|
+
|
|
55
|
+
<View style={styles.actions}>
|
|
56
|
+
<AtomicButton
|
|
57
|
+
title={translations.saveButtonText}
|
|
58
|
+
onPress={onSave}
|
|
59
|
+
variant="primary"
|
|
60
|
+
size="lg"
|
|
61
|
+
/>
|
|
62
|
+
<AtomicButton
|
|
63
|
+
title={translations.tryAnotherText}
|
|
64
|
+
onPress={onReset}
|
|
65
|
+
variant="secondary"
|
|
66
|
+
size="lg"
|
|
67
|
+
/>
|
|
68
|
+
</View>
|
|
69
|
+
</View>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const styles = StyleSheet.create({
|
|
74
|
+
container: {
|
|
75
|
+
flex: 1,
|
|
76
|
+
paddingHorizontal: 24,
|
|
77
|
+
},
|
|
78
|
+
title: {
|
|
79
|
+
textAlign: "center",
|
|
80
|
+
marginBottom: 24,
|
|
81
|
+
},
|
|
82
|
+
imageCard: {
|
|
83
|
+
borderRadius: 24,
|
|
84
|
+
overflow: "hidden",
|
|
85
|
+
marginBottom: 24,
|
|
86
|
+
},
|
|
87
|
+
image: {
|
|
88
|
+
width: "100%",
|
|
89
|
+
aspectRatio: 1,
|
|
90
|
+
},
|
|
91
|
+
actions: {
|
|
92
|
+
gap: 12,
|
|
93
|
+
},
|
|
94
|
+
});
|