@umituz/react-native-ai-generation-content 1.12.21 → 1.12.23
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 +33 -15
- package/src/domains/content-moderation/infrastructure/services/content-moderation.service.ts +4 -32
- package/src/domains/content-moderation/infrastructure/services/moderators/base.moderator.ts +1 -1
- package/src/domains/face-detection/infrastructure/validators/faceValidator.ts +1 -1
- package/src/domains/face-detection/presentation/components/FaceValidationStatus.tsx +3 -3
- package/src/domains/feature-background/presentation/components/BackgroundFeature.tsx +5 -4
- package/src/domains/feature-background/presentation/components/ComparisonSlider.tsx +45 -51
- package/src/domains/feature-background/presentation/components/ErrorDisplay.tsx +5 -3
- package/src/domains/feature-background/presentation/components/ModeSelector.tsx +2 -2
- package/src/domains/feature-background/presentation/hooks/useBackgroundFeature.ts +3 -2
- package/src/domains/prompts/domain/entities/FuturePredictionConfig.ts +2 -1
- package/src/domains/prompts/domain/entities/GeneratedPrompt.ts +0 -1
- package/src/domains/prompts/domain/repositories/IAIPromptServices.ts +6 -12
- package/src/domains/prompts/infrastructure/repositories/PromptHistoryRepository.ts +42 -42
- package/src/domains/prompts/infrastructure/repositories/TemplateRepository.ts +42 -42
- package/src/domains/prompts/infrastructure/services/BackgroundRemovalService.ts +7 -7
- package/src/domains/prompts/infrastructure/services/ColorizationService.ts +7 -7
- package/src/domains/prompts/infrastructure/services/FaceSwapService.ts +19 -20
- package/src/domains/prompts/infrastructure/services/FuturePredictionService.ts +11 -31
- package/src/domains/prompts/infrastructure/services/ImageEnhancementService.ts +7 -7
- package/src/domains/prompts/infrastructure/services/PhotoRestorationService.ts +7 -7
- package/src/domains/prompts/infrastructure/services/PromptGenerationService.ts +13 -13
- package/src/domains/prompts/infrastructure/services/StyleTransferService.ts +8 -8
- package/src/domains/prompts/infrastructure/services/TextGenerationService.ts +7 -7
- package/src/domains/prompts/presentation/hooks/useAIServices.ts +30 -28
- package/src/domains/prompts/presentation/hooks/useFaceSwap.ts +1 -2
- package/src/domains/prompts/presentation/hooks/usePromptGeneration.ts +4 -5
- package/src/domains/prompts/presentation/hooks/useStyleTransfer.ts +1 -1
- package/src/domains/prompts/presentation/hooks/useTemplateRepository.ts +3 -3
- package/src/domains/prompts/presentation/theme/utils.ts +1 -1
- package/src/index.ts +0 -5
- package/src/infrastructure/utils/status-checker.util.ts +4 -4
- package/src/infrastructure/wrappers/synchronous-generation.wrapper.ts +3 -3
- package/src/presentation/components/result/GenerationResultContent.tsx +21 -22
- package/src/presentation/components/result/ResultActions.tsx +51 -52
- package/src/presentation/components/result/ResultHeader.tsx +24 -25
- package/src/presentation/components/result/ResultImageCard.tsx +19 -20
- package/src/presentation/components/result/ResultStoryCard.tsx +23 -24
- package/src/presentation/hooks/photo-generation.types.ts +4 -4
- package/src/presentation/hooks/usePhotoGeneration.ts +18 -13
- package/src/domains/creations/application/services/CreationsService.ts +0 -72
- package/src/domains/creations/domain/entities/Creation.ts +0 -54
- package/src/domains/creations/domain/entities/index.ts +0 -6
- package/src/domains/creations/domain/repositories/ICreationsRepository.ts +0 -25
- package/src/domains/creations/domain/repositories/index.ts +0 -5
- package/src/domains/creations/domain/services/ICreationsStorageService.ts +0 -13
- package/src/domains/creations/domain/value-objects/CreationsConfig.ts +0 -77
- package/src/domains/creations/domain/value-objects/index.ts +0 -12
- package/src/domains/creations/index.ts +0 -84
- package/src/domains/creations/infrastructure/adapters/createRepository.ts +0 -54
- package/src/domains/creations/infrastructure/adapters/index.ts +0 -5
- package/src/domains/creations/infrastructure/repositories/CreationsRepository.ts +0 -263
- package/src/domains/creations/infrastructure/repositories/index.ts +0 -8
- package/src/domains/creations/infrastructure/services/CreationsStorageService.ts +0 -48
- package/src/domains/creations/presentation/components/CreationCard.tsx +0 -196
- package/src/domains/creations/presentation/components/CreationDetail/DetailActions.tsx +0 -76
- package/src/domains/creations/presentation/components/CreationDetail/DetailHeader.tsx +0 -81
- package/src/domains/creations/presentation/components/CreationDetail/DetailImage.tsx +0 -41
- package/src/domains/creations/presentation/components/CreationDetail/DetailStory.tsx +0 -67
- package/src/domains/creations/presentation/components/CreationDetail/index.ts +0 -4
- package/src/domains/creations/presentation/components/CreationImageViewer.tsx +0 -101
- package/src/domains/creations/presentation/components/CreationThumbnail.tsx +0 -63
- package/src/domains/creations/presentation/components/CreationsGalleryEmptyState.tsx +0 -77
- package/src/domains/creations/presentation/components/CreationsGrid.tsx +0 -87
- package/src/domains/creations/presentation/components/CreationsHomeCard.tsx +0 -176
- package/src/domains/creations/presentation/components/EmptyState.tsx +0 -75
- package/src/domains/creations/presentation/components/FilterBottomSheet.tsx +0 -157
- package/src/domains/creations/presentation/components/FilterChips.tsx +0 -105
- package/src/domains/creations/presentation/components/GalleryHeader.tsx +0 -157
- package/src/domains/creations/presentation/components/index.ts +0 -20
- package/src/domains/creations/presentation/hooks/index.ts +0 -9
- package/src/domains/creations/presentation/hooks/useCreations.ts +0 -33
- package/src/domains/creations/presentation/hooks/useCreationsFilter.ts +0 -90
- package/src/domains/creations/presentation/hooks/useDeleteCreation.ts +0 -51
- package/src/domains/creations/presentation/hooks/useDeleteMultipleCreations.ts +0 -57
- package/src/domains/creations/presentation/hooks/useToggleFavorite.ts +0 -59
- package/src/domains/creations/presentation/screens/CreationDetailScreen.tsx +0 -71
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +0 -264
- package/src/domains/creations/presentation/screens/index.ts +0 -5
- package/src/domains/creations/presentation/utils/filterUtils.ts +0 -52
- package/src/domains/creations/types.d.ts +0 -107
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { View, StyleSheet, Image, Dimensions } from 'react-native';
|
|
4
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
5
|
-
|
|
6
|
-
interface DetailImageProps {
|
|
7
|
-
readonly uri: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const { width } = Dimensions.get('window');
|
|
11
|
-
|
|
12
|
-
export const DetailImage: React.FC<DetailImageProps> = ({ uri }) => {
|
|
13
|
-
const tokens = useAppDesignTokens();
|
|
14
|
-
const styles = useStyles(tokens);
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<View style={styles.container}>
|
|
18
|
-
<View style={styles.frame}>
|
|
19
|
-
<Image source={{ uri }} style={styles.image} resizeMode="cover" />
|
|
20
|
-
</View>
|
|
21
|
-
</View>
|
|
22
|
-
);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const useStyles = (tokens: any) => StyleSheet.create({
|
|
26
|
-
container: {
|
|
27
|
-
paddingHorizontal: tokens.spacing.lg,
|
|
28
|
-
marginVertical: tokens.spacing.lg,
|
|
29
|
-
},
|
|
30
|
-
frame: {
|
|
31
|
-
width: width - (tokens.spacing.lg * 2),
|
|
32
|
-
height: width - (tokens.spacing.lg * 2),
|
|
33
|
-
borderRadius: 24,
|
|
34
|
-
overflow: 'hidden',
|
|
35
|
-
backgroundColor: tokens.colors.surface,
|
|
36
|
-
},
|
|
37
|
-
image: {
|
|
38
|
-
width: '100%',
|
|
39
|
-
height: '100%',
|
|
40
|
-
},
|
|
41
|
-
});
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { View, StyleSheet } from 'react-native';
|
|
4
|
-
import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
5
|
-
import { LinearGradient } from 'expo-linear-gradient';
|
|
6
|
-
|
|
7
|
-
interface DetailStoryProps {
|
|
8
|
-
readonly story: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const DetailStory: React.FC<DetailStoryProps> = ({ story }) => {
|
|
12
|
-
const tokens = useAppDesignTokens();
|
|
13
|
-
const styles = useStyles(tokens);
|
|
14
|
-
|
|
15
|
-
if (!story) return null;
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<View style={styles.container}>
|
|
19
|
-
<LinearGradient
|
|
20
|
-
colors={[tokens.colors.primary + '15', tokens.colors.primary + '05']}
|
|
21
|
-
style={styles.gradient}
|
|
22
|
-
>
|
|
23
|
-
<AtomicText style={styles.quoteMark}>"</AtomicText>
|
|
24
|
-
<AtomicText style={styles.text}>{story}</AtomicText>
|
|
25
|
-
<View style={styles.quoteEndRow}>
|
|
26
|
-
<AtomicText style={[styles.quoteMark, styles.quoteEnd]}>"</AtomicText>
|
|
27
|
-
</View>
|
|
28
|
-
</LinearGradient>
|
|
29
|
-
</View>
|
|
30
|
-
);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const useStyles = (tokens: any) => StyleSheet.create({
|
|
34
|
-
container: {
|
|
35
|
-
paddingHorizontal: tokens.spacing.lg,
|
|
36
|
-
marginBottom: tokens.spacing.lg,
|
|
37
|
-
},
|
|
38
|
-
gradient: {
|
|
39
|
-
padding: tokens.spacing.lg,
|
|
40
|
-
borderRadius: 20,
|
|
41
|
-
borderWidth: 1,
|
|
42
|
-
borderColor: tokens.colors.primary + '20',
|
|
43
|
-
},
|
|
44
|
-
quoteMark: {
|
|
45
|
-
fontSize: 48,
|
|
46
|
-
lineHeight: 48,
|
|
47
|
-
color: tokens.colors.primary,
|
|
48
|
-
opacity: 0.4,
|
|
49
|
-
marginBottom: -16,
|
|
50
|
-
},
|
|
51
|
-
quoteEndRow: {
|
|
52
|
-
alignItems: 'flex-end',
|
|
53
|
-
marginTop: -16,
|
|
54
|
-
},
|
|
55
|
-
quoteEnd: {
|
|
56
|
-
marginBottom: 0,
|
|
57
|
-
},
|
|
58
|
-
text: {
|
|
59
|
-
fontSize: 16,
|
|
60
|
-
lineHeight: 26,
|
|
61
|
-
textAlign: 'center',
|
|
62
|
-
fontStyle: 'italic',
|
|
63
|
-
fontWeight: '500',
|
|
64
|
-
color: tokens.colors.textPrimary,
|
|
65
|
-
paddingBottom: 4,
|
|
66
|
-
},
|
|
67
|
-
});
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
3
|
-
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
4
|
-
import { ImageGallery } from "@umituz/react-native-image";
|
|
5
|
-
import { AtomicIcon, AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
6
|
-
import type { Creation } from "../../domain/entities/Creation";
|
|
7
|
-
|
|
8
|
-
interface CreationImageViewerProps {
|
|
9
|
-
readonly creations: Creation[];
|
|
10
|
-
readonly visible: boolean;
|
|
11
|
-
readonly index: number;
|
|
12
|
-
readonly enableEditing?: boolean;
|
|
13
|
-
readonly onDismiss: () => void;
|
|
14
|
-
readonly onIndexChange: (index: number) => void;
|
|
15
|
-
readonly onImageEdit?: (uri: string, creationId: string) => void | Promise<void>;
|
|
16
|
-
readonly selectedCreationId?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const CreationImageViewer: React.FC<CreationImageViewerProps> = ({
|
|
20
|
-
creations,
|
|
21
|
-
visible,
|
|
22
|
-
index,
|
|
23
|
-
enableEditing = false,
|
|
24
|
-
onDismiss,
|
|
25
|
-
onIndexChange,
|
|
26
|
-
onImageEdit,
|
|
27
|
-
selectedCreationId,
|
|
28
|
-
}) => {
|
|
29
|
-
const tokens = useAppDesignTokens();
|
|
30
|
-
const insets = useSafeAreaInsets();
|
|
31
|
-
|
|
32
|
-
const handleImageChange = async (uri: string) => {
|
|
33
|
-
if (selectedCreationId && onImageEdit) {
|
|
34
|
-
await onImageEdit(uri, selectedCreationId);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const HeaderComponent = ({ imageIndex }: { imageIndex: number }) => (
|
|
39
|
-
<View style={[styles.header, { paddingTop: Math.max(insets.top, tokens.spacing.sm) }]} pointerEvents="box-none">
|
|
40
|
-
<View style={styles.counter}>
|
|
41
|
-
<AtomicText style={styles.counterText}>
|
|
42
|
-
{imageIndex + 1} / {creations.length}
|
|
43
|
-
</AtomicText>
|
|
44
|
-
</View>
|
|
45
|
-
<TouchableOpacity
|
|
46
|
-
onPress={onDismiss}
|
|
47
|
-
style={styles.closeButton}
|
|
48
|
-
activeOpacity={0.7}
|
|
49
|
-
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
50
|
-
>
|
|
51
|
-
<AtomicIcon name="close" size="lg" color="onPrimary" />
|
|
52
|
-
</TouchableOpacity>
|
|
53
|
-
</View>
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
const styles = StyleSheet.create({
|
|
57
|
-
header: {
|
|
58
|
-
position: 'absolute',
|
|
59
|
-
top: 0,
|
|
60
|
-
left: 0,
|
|
61
|
-
right: 0,
|
|
62
|
-
flexDirection: 'row',
|
|
63
|
-
justifyContent: 'space-between',
|
|
64
|
-
paddingHorizontal: tokens.spacing.lg,
|
|
65
|
-
zIndex: 999,
|
|
66
|
-
},
|
|
67
|
-
counter: {
|
|
68
|
-
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
69
|
-
paddingHorizontal: tokens.spacing.md,
|
|
70
|
-
paddingVertical: tokens.spacing.xs,
|
|
71
|
-
borderRadius: 999,
|
|
72
|
-
justifyContent: 'center',
|
|
73
|
-
},
|
|
74
|
-
counterText: {
|
|
75
|
-
...tokens.typography.bodyMedium,
|
|
76
|
-
color: '#FFFFFF',
|
|
77
|
-
fontWeight: '600',
|
|
78
|
-
},
|
|
79
|
-
closeButton: {
|
|
80
|
-
width: 40,
|
|
81
|
-
height: 40,
|
|
82
|
-
borderRadius: 20,
|
|
83
|
-
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
84
|
-
justifyContent: 'center',
|
|
85
|
-
alignItems: 'center',
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<ImageGallery
|
|
91
|
-
images={creations.map((c) => ({ uri: c.uri }))}
|
|
92
|
-
visible={visible}
|
|
93
|
-
index={index}
|
|
94
|
-
onDismiss={onDismiss}
|
|
95
|
-
onIndexChange={onIndexChange}
|
|
96
|
-
HeaderComponent={HeaderComponent}
|
|
97
|
-
{...(enableEditing && { enableEditing } as any)}
|
|
98
|
-
{...(onImageEdit && { onImageChange: handleImageChange } as any)}
|
|
99
|
-
/>
|
|
100
|
-
);
|
|
101
|
-
};
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CreationThumbnail Component
|
|
3
|
-
* Displays a single creation thumbnail
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, { useMemo, useState } from "react";
|
|
7
|
-
import { Image, TouchableOpacity, StyleSheet, View } from "react-native";
|
|
8
|
-
import {
|
|
9
|
-
useAppDesignTokens,
|
|
10
|
-
AtomicIcon,
|
|
11
|
-
} from "@umituz/react-native-design-system";
|
|
12
|
-
|
|
13
|
-
interface CreationThumbnailProps {
|
|
14
|
-
readonly uri: string;
|
|
15
|
-
readonly size?: number;
|
|
16
|
-
readonly onPress?: () => void;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function CreationThumbnail({
|
|
20
|
-
uri,
|
|
21
|
-
size = 72,
|
|
22
|
-
onPress,
|
|
23
|
-
}: CreationThumbnailProps) {
|
|
24
|
-
const tokens = useAppDesignTokens();
|
|
25
|
-
const [isPressed, setIsPressed] = useState(false);
|
|
26
|
-
|
|
27
|
-
const styles = useMemo(
|
|
28
|
-
() =>
|
|
29
|
-
StyleSheet.create({
|
|
30
|
-
thumbnail: {
|
|
31
|
-
width: size,
|
|
32
|
-
height: size,
|
|
33
|
-
borderRadius: tokens.spacing.sm,
|
|
34
|
-
backgroundColor: tokens.colors.backgroundSecondary,
|
|
35
|
-
},
|
|
36
|
-
overlay: {
|
|
37
|
-
...StyleSheet.absoluteFillObject,
|
|
38
|
-
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
39
|
-
borderRadius: tokens.spacing.sm,
|
|
40
|
-
justifyContent: "center",
|
|
41
|
-
alignItems: "center",
|
|
42
|
-
},
|
|
43
|
-
}),
|
|
44
|
-
[tokens, size]
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<TouchableOpacity
|
|
49
|
-
onPress={onPress}
|
|
50
|
-
disabled={!onPress}
|
|
51
|
-
onPressIn={() => setIsPressed(true)}
|
|
52
|
-
onPressOut={() => setIsPressed(false)}
|
|
53
|
-
activeOpacity={1}
|
|
54
|
-
>
|
|
55
|
-
<Image source={{ uri }} style={styles.thumbnail} />
|
|
56
|
-
{isPressed && onPress && (
|
|
57
|
-
<View style={styles.overlay}>
|
|
58
|
-
<AtomicIcon name="eye" size="lg" color="textInverse" />
|
|
59
|
-
</View>
|
|
60
|
-
)}
|
|
61
|
-
</TouchableOpacity>
|
|
62
|
-
);
|
|
63
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } from "react";
|
|
2
|
-
import { View, StyleSheet, ActivityIndicator } from "react-native";
|
|
3
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
4
|
-
import { EmptyState } from "./EmptyState";
|
|
5
|
-
import type { CreationsConfig } from "../../domain/value-objects/CreationsConfig";
|
|
6
|
-
|
|
7
|
-
interface CreationsGalleryEmptyStateProps {
|
|
8
|
-
readonly isLoading: boolean;
|
|
9
|
-
readonly hasCreations: boolean;
|
|
10
|
-
readonly config: CreationsConfig;
|
|
11
|
-
readonly t: (key: string) => string;
|
|
12
|
-
readonly emptyActionLabel?: string;
|
|
13
|
-
readonly onEmptyAction?: () => void;
|
|
14
|
-
readonly clearFilters: () => void;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const CreationsGalleryEmptyState: React.FC<CreationsGalleryEmptyStateProps> = ({
|
|
18
|
-
isLoading,
|
|
19
|
-
hasCreations,
|
|
20
|
-
config,
|
|
21
|
-
t,
|
|
22
|
-
emptyActionLabel,
|
|
23
|
-
onEmptyAction,
|
|
24
|
-
clearFilters,
|
|
25
|
-
}) => {
|
|
26
|
-
const tokens = useAppDesignTokens();
|
|
27
|
-
const styles = useStyles(tokens);
|
|
28
|
-
|
|
29
|
-
return useMemo(() => {
|
|
30
|
-
// 1. Loading State
|
|
31
|
-
if (isLoading && !hasCreations) {
|
|
32
|
-
return (
|
|
33
|
-
<View style={styles.centerContainer}>
|
|
34
|
-
<ActivityIndicator size="large" color={tokens.colors.primary} />
|
|
35
|
-
</View>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// 2. System Empty State (User has NO creations at all)
|
|
40
|
-
// We check 'hasCreations' which represents the full list presence
|
|
41
|
-
if (!hasCreations) {
|
|
42
|
-
return (
|
|
43
|
-
<View style={styles.centerContainer}>
|
|
44
|
-
<EmptyState
|
|
45
|
-
title={t(config.translations.empty)}
|
|
46
|
-
description={t(config.translations.emptyDescription)}
|
|
47
|
-
actionLabel={emptyActionLabel}
|
|
48
|
-
onAction={onEmptyAction}
|
|
49
|
-
/>
|
|
50
|
-
</View>
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// 3. Filter Empty State (User has creations, but filter returns none)
|
|
55
|
-
// This component is rendered when the list is empty, but hasCreations is true.
|
|
56
|
-
return (
|
|
57
|
-
<View style={styles.centerContainer}>
|
|
58
|
-
<EmptyState
|
|
59
|
-
title={t("common.no_results") || "No results"}
|
|
60
|
-
description={t("common.no_results_description") || "Try changing your filters"}
|
|
61
|
-
actionLabel={t("common.clear_all") || "Clear All"}
|
|
62
|
-
onAction={clearFilters}
|
|
63
|
-
/>
|
|
64
|
-
</View>
|
|
65
|
-
);
|
|
66
|
-
}, [isLoading, hasCreations, config, t, emptyActionLabel, onEmptyAction, clearFilters, styles.centerContainer, tokens.colors.primary]);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const useStyles = (tokens: any) => StyleSheet.create({
|
|
70
|
-
centerContainer: {
|
|
71
|
-
flex: 1,
|
|
72
|
-
justifyContent: 'center',
|
|
73
|
-
alignItems: 'center',
|
|
74
|
-
minHeight: 400,
|
|
75
|
-
paddingHorizontal: tokens.spacing.xl
|
|
76
|
-
},
|
|
77
|
-
});
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { FlatList, RefreshControl, StyleSheet } from 'react-native';
|
|
3
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
4
|
-
import type { Creation } from "../../domain/entities/Creation";
|
|
5
|
-
import type { CreationType } from "../../domain/value-objects/CreationsConfig";
|
|
6
|
-
import { CreationCard } from "./CreationCard";
|
|
7
|
-
|
|
8
|
-
interface CreationsGridProps {
|
|
9
|
-
readonly creations: Creation[];
|
|
10
|
-
readonly types: readonly CreationType[];
|
|
11
|
-
readonly isLoading: boolean;
|
|
12
|
-
readonly onRefresh: () => void;
|
|
13
|
-
readonly onView: (creation: Creation) => void;
|
|
14
|
-
readonly onShare: (creation: Creation) => void;
|
|
15
|
-
readonly onDelete: (creation: Creation) => void;
|
|
16
|
-
readonly onToggleFavorite?: (creation: Creation) => void;
|
|
17
|
-
readonly selectedIds?: string[];
|
|
18
|
-
readonly onSelect?: (creation: Creation) => void;
|
|
19
|
-
readonly isSelectionMode?: boolean;
|
|
20
|
-
readonly contentContainerStyle?: any;
|
|
21
|
-
readonly ListEmptyComponent?: React.ReactElement | null;
|
|
22
|
-
readonly ListHeaderComponent?: React.ComponentType<any> | React.ReactElement | null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const CreationsGrid: React.FC<CreationsGridProps> = ({
|
|
26
|
-
creations,
|
|
27
|
-
types,
|
|
28
|
-
isLoading,
|
|
29
|
-
onRefresh,
|
|
30
|
-
onView,
|
|
31
|
-
onShare,
|
|
32
|
-
onDelete,
|
|
33
|
-
onToggleFavorite,
|
|
34
|
-
selectedIds = [],
|
|
35
|
-
onSelect,
|
|
36
|
-
isSelectionMode = false,
|
|
37
|
-
contentContainerStyle,
|
|
38
|
-
ListEmptyComponent,
|
|
39
|
-
ListHeaderComponent,
|
|
40
|
-
}) => {
|
|
41
|
-
const tokens = useAppDesignTokens();
|
|
42
|
-
const styles = useStyles(tokens);
|
|
43
|
-
|
|
44
|
-
const renderItem = ({ item }: { item: Creation }) => (
|
|
45
|
-
<CreationCard
|
|
46
|
-
creation={item}
|
|
47
|
-
types={types as CreationType[]}
|
|
48
|
-
onView={() => onView(item)}
|
|
49
|
-
onShare={() => onShare(item)}
|
|
50
|
-
onDelete={() => onDelete(item)}
|
|
51
|
-
onToggleFavorite={onToggleFavorite}
|
|
52
|
-
isSelected={selectedIds.includes(item.id)}
|
|
53
|
-
onSelect={onSelect}
|
|
54
|
-
isSelectionMode={isSelectionMode}
|
|
55
|
-
/>
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
return (
|
|
59
|
-
<FlatList
|
|
60
|
-
data={creations}
|
|
61
|
-
renderItem={renderItem}
|
|
62
|
-
keyExtractor={(item) => item.id}
|
|
63
|
-
ListHeaderComponent={ListHeaderComponent}
|
|
64
|
-
ListEmptyComponent={ListEmptyComponent}
|
|
65
|
-
contentContainerStyle={[
|
|
66
|
-
styles.list,
|
|
67
|
-
contentContainerStyle,
|
|
68
|
-
(!creations || creations.length === 0) && { flexGrow: 1 }
|
|
69
|
-
]}
|
|
70
|
-
showsVerticalScrollIndicator={false}
|
|
71
|
-
refreshControl={
|
|
72
|
-
<RefreshControl
|
|
73
|
-
refreshing={isLoading}
|
|
74
|
-
onRefresh={onRefresh}
|
|
75
|
-
tintColor={tokens.colors.primary}
|
|
76
|
-
/>
|
|
77
|
-
}
|
|
78
|
-
/>
|
|
79
|
-
);
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const useStyles = (tokens: any) => StyleSheet.create({
|
|
83
|
-
list: {
|
|
84
|
-
padding: tokens.spacing.md,
|
|
85
|
-
paddingBottom: 100, // Space for fab or bottom tab
|
|
86
|
-
},
|
|
87
|
-
});
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CreationsHomeCard Component
|
|
3
|
-
* Shows user's creations preview on home screen
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
declare const __DEV__: boolean;
|
|
7
|
-
|
|
8
|
-
import React, { useMemo, useCallback } from "react";
|
|
9
|
-
import { View, TouchableOpacity, FlatList, StyleSheet } from "react-native";
|
|
10
|
-
import {
|
|
11
|
-
AtomicText,
|
|
12
|
-
AtomicIcon,
|
|
13
|
-
useAppDesignTokens,
|
|
14
|
-
} from "@umituz/react-native-design-system";
|
|
15
|
-
import type { Creation } from "../../domain/entities/Creation";
|
|
16
|
-
import { CreationThumbnail } from "./CreationThumbnail";
|
|
17
|
-
|
|
18
|
-
interface CreationsHomeCardProps {
|
|
19
|
-
readonly creations: Creation[] | undefined;
|
|
20
|
-
readonly isLoading: boolean;
|
|
21
|
-
readonly title: string;
|
|
22
|
-
readonly countLabel: string;
|
|
23
|
-
readonly loadingLabel: string;
|
|
24
|
-
readonly maxThumbnails?: number;
|
|
25
|
-
readonly onPress: () => void;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function CreationsHomeCard({
|
|
29
|
-
creations,
|
|
30
|
-
isLoading,
|
|
31
|
-
title,
|
|
32
|
-
countLabel,
|
|
33
|
-
loadingLabel,
|
|
34
|
-
maxThumbnails = 4,
|
|
35
|
-
onPress,
|
|
36
|
-
}: CreationsHomeCardProps) {
|
|
37
|
-
const tokens = useAppDesignTokens();
|
|
38
|
-
|
|
39
|
-
if (__DEV__) {
|
|
40
|
-
console.log("[CreationsHomeCard] Render:", {
|
|
41
|
-
isLoading,
|
|
42
|
-
count: creations?.length ?? 0,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const styles = useMemo(
|
|
47
|
-
() =>
|
|
48
|
-
StyleSheet.create({
|
|
49
|
-
container: {
|
|
50
|
-
backgroundColor: tokens.colors.surface,
|
|
51
|
-
borderRadius: tokens.spacing.md,
|
|
52
|
-
padding: tokens.spacing.md,
|
|
53
|
-
},
|
|
54
|
-
header: {
|
|
55
|
-
flexDirection: "row",
|
|
56
|
-
justifyContent: "space-between",
|
|
57
|
-
alignItems: "center",
|
|
58
|
-
marginBottom: tokens.spacing.md,
|
|
59
|
-
},
|
|
60
|
-
headerLeft: {
|
|
61
|
-
flexDirection: "row",
|
|
62
|
-
alignItems: "center",
|
|
63
|
-
gap: tokens.spacing.sm,
|
|
64
|
-
},
|
|
65
|
-
icon: {
|
|
66
|
-
fontSize: 20,
|
|
67
|
-
},
|
|
68
|
-
title: {
|
|
69
|
-
...tokens.typography.bodyLarge,
|
|
70
|
-
fontWeight: "600",
|
|
71
|
-
color: tokens.colors.textPrimary,
|
|
72
|
-
},
|
|
73
|
-
viewAll: {
|
|
74
|
-
flexDirection: "row",
|
|
75
|
-
alignItems: "center",
|
|
76
|
-
gap: tokens.spacing.xs,
|
|
77
|
-
},
|
|
78
|
-
count: {
|
|
79
|
-
...tokens.typography.bodySmall,
|
|
80
|
-
color: tokens.colors.textSecondary,
|
|
81
|
-
},
|
|
82
|
-
thumbnailList: {
|
|
83
|
-
gap: tokens.spacing.sm,
|
|
84
|
-
},
|
|
85
|
-
loadingText: {
|
|
86
|
-
...tokens.typography.bodySmall,
|
|
87
|
-
color: tokens.colors.textSecondary,
|
|
88
|
-
textAlign: "center",
|
|
89
|
-
padding: tokens.spacing.md,
|
|
90
|
-
},
|
|
91
|
-
moreBadge: {
|
|
92
|
-
width: 72,
|
|
93
|
-
height: 72,
|
|
94
|
-
borderRadius: tokens.spacing.sm,
|
|
95
|
-
backgroundColor: tokens.colors.backgroundSecondary,
|
|
96
|
-
justifyContent: "center",
|
|
97
|
-
alignItems: "center",
|
|
98
|
-
},
|
|
99
|
-
moreText: {
|
|
100
|
-
...tokens.typography.bodySmall,
|
|
101
|
-
fontWeight: "600",
|
|
102
|
-
color: tokens.colors.primary,
|
|
103
|
-
},
|
|
104
|
-
}),
|
|
105
|
-
[tokens],
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
const displayItems = useMemo(() => {
|
|
109
|
-
if (!creations) return [];
|
|
110
|
-
return creations.slice(0, maxThumbnails);
|
|
111
|
-
}, [creations, maxThumbnails]);
|
|
112
|
-
|
|
113
|
-
const renderItem = useCallback(
|
|
114
|
-
({ item, index }: { item: Creation; index: number }) => {
|
|
115
|
-
const isLast = index === maxThumbnails - 1;
|
|
116
|
-
const hasMore = creations && creations.length > maxThumbnails;
|
|
117
|
-
|
|
118
|
-
if (isLast && hasMore) {
|
|
119
|
-
return (
|
|
120
|
-
<TouchableOpacity style={styles.moreBadge} onPress={onPress}>
|
|
121
|
-
<AtomicText style={styles.moreText}>
|
|
122
|
-
+{creations.length - maxThumbnails + 1}
|
|
123
|
-
</AtomicText>
|
|
124
|
-
</TouchableOpacity>
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return <CreationThumbnail uri={item.uri} onPress={onPress} />;
|
|
129
|
-
},
|
|
130
|
-
[styles, creations, maxThumbnails, onPress],
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
if (isLoading) {
|
|
134
|
-
return (
|
|
135
|
-
<View style={styles.container}>
|
|
136
|
-
<AtomicText style={styles.loadingText}>{loadingLabel}</AtomicText>
|
|
137
|
-
</View>
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (!creations || creations.length === 0) {
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const count = creations.length;
|
|
146
|
-
|
|
147
|
-
return (
|
|
148
|
-
<TouchableOpacity
|
|
149
|
-
style={styles.container}
|
|
150
|
-
onPress={onPress}
|
|
151
|
-
activeOpacity={0.8}
|
|
152
|
-
>
|
|
153
|
-
<View style={styles.header}>
|
|
154
|
-
<View style={styles.headerLeft}>
|
|
155
|
-
<AtomicText style={styles.icon}>🎨</AtomicText>
|
|
156
|
-
<AtomicText style={styles.title}>{title}</AtomicText>
|
|
157
|
-
</View>
|
|
158
|
-
<TouchableOpacity style={styles.viewAll} onPress={onPress}>
|
|
159
|
-
<AtomicText style={styles.count}>
|
|
160
|
-
{countLabel.replace("{{count}}", String(count))}
|
|
161
|
-
</AtomicText>
|
|
162
|
-
<AtomicIcon name="chevron-forward" size="sm" color="primary" />
|
|
163
|
-
</TouchableOpacity>
|
|
164
|
-
</View>
|
|
165
|
-
<FlatList
|
|
166
|
-
data={displayItems}
|
|
167
|
-
renderItem={renderItem}
|
|
168
|
-
keyExtractor={(item) => item.id}
|
|
169
|
-
horizontal
|
|
170
|
-
showsHorizontalScrollIndicator={false}
|
|
171
|
-
contentContainerStyle={styles.thumbnailList}
|
|
172
|
-
scrollEnabled={false}
|
|
173
|
-
/>
|
|
174
|
-
</TouchableOpacity>
|
|
175
|
-
);
|
|
176
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* EmptyState Component
|
|
3
|
-
* Displays when no creations exist
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, { useMemo } from "react";
|
|
7
|
-
import { View, StyleSheet } from "react-native";
|
|
8
|
-
import { AtomicText, AtomicButton, AtomicIcon, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
9
|
-
|
|
10
|
-
interface EmptyStateProps {
|
|
11
|
-
readonly title: string;
|
|
12
|
-
readonly description: string;
|
|
13
|
-
readonly iconName?: string;
|
|
14
|
-
readonly actionLabel?: string;
|
|
15
|
-
readonly onAction?: () => void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function EmptyState({
|
|
19
|
-
title,
|
|
20
|
-
description,
|
|
21
|
-
iconName = "images-outline",
|
|
22
|
-
actionLabel,
|
|
23
|
-
onAction,
|
|
24
|
-
}: EmptyStateProps) {
|
|
25
|
-
const tokens = useAppDesignTokens();
|
|
26
|
-
|
|
27
|
-
const styles = useMemo(
|
|
28
|
-
() =>
|
|
29
|
-
StyleSheet.create({
|
|
30
|
-
container: {
|
|
31
|
-
flex: 1,
|
|
32
|
-
justifyContent: "center",
|
|
33
|
-
alignItems: "center",
|
|
34
|
-
padding: tokens.spacing.xl,
|
|
35
|
-
},
|
|
36
|
-
iconContainer: {
|
|
37
|
-
marginBottom: tokens.spacing.lg,
|
|
38
|
-
},
|
|
39
|
-
title: {
|
|
40
|
-
...tokens.typography.headingSmall,
|
|
41
|
-
color: tokens.colors.textPrimary,
|
|
42
|
-
textAlign: "center",
|
|
43
|
-
marginBottom: tokens.spacing.sm,
|
|
44
|
-
},
|
|
45
|
-
description: {
|
|
46
|
-
...tokens.typography.bodyMedium,
|
|
47
|
-
color: tokens.colors.textSecondary,
|
|
48
|
-
textAlign: "center",
|
|
49
|
-
marginBottom: onAction ? tokens.spacing.xl : 0,
|
|
50
|
-
},
|
|
51
|
-
button: {
|
|
52
|
-
minWidth: 160,
|
|
53
|
-
},
|
|
54
|
-
}),
|
|
55
|
-
[tokens, onAction],
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
return (
|
|
59
|
-
<View style={styles.container}>
|
|
60
|
-
<View style={styles.iconContainer}>
|
|
61
|
-
<AtomicIcon name={iconName} size={64} color="secondary" />
|
|
62
|
-
</View>
|
|
63
|
-
<AtomicText style={styles.title}>{title}</AtomicText>
|
|
64
|
-
<AtomicText style={styles.description}>{description}</AtomicText>
|
|
65
|
-
{onAction && actionLabel && (
|
|
66
|
-
<AtomicButton
|
|
67
|
-
title={actionLabel}
|
|
68
|
-
onPress={onAction}
|
|
69
|
-
variant="primary"
|
|
70
|
-
style={styles.button}
|
|
71
|
-
/>
|
|
72
|
-
)}
|
|
73
|
-
</View>
|
|
74
|
-
);
|
|
75
|
-
}
|