@umituz/react-native-design-system 4.23.53 → 4.23.55
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/atoms/AtomicInput.tsx +2 -2
- package/src/atoms/index.ts +1 -1
- package/src/atoms/input/components/InputIcon.tsx +2 -2
- package/src/exports/molecules/navigation.ts +0 -2
- package/src/global.d.ts +4 -0
- package/src/image/infrastructure/services/ImageEditorService.ts +1 -1
- package/src/image/infrastructure/utils/FilterProcessor.ts +16 -14
- package/src/image/infrastructure/utils/LayerManager.ts +7 -4
- package/src/image/infrastructure/utils/validation/mime-type-validator.ts +1 -1
- package/src/image/presentation/components/ImageGallery.tsx +2 -2
- package/src/image/presentation/components/editor/TextEditorSheet.tsx +3 -3
- package/src/image/presentation/hooks/useImageBatch.ts +1 -1
- package/src/infinite-scroll/domain/types/infinite-scroll-config.ts +1 -1
- package/src/init/createAppInitializer.ts +1 -2
- package/src/init/env/createEnvConfig.ts +2 -3
- package/src/media/domain/entities/CardMultimedia.types.ts +17 -2
- package/src/media/domain/entities/MultimediaFlashcardTypes.ts +17 -2
- package/src/media/infrastructure/services/CardMediaUploadService.ts +2 -3
- package/src/media/infrastructure/services/CardMediaValidationService.ts +2 -2
- package/src/media/infrastructure/services/CardMultimediaService.ts +3 -2
- package/src/media/infrastructure/services/MediaUploadService.ts +2 -1
- package/src/media/infrastructure/services/MediaValidationService.ts +2 -2
- package/src/media/infrastructure/services/MultimediaFlashcardService.ts +3 -2
- package/src/media/presentation/hooks/card-multimedia.types.ts +5 -3
- package/src/media/presentation/hooks/multimedia.types.ts +5 -3
- package/src/media/presentation/hooks/useCardMediaUpload.ts +2 -1
- package/src/media/presentation/hooks/useCardMediaValidation.ts +2 -2
- package/src/media/presentation/hooks/useCardMultimediaFlashcard.ts +3 -2
- package/src/media/presentation/hooks/useMediaUpload.ts +2 -1
- package/src/media/presentation/hooks/useMediaValidation.ts +2 -2
- package/src/media/presentation/hooks/useMultimediaFlashcard.ts +3 -2
- package/src/molecules/BaseModal.tsx +2 -3
- package/src/molecules/bottom-sheet/components/BottomSheetModal.tsx +0 -1
- package/src/molecules/bottom-sheet/components/filter/FilterBottomSheet.tsx +15 -6
- package/src/molecules/bottom-sheet/components/filter/FilterSheetComponents/FilterSheetOption.tsx +1 -1
- package/src/molecules/calendar/infrastructure/storage/CalendarStore.ts +2 -3
- package/src/molecules/calendar/presentation/hooks/useCalendar.ts +3 -3
- package/src/molecules/circular-menu/CircularMenuItem.tsx +1 -1
- package/src/molecules/countdown/components/CountdownHeader.tsx +1 -1
- package/src/molecules/emoji/domain/entities/Emoji.ts +17 -6
- package/src/molecules/emoji/presentation/components/EmojiPicker.tsx +2 -2
- package/src/molecules/filter-group/FilterGroup.tsx +1 -1
- package/src/molecules/filter-group/types.ts +4 -3
- package/src/molecules/navigation/StackNavigator.tsx +3 -3
- package/src/molecules/navigation/TabsNavigator.tsx +6 -6
- package/src/molecules/navigation/components/TabLabel.tsx +1 -1
- package/src/molecules/navigation/index.ts +0 -2
- package/src/molecules/navigation/types.ts +2 -2
- package/src/molecules/navigation/utils/NavigationTheme.ts +1 -1
- package/src/molecules/navigation/utils/ScreenFactory.ts +2 -2
- package/src/molecules/splash/components/SplashScreen.tsx +0 -1
- package/src/molecules/splash/hooks/useSplashFlow.ts +2 -3
- package/src/onboarding/domain/entities/OnboardingQuestion.ts +8 -3
- package/src/onboarding/domain/entities/OnboardingSlide.ts +8 -6
- package/src/onboarding/domain/entities/OnboardingUserData.ts +5 -3
- package/src/onboarding/infrastructure/hooks/useOnboardingAnswers.ts +4 -3
- package/src/onboarding/infrastructure/services/ValidationManager.ts +5 -5
- package/src/onboarding/infrastructure/storage/OnboardingStoreSelectors.ts +2 -1
- package/src/onboarding/infrastructure/storage/actions/answerActions.ts +2 -1
- package/src/onboarding/presentation/components/BackgroundVideo.tsx +3 -2
- package/src/onboarding/presentation/components/OnboardingSlide.tsx +1 -1
- package/src/onboarding/presentation/components/QuestionRenderer.tsx +11 -11
- package/src/onboarding/presentation/components/QuestionSlide.tsx +3 -2
- package/src/onboarding/presentation/components/QuestionSlideHeader.tsx +1 -1
- package/src/onboarding/presentation/components/questions/QuestionOptionItem.tsx +1 -1
- package/src/onboarding/presentation/components/questions/SingleChoiceQuestion.tsx +1 -1
- package/src/onboarding/presentation/hooks/useOnboardingContainerStyle.ts +2 -1
- package/src/onboarding/presentation/hooks/useOnboardingScreenState.ts +5 -3
- package/src/onboarding/presentation/types/OnboardingProps.ts +5 -3
- package/src/storage/cache/domain/Cache.ts +4 -4
- package/src/storage/cache/infrastructure/TTLCache.ts +7 -7
- package/src/storage/domain/factories/StoreFactory.ts +6 -6
- package/src/storage/domain/utils/devUtils.ts +8 -8
- package/src/tanstack/infrastructure/monitoring/DevMonitorLogger.ts +6 -6
- package/src/tanstack/infrastructure/providers/TanstackProvider.tsx +1 -1
- package/src/tanstack/presentation/hooks/useOptimisticUpdate.ts +1 -1
- package/src/theme/core/TokenFactory.ts +40 -33
- package/src/theme/core/colors/DarkColors.ts +1 -1
- package/src/theme/core/colors/LightColors.ts +1 -1
- package/src/theme/infrastructure/globalThemeStore.ts +0 -1
- package/src/atoms/AtomicChip.tsx +0 -7
- package/src/image/infrastructure/utils/ImageFilterUtils.ts +0 -20
- package/src/molecules/navigation/createStackNavigator.ts +0 -20
- package/src/molecules/navigation/createTabNavigator.ts +0 -20
- package/src/safe-area/utils/performance.ts +0 -10
- package/src/storage/cache/types.d.ts +0 -3
- package/src/tanstack/types/global.d.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.55",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone, offline, onboarding, and loading utilities",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -130,7 +130,7 @@ export const AtomicInput = React.forwardRef<React.ElementRef<typeof TextInput>,
|
|
|
130
130
|
<View style={containerStyle}>
|
|
131
131
|
{leadingIcon && (
|
|
132
132
|
<InputIcon
|
|
133
|
-
name={leadingIcon
|
|
133
|
+
name={leadingIcon }
|
|
134
134
|
size={sizeConfig.iconSize}
|
|
135
135
|
color={iconColor}
|
|
136
136
|
position="leading"
|
|
@@ -182,7 +182,7 @@ export const AtomicInput = React.forwardRef<React.ElementRef<typeof TextInput>,
|
|
|
182
182
|
|
|
183
183
|
{trailingIcon && !showPasswordToggle && (
|
|
184
184
|
<InputIcon
|
|
185
|
-
name={trailingIcon
|
|
185
|
+
name={trailingIcon }
|
|
186
186
|
size={sizeConfig.iconSize}
|
|
187
187
|
color={iconColor}
|
|
188
188
|
position="trailing"
|
package/src/atoms/index.ts
CHANGED
|
@@ -61,7 +61,7 @@ export {
|
|
|
61
61
|
export { AtomicAvatar, type AtomicAvatarProps } from './AtomicAvatar';
|
|
62
62
|
|
|
63
63
|
// Chip
|
|
64
|
-
export { AtomicChip, type AtomicChipProps } from './
|
|
64
|
+
export { AtomicChip, type AtomicChipProps } from './chip';
|
|
65
65
|
|
|
66
66
|
// Progress
|
|
67
67
|
export { AtomicProgress, type AtomicProgressProps } from './AtomicProgress';
|
|
@@ -24,14 +24,14 @@ export const InputIcon: React.FC<InputIconProps> = ({
|
|
|
24
24
|
if (onPress) {
|
|
25
25
|
return (
|
|
26
26
|
<Pressable onPress={onPress} style={style} testID={testID}>
|
|
27
|
-
<AtomicIcon name={name
|
|
27
|
+
<AtomicIcon name={name} customSize={size} customColor={color} />
|
|
28
28
|
</Pressable>
|
|
29
29
|
);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
return (
|
|
33
33
|
<AtomicIcon
|
|
34
|
-
name={name
|
|
34
|
+
name={name}
|
|
35
35
|
customSize={size}
|
|
36
36
|
customColor={color}
|
|
37
37
|
style={style}
|
package/src/global.d.ts
ADDED
|
@@ -79,7 +79,7 @@ export class ImageEditorService {
|
|
|
79
79
|
return ImageEditorService.commitHistory(state, newLayers);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
static addElementToLayer(state: EditorState, layerId: string, element:
|
|
82
|
+
static addElementToLayer(state: EditorState, layerId: string, element: EditorLayer['elements'][number]): EditorState {
|
|
83
83
|
const layer = state.layers.find(l => l.id === layerId);
|
|
84
84
|
if (!layer || layer.locked) {
|
|
85
85
|
throw ImageErrorHandler.createError('Invalid layer operation', IMAGE_ERROR_CODES.VALIDATION_ERROR, 'addElementToLayer');
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Infrastructure - Filter Processor
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Filter processing with preset management
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { ColorAdjustmentFilters } from './filters/ColorAdjustmentFilters';
|
|
8
|
+
import { StyleFilters } from './filters/StyleFilters';
|
|
9
|
+
import { FilterHelpers } from './filters/FilterHelpers';
|
|
8
10
|
|
|
9
11
|
export interface FilterPreset {
|
|
10
12
|
id: string;
|
|
@@ -27,7 +29,7 @@ export interface FilterParameter {
|
|
|
27
29
|
export interface FilterState {
|
|
28
30
|
id: string;
|
|
29
31
|
intensity: number;
|
|
30
|
-
parameters: Record<string,
|
|
32
|
+
parameters: Record<string, number>;
|
|
31
33
|
enabled: boolean;
|
|
32
34
|
}
|
|
33
35
|
|
|
@@ -80,8 +82,8 @@ export class FilterProcessor {
|
|
|
80
82
|
const preset = this.getPreset(presetId);
|
|
81
83
|
if (!preset) throw new Error(`Filter preset not found: ${presetId}`);
|
|
82
84
|
|
|
83
|
-
const parameters: Record<string,
|
|
84
|
-
preset.parameters.forEach(param => { parameters[param.name] = param.value; });
|
|
85
|
+
const parameters: Record<string, number> = {};
|
|
86
|
+
preset.parameters.forEach(param => { parameters[param.name] = param.value as number; });
|
|
85
87
|
|
|
86
88
|
return { id: presetId, intensity: 100, parameters, enabled: true };
|
|
87
89
|
}
|
|
@@ -95,30 +97,30 @@ export class FilterProcessor {
|
|
|
95
97
|
const preset = this.getPreset(filterState.id);
|
|
96
98
|
if (!preset || !filterState.enabled) return imageData;
|
|
97
99
|
|
|
98
|
-
let processedData = new Uint8ClampedArray(imageData);
|
|
100
|
+
let processedData: Uint8ClampedArray = new Uint8ClampedArray(imageData);
|
|
99
101
|
|
|
100
102
|
switch (filterState.id) {
|
|
101
103
|
case 'brightness':
|
|
102
|
-
processedData =
|
|
104
|
+
processedData = new Uint8ClampedArray(ColorAdjustmentFilters.applyBrightness(processedData, filterState.parameters.brightness ?? 0));
|
|
103
105
|
break;
|
|
104
106
|
case 'contrast':
|
|
105
|
-
processedData =
|
|
107
|
+
processedData = new Uint8ClampedArray(ColorAdjustmentFilters.applyContrast(processedData, filterState.parameters.contrast ?? 0));
|
|
106
108
|
break;
|
|
107
109
|
case 'saturation':
|
|
108
|
-
processedData =
|
|
110
|
+
processedData = new Uint8ClampedArray(ColorAdjustmentFilters.applySaturation(processedData, filterState.parameters.saturation ?? 0));
|
|
109
111
|
break;
|
|
110
112
|
case 'vintage':
|
|
111
|
-
processedData =
|
|
113
|
+
processedData = new Uint8ClampedArray(StyleFilters.applyVintage(processedData, filterState.parameters.intensity ?? 50, filterState.parameters.warmth ?? 30));
|
|
112
114
|
break;
|
|
113
115
|
case 'blur':
|
|
114
|
-
processedData =
|
|
116
|
+
processedData = new Uint8ClampedArray(StyleFilters.applyBlur(processedData, filterState.parameters.radius ?? 0, width, height));
|
|
115
117
|
break;
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
if (filterState.intensity < 100) {
|
|
119
|
-
processedData =
|
|
121
|
+
processedData = new Uint8ClampedArray(FilterHelpers.applyIntensity(imageData, processedData, filterState.intensity / 100));
|
|
120
122
|
}
|
|
121
123
|
|
|
122
|
-
return processedData
|
|
124
|
+
return processedData;
|
|
123
125
|
}
|
|
124
|
-
}
|
|
126
|
+
}
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* Manages editor layers with composition and rendering
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { EditorLayer } from '../../domain/entities/EditorTypes';
|
|
8
|
+
|
|
9
|
+
type LayerElement = EditorLayer['elements'][number];
|
|
7
10
|
export type LayerOperation = 'add' | 'remove' | 'move' | 'merge' | 'duplicate';
|
|
8
11
|
|
|
9
12
|
export interface LayerComposition {
|
|
@@ -19,13 +22,13 @@ export class LayerManager {
|
|
|
19
22
|
layers: Array<{
|
|
20
23
|
id: string;
|
|
21
24
|
name: string;
|
|
22
|
-
elements:
|
|
25
|
+
elements: LayerElement[];
|
|
23
26
|
}>,
|
|
24
27
|
targetIds: string[]
|
|
25
28
|
): Array<{
|
|
26
29
|
id: string;
|
|
27
30
|
name: string;
|
|
28
|
-
elements:
|
|
31
|
+
elements: LayerElement[];
|
|
29
32
|
}> {
|
|
30
33
|
const targetLayers = layers.filter(layer => targetIds.includes(layer.id));
|
|
31
34
|
const otherLayers = layers.filter(layer => !targetIds.includes(layer.id));
|
|
@@ -47,12 +50,12 @@ export class LayerManager {
|
|
|
47
50
|
layer: {
|
|
48
51
|
id: string;
|
|
49
52
|
name: string;
|
|
50
|
-
elements:
|
|
53
|
+
elements: LayerElement[];
|
|
51
54
|
}
|
|
52
55
|
): {
|
|
53
56
|
id: string;
|
|
54
57
|
name: string;
|
|
55
|
-
elements:
|
|
58
|
+
elements: LayerElement[];
|
|
56
59
|
} {
|
|
57
60
|
return {
|
|
58
61
|
id: Math.random().toString(36).substr(2, 9),
|
|
@@ -47,7 +47,7 @@ export function validateImageMimeType(
|
|
|
47
47
|
return { isValid: false, error: `${fieldName} MIME type is required` };
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
if (!SUPPORTED_IMAGE_MIME_TYPES.includes(mimeType
|
|
50
|
+
if (!(SUPPORTED_IMAGE_MIME_TYPES as readonly string[]).includes(mimeType)) {
|
|
51
51
|
return {
|
|
52
52
|
isValid: false,
|
|
53
53
|
error: `${fieldName} must be an image (JPEG, PNG, WEBP, or GIF)`,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React, { useCallback, useState, useEffect } from 'react';
|
|
9
|
-
import { Modal, View, StyleSheet, FlatList, Dimensions } from 'react-native';
|
|
9
|
+
import { Modal, View, StyleSheet, FlatList, Dimensions, type NativeSyntheticEvent, type NativeScrollEvent } from 'react-native';
|
|
10
10
|
import { Image } from 'expo-image';
|
|
11
11
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
12
12
|
import type { ImageViewerItem, ImageGalleryOptions } from '../../domain/entities/ImageTypes';
|
|
@@ -48,7 +48,7 @@ export const ImageGallery: React.FC<ImageGalleryProps> = ({
|
|
|
48
48
|
await onImageChange(currentImage.uri, currentIndex);
|
|
49
49
|
}, [images, currentIndex, onImageChange]);
|
|
50
50
|
|
|
51
|
-
const handleScroll = useCallback((event:
|
|
51
|
+
const handleScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
52
52
|
const nextIndex = Math.round(event.nativeEvent.contentOffset.x / SCREEN_WIDTH);
|
|
53
53
|
if (nextIndex !== currentIndex) {
|
|
54
54
|
setCurrentIndex(nextIndex);
|
|
@@ -37,7 +37,7 @@ export const TextEditorSheet = forwardRef<BottomSheetModalRef, TextEditorSheetPr
|
|
|
37
37
|
const [activeTab, setActiveTab] = useState<'content' | 'style' | 'transform'>('content');
|
|
38
38
|
const { onDismiss, snapPoints = ['75%'], t } = props;
|
|
39
39
|
|
|
40
|
-
const tabs = [
|
|
40
|
+
const tabs: { id: 'content' | 'style' | 'transform'; label: string; icon: string }[] = [
|
|
41
41
|
{ id: 'content', label: 'Text', icon: 'text' },
|
|
42
42
|
{ id: 'style', label: 'Style', icon: 'color-palette' },
|
|
43
43
|
{ id: 'transform', label: 'Edit', icon: 'options' },
|
|
@@ -50,13 +50,13 @@ export const TextEditorSheet = forwardRef<BottomSheetModalRef, TextEditorSheetPr
|
|
|
50
50
|
{tabs.map(tab => (
|
|
51
51
|
<TouchableOpacity
|
|
52
52
|
key={tab.id}
|
|
53
|
-
onPress={() => setActiveTab(tab.id
|
|
53
|
+
onPress={() => setActiveTab(tab.id)}
|
|
54
54
|
style={{
|
|
55
55
|
flex: 1, alignItems: 'center', paddingVertical: tokens.spacing.sm,
|
|
56
56
|
borderBottomWidth: 3, borderBottomColor: activeTab === tab.id ? tokens.colors.primary : 'transparent'
|
|
57
57
|
}}
|
|
58
58
|
>
|
|
59
|
-
<AtomicIcon name={tab.icon
|
|
59
|
+
<AtomicIcon name={tab.icon} size={20} color={activeTab === tab.id ? 'primary' : 'secondary'} />
|
|
60
60
|
<AtomicText style={{
|
|
61
61
|
...tokens.typography.labelSmall,
|
|
62
62
|
color: activeTab === tab.id ? tokens.colors.primary : tokens.colors.textSecondary,
|
|
@@ -12,7 +12,7 @@ export const useImageBatch = () => {
|
|
|
12
12
|
const processBatch = useCallback((operations: BatchOperation[], options?: BatchProcessingOptions) =>
|
|
13
13
|
execute(() => ImageBatchService.processBatch(operations, options), 'Failed to process batch'), [execute]);
|
|
14
14
|
|
|
15
|
-
const resizeBatch = useCallback((uris: string[], width?: number, height?: number, options?: BatchProcessingOptions & { saveOptions?:
|
|
15
|
+
const resizeBatch = useCallback((uris: string[], width?: number, height?: number, options?: BatchProcessingOptions & { saveOptions?: Record<string, unknown> }) =>
|
|
16
16
|
execute(() => ImageBatchService.resizeBatch(uris, width, height, options), 'Failed to resize batch'), [execute]);
|
|
17
17
|
|
|
18
18
|
const compressBatch = useCallback((uris: string[], quality?: number, options?: BatchProcessingOptions) =>
|
|
@@ -9,7 +9,6 @@ import type {
|
|
|
9
9
|
InitModule,
|
|
10
10
|
} from "./types";
|
|
11
11
|
|
|
12
|
-
declare const __DEV__: boolean;
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Create an app initializer with singleton pattern
|
|
@@ -33,7 +32,7 @@ export function createAppInitializer(
|
|
|
33
32
|
): () => Promise<AppInitializerResult> {
|
|
34
33
|
const {
|
|
35
34
|
modules,
|
|
36
|
-
debug =
|
|
35
|
+
debug = __DEV__,
|
|
37
36
|
continueOnError = true,
|
|
38
37
|
onComplete,
|
|
39
38
|
onError,
|
|
@@ -12,7 +12,6 @@ import type {
|
|
|
12
12
|
FirebaseEnvConfig,
|
|
13
13
|
} from "./types";
|
|
14
14
|
|
|
15
|
-
declare const __DEV__: boolean;
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
17
|
* Default key mappings for environment variables
|
|
@@ -109,7 +108,7 @@ export function createEnvConfig(
|
|
|
109
108
|
const testStoreKey = config.revenueCat?.testStoreKey;
|
|
110
109
|
|
|
111
110
|
if (isExpoGo && testStoreKey) {
|
|
112
|
-
if (
|
|
111
|
+
if (__DEV__) {
|
|
113
112
|
console.log("[createEnvConfig] Using RevenueCat Test Store Key (Expo Go)");
|
|
114
113
|
}
|
|
115
114
|
return testStoreKey;
|
|
@@ -130,7 +129,7 @@ export function createEnvConfig(
|
|
|
130
129
|
|
|
131
130
|
const getRevenueCatTestStoreKey = (): string | undefined => {
|
|
132
131
|
// Only return test store key in development
|
|
133
|
-
if (
|
|
132
|
+
if (__DEV__) {
|
|
134
133
|
return config.revenueCat?.testStoreKey;
|
|
135
134
|
}
|
|
136
135
|
return undefined;
|
|
@@ -79,6 +79,21 @@ export interface CardMediaCompressionOptions {
|
|
|
79
79
|
format?: "jpeg" | "png" | "webp";
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
export interface CardMediaFile {
|
|
83
|
+
name: string;
|
|
84
|
+
type: string;
|
|
85
|
+
size: number;
|
|
86
|
+
uri?: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface CreateCardMultimediaData {
|
|
90
|
+
front: string;
|
|
91
|
+
back: string;
|
|
92
|
+
difficulty?: "easy" | "medium" | "hard";
|
|
93
|
+
tags?: string[];
|
|
94
|
+
media?: CardMediaAttachment[];
|
|
95
|
+
}
|
|
96
|
+
|
|
82
97
|
export interface CardMediaValidation {
|
|
83
98
|
isValid: boolean;
|
|
84
99
|
errors: string[];
|
|
@@ -88,13 +103,13 @@ export interface CardMediaValidation {
|
|
|
88
103
|
|
|
89
104
|
export interface CardMultimediaFlashcardService {
|
|
90
105
|
uploadMedia(
|
|
91
|
-
file:
|
|
106
|
+
file: CardMediaFile,
|
|
92
107
|
options?: CardMediaCompressionOptions,
|
|
93
108
|
): Promise<CardMediaAttachment>;
|
|
94
109
|
generateMedia(
|
|
95
110
|
request: CardMediaGenerationRequest,
|
|
96
111
|
): Promise<CardMediaGenerationResult>;
|
|
97
|
-
validateMedia(file:
|
|
112
|
+
validateMedia(file: CardMediaFile): Promise<CardMediaValidation>;
|
|
98
113
|
optimizeMedia(
|
|
99
114
|
attachment: CardMediaAttachment,
|
|
100
115
|
options: CardMediaCompressionOptions,
|
|
@@ -79,6 +79,21 @@ export interface MediaCompressionOptions {
|
|
|
79
79
|
format?: "jpeg" | "png" | "webp";
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
export interface MediaFile {
|
|
83
|
+
name: string;
|
|
84
|
+
type: string;
|
|
85
|
+
size: number;
|
|
86
|
+
uri?: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface CreateMultimediaCardData {
|
|
90
|
+
front: string;
|
|
91
|
+
back: string;
|
|
92
|
+
difficulty?: "easy" | "medium" | "hard";
|
|
93
|
+
tags?: string[];
|
|
94
|
+
media?: MediaAttachment[];
|
|
95
|
+
}
|
|
96
|
+
|
|
82
97
|
export interface MediaValidation {
|
|
83
98
|
isValid: boolean;
|
|
84
99
|
errors: string[];
|
|
@@ -88,13 +103,13 @@ export interface MediaValidation {
|
|
|
88
103
|
|
|
89
104
|
export interface MultimediaFlashcardService {
|
|
90
105
|
uploadMedia(
|
|
91
|
-
file:
|
|
106
|
+
file: MediaFile,
|
|
92
107
|
options?: MediaCompressionOptions,
|
|
93
108
|
): Promise<MediaAttachment>;
|
|
94
109
|
generateMedia(
|
|
95
110
|
request: MediaGenerationRequest,
|
|
96
111
|
): Promise<MediaGenerationResult>;
|
|
97
|
-
validateMedia(file:
|
|
112
|
+
validateMedia(file: MediaFile): Promise<MediaValidation>;
|
|
98
113
|
optimizeMedia(
|
|
99
114
|
attachment: MediaAttachment,
|
|
100
115
|
options: MediaCompressionOptions,
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
* Handles media upload, download, and URL operations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
declare var __DEV__: boolean;
|
|
7
|
-
|
|
8
6
|
import type {
|
|
9
7
|
CardMediaAttachment,
|
|
10
8
|
CardMediaCompressionOptions,
|
|
9
|
+
CardMediaFile,
|
|
11
10
|
} from "../../domain/entities/CardMultimedia.types";
|
|
12
11
|
import { generateThumbnail, getMediaDuration } from "../utils/file-media-utils";
|
|
13
12
|
import { getMediaTypeFromMime } from "../utils/mime-type-detector";
|
|
@@ -17,7 +16,7 @@ export class CardMediaUploadService {
|
|
|
17
16
|
* Upload media file with optional compression
|
|
18
17
|
*/
|
|
19
18
|
async uploadMedia(
|
|
20
|
-
file:
|
|
19
|
+
file: CardMediaFile,
|
|
21
20
|
_options?: CardMediaCompressionOptions,
|
|
22
21
|
): Promise<CardMediaAttachment> {
|
|
23
22
|
try {
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
import { formatFileSize } from "../utils/media-collection-utils";
|
|
7
7
|
import { getMediaDuration } from "../utils/file-media-utils";
|
|
8
|
-
import type { CardMediaValidation } from "../../domain/entities/CardMultimedia.types";
|
|
8
|
+
import type { CardMediaValidation, CardMediaFile } from "../../domain/entities/CardMultimedia.types";
|
|
9
9
|
|
|
10
10
|
export class CardMediaValidationService {
|
|
11
11
|
/**
|
|
12
12
|
* Validate media file before upload
|
|
13
13
|
*/
|
|
14
|
-
async validateMedia(file:
|
|
14
|
+
async validateMedia(file: CardMediaFile): Promise<CardMediaValidation> {
|
|
15
15
|
try {
|
|
16
16
|
const errors: string[] = [];
|
|
17
17
|
const warnings: string[] = [];
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type {
|
|
7
7
|
CardMediaAttachment,
|
|
8
|
+
CardMediaFile,
|
|
8
9
|
CardMediaGenerationRequest,
|
|
9
10
|
CardMediaGenerationResult,
|
|
10
11
|
CardMediaCompressionOptions,
|
|
@@ -42,7 +43,7 @@ export class CardMultimediaFlashcardService implements ICardMultimediaFlashcardS
|
|
|
42
43
|
* Upload media file with optional compression
|
|
43
44
|
*/
|
|
44
45
|
async uploadMedia(
|
|
45
|
-
file:
|
|
46
|
+
file: CardMediaFile,
|
|
46
47
|
options?: CardMediaCompressionOptions,
|
|
47
48
|
): Promise<CardMediaAttachment> {
|
|
48
49
|
return this.uploadService.uploadMedia(file, options);
|
|
@@ -60,7 +61,7 @@ export class CardMultimediaFlashcardService implements ICardMultimediaFlashcardS
|
|
|
60
61
|
/**
|
|
61
62
|
* Validate media file before upload
|
|
62
63
|
*/
|
|
63
|
-
async validateMedia(file:
|
|
64
|
+
async validateMedia(file: CardMediaFile): Promise<CardMediaValidation> {
|
|
64
65
|
return this.validationService.validateMedia(file);
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import type {
|
|
7
7
|
MediaAttachment,
|
|
8
8
|
MediaCompressionOptions,
|
|
9
|
+
MediaFile,
|
|
9
10
|
} from "../../domain/entities/MultimediaFlashcardTypes";
|
|
10
11
|
import { generateThumbnail, getMediaDuration } from "../utils/file-media-utils";
|
|
11
12
|
import { getMediaTypeFromMime } from "../utils/mime-type-detector";
|
|
@@ -15,7 +16,7 @@ export class MediaUploadService {
|
|
|
15
16
|
* Upload media file with optional compression
|
|
16
17
|
*/
|
|
17
18
|
async uploadMedia(
|
|
18
|
-
file:
|
|
19
|
+
file: MediaFile,
|
|
19
20
|
_options?: MediaCompressionOptions,
|
|
20
21
|
): Promise<MediaAttachment> {
|
|
21
22
|
try {
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { formatFileSize } from "../utils/media-collection-utils";
|
|
7
|
-
import type { MediaValidation } from "../../domain/entities/MultimediaFlashcardTypes";
|
|
7
|
+
import type { MediaValidation, MediaFile } from "../../domain/entities/MultimediaFlashcardTypes";
|
|
8
8
|
|
|
9
9
|
export class MediaValidationService {
|
|
10
10
|
/**
|
|
11
11
|
* Validate media file before upload
|
|
12
12
|
*/
|
|
13
|
-
async validateMedia(file:
|
|
13
|
+
async validateMedia(file: MediaFile): Promise<MediaValidation> {
|
|
14
14
|
try {
|
|
15
15
|
const errors: string[] = [];
|
|
16
16
|
const warnings: string[] = [];
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type {
|
|
7
7
|
MediaAttachment,
|
|
8
|
+
MediaFile,
|
|
8
9
|
MediaGenerationRequest,
|
|
9
10
|
MediaGenerationResult,
|
|
10
11
|
MediaCompressionOptions,
|
|
@@ -40,7 +41,7 @@ export class MultimediaFlashcardService {
|
|
|
40
41
|
* Upload media file with optional compression
|
|
41
42
|
*/
|
|
42
43
|
async uploadMedia(
|
|
43
|
-
file:
|
|
44
|
+
file: MediaFile,
|
|
44
45
|
options?: MediaCompressionOptions,
|
|
45
46
|
): Promise<MediaAttachment> {
|
|
46
47
|
return this.uploadService.uploadMedia(file, options);
|
|
@@ -58,7 +59,7 @@ export class MultimediaFlashcardService {
|
|
|
58
59
|
/**
|
|
59
60
|
* Validate media file before upload
|
|
60
61
|
*/
|
|
61
|
-
async validateMedia(file:
|
|
62
|
+
async validateMedia(file: MediaFile): Promise<MediaValidation> {
|
|
62
63
|
return this.validationService.validateMedia(file);
|
|
63
64
|
}
|
|
64
65
|
|
|
@@ -5,17 +5,19 @@
|
|
|
5
5
|
|
|
6
6
|
import type {
|
|
7
7
|
CardMediaAttachment,
|
|
8
|
+
CardMediaFile,
|
|
8
9
|
CardMediaGenerationRequest,
|
|
9
10
|
CardMediaGenerationResult,
|
|
10
11
|
CardMediaCompressionOptions,
|
|
11
12
|
CardMediaValidation,
|
|
12
13
|
CardMediaUploadProgress,
|
|
13
14
|
CardMultimediaFlashcard,
|
|
15
|
+
CreateCardMultimediaData,
|
|
14
16
|
} from "../../domain/entities/CardMultimedia.types";
|
|
15
17
|
|
|
16
18
|
export interface UseCardMediaUploadResult {
|
|
17
19
|
uploadMedia: (
|
|
18
|
-
file:
|
|
20
|
+
file: CardMediaFile,
|
|
19
21
|
options?: CardMediaCompressionOptions,
|
|
20
22
|
) => Promise<CardMediaAttachment>;
|
|
21
23
|
isUploading: boolean;
|
|
@@ -33,14 +35,14 @@ export interface UseCardMediaGenerationResult {
|
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export interface UseCardMediaValidationResult {
|
|
36
|
-
validateMedia: (file:
|
|
38
|
+
validateMedia: (file: CardMediaFile) => Promise<CardMediaValidation>;
|
|
37
39
|
isValidating: boolean;
|
|
38
40
|
validation: CardMediaValidation | null;
|
|
39
41
|
error: string | null;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
export interface UseCardMultimediaFlashcardResult {
|
|
43
|
-
createCardMultimedia: (cardData:
|
|
45
|
+
createCardMultimedia: (cardData: CreateCardMultimediaData) => Promise<CardMultimediaFlashcard>;
|
|
44
46
|
updateCardMedia: (
|
|
45
47
|
cardId: string,
|
|
46
48
|
media: CardMediaAttachment[],
|
|
@@ -5,17 +5,19 @@
|
|
|
5
5
|
|
|
6
6
|
import type {
|
|
7
7
|
MediaAttachment,
|
|
8
|
+
MediaFile,
|
|
8
9
|
MediaGenerationRequest,
|
|
9
10
|
MediaGenerationResult,
|
|
10
11
|
MediaCompressionOptions,
|
|
11
12
|
MediaValidation,
|
|
12
13
|
MediaUploadProgress,
|
|
13
14
|
MultimediaFlashcard,
|
|
15
|
+
CreateMultimediaCardData,
|
|
14
16
|
} from "../../domain/entities/MultimediaFlashcardTypes";
|
|
15
17
|
|
|
16
18
|
export interface UseMediaUploadResult {
|
|
17
19
|
uploadMedia: (
|
|
18
|
-
file:
|
|
20
|
+
file: MediaFile,
|
|
19
21
|
options?: MediaCompressionOptions,
|
|
20
22
|
) => Promise<MediaAttachment>;
|
|
21
23
|
isUploading: boolean;
|
|
@@ -33,14 +35,14 @@ export interface UseMediaGenerationResult {
|
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export interface UseMediaValidationResult {
|
|
36
|
-
validateMedia: (file:
|
|
38
|
+
validateMedia: (file: MediaFile) => Promise<MediaValidation>;
|
|
37
39
|
isValidating: boolean;
|
|
38
40
|
validation: MediaValidation | null;
|
|
39
41
|
error: string | null;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
export interface UseMultimediaFlashcardResult {
|
|
43
|
-
createMultimediaCard: (cardData:
|
|
45
|
+
createMultimediaCard: (cardData: CreateMultimediaCardData) => Promise<MultimediaFlashcard>;
|
|
44
46
|
updateMedia: (
|
|
45
47
|
cardId: string,
|
|
46
48
|
media: MediaAttachment[],
|
|
@@ -10,6 +10,7 @@ import type { UseCardMediaUploadResult } from "./card-multimedia.types";
|
|
|
10
10
|
import type {
|
|
11
11
|
CardMediaAttachment,
|
|
12
12
|
CardMediaCompressionOptions,
|
|
13
|
+
CardMediaFile,
|
|
13
14
|
CardMediaUploadProgress,
|
|
14
15
|
} from "../../domain/entities/CardMultimedia.types";
|
|
15
16
|
|
|
@@ -20,7 +21,7 @@ export const useCardMediaUpload = (): UseCardMediaUploadResult => {
|
|
|
20
21
|
const [error, setError] = React.useState<string | null>(null);
|
|
21
22
|
|
|
22
23
|
const uploadMedia = React.useCallback(
|
|
23
|
-
async (file:
|
|
24
|
+
async (file: CardMediaFile, _options?: CardMediaCompressionOptions) => {
|
|
24
25
|
try {
|
|
25
26
|
setIsUploading(true);
|
|
26
27
|
setError(null);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import React from "react";
|
|
7
7
|
import { formatFileSize } from "../../infrastructure/utils/media-collection-utils";
|
|
8
8
|
import type { UseCardMediaValidationResult } from "./card-multimedia.types";
|
|
9
|
-
import type { CardMediaValidation } from "../../domain/entities/CardMultimedia.types";
|
|
9
|
+
import type { CardMediaValidation, CardMediaFile } from "../../domain/entities/CardMultimedia.types";
|
|
10
10
|
|
|
11
11
|
export const useCardMediaValidation = (): UseCardMediaValidationResult => {
|
|
12
12
|
const [isValidating, setIsValidating] = React.useState(false);
|
|
@@ -15,7 +15,7 @@ export const useCardMediaValidation = (): UseCardMediaValidationResult => {
|
|
|
15
15
|
const [error, setError] = React.useState<string | null>(null);
|
|
16
16
|
|
|
17
17
|
const validateMedia = React.useCallback(
|
|
18
|
-
async (file:
|
|
18
|
+
async (file: CardMediaFile): Promise<CardMediaValidation> => {
|
|
19
19
|
try {
|
|
20
20
|
setIsValidating(true);
|
|
21
21
|
setError(null);
|
|
@@ -9,6 +9,7 @@ import type { UseCardMultimediaFlashcardResult } from "./card-multimedia.types";
|
|
|
9
9
|
import type {
|
|
10
10
|
CardMediaAttachment,
|
|
11
11
|
CardMultimediaFlashcard,
|
|
12
|
+
CreateCardMultimediaData,
|
|
12
13
|
} from "../../domain/entities/CardMultimedia.types";
|
|
13
14
|
|
|
14
15
|
// Export individual hooks
|
|
@@ -33,7 +34,7 @@ export const useCardMultimediaFlashcard =
|
|
|
33
34
|
const [error, setError] = React.useState<string | null>(null);
|
|
34
35
|
|
|
35
36
|
const createCardMultimedia = React.useCallback(
|
|
36
|
-
async (cardData:
|
|
37
|
+
async (cardData: CreateCardMultimediaData): Promise<CardMultimediaFlashcard> => {
|
|
37
38
|
try {
|
|
38
39
|
setIsProcessing(true);
|
|
39
40
|
setError(null);
|
|
@@ -51,7 +52,7 @@ export const useCardMultimediaFlashcard =
|
|
|
51
52
|
hasMedia: (cardData.media || []).length > 0,
|
|
52
53
|
mediaType: extractMediaTypes(cardData.media || []),
|
|
53
54
|
isDownloaded: (cardData.media || []).every(
|
|
54
|
-
(m:
|
|
55
|
+
(m: CardMediaAttachment) => m.isDownloaded,
|
|
55
56
|
),
|
|
56
57
|
estimatedSize: calculateTotalSize(cardData.media || []),
|
|
57
58
|
createdAt: new Date().toISOString(),
|