@umituz/react-native-design-system 2.9.40 → 2.9.42

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-design-system",
3
- "version": "2.9.40",
3
+ "version": "2.9.42",
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",
@@ -44,15 +44,44 @@ export type { SaveResult, SaveOptions } from "./infrastructure/services/MediaSav
44
44
  // Presentation Layer - Original Media Hooks
45
45
  export { useMedia } from "./presentation/hooks/useMedia";
46
46
 
47
- // Media Helper Utilities
47
+ // URL Media Detection
48
48
  export {
49
49
  getMediaTypeFromUrl,
50
50
  isVideoUrl,
51
51
  isImageUrl,
52
- getMediaType,
53
- getCardMediaType,
52
+ isAudioUrl,
53
+ type UrlMediaType,
54
+ } from "./infrastructure/utils/url-media-detector";
55
+
56
+ // MIME Type Detection
57
+ export {
58
+ getMediaTypeFromMime,
59
+ isVideoMime,
60
+ isImageMime,
61
+ isAudioMime,
62
+ type MimeMediaType,
63
+ } from "./infrastructure/utils/mime-type-detector";
64
+
65
+ // Media URL Extraction
66
+ export {
67
+ extractMediaUrl,
68
+ extractVideoUrl,
69
+ extractImageUrl,
70
+ type MediaUrlResult,
71
+ } from "./infrastructure/utils/media-url-extractor";
72
+
73
+ // Media Collection Utilities
74
+ export {
75
+ extractMediaTypes,
76
+ calculateTotalSize,
54
77
  formatFileSize,
55
- } from "./infrastructure/utils/mediaHelpers";
78
+ } from "./infrastructure/utils/media-collection-utils";
79
+
80
+ // File Media Utilities
81
+ export {
82
+ getMediaDuration,
83
+ generateThumbnail,
84
+ } from "./infrastructure/utils/file-media-utils";
56
85
 
57
86
  // Multimedia Flashcard Support
58
87
  export type {
@@ -9,11 +9,8 @@ import type {
9
9
  CardMediaAttachment,
10
10
  CardMediaCompressionOptions,
11
11
  } from "../../domain/entities/CardMultimedia.types";
12
- import {
13
- generateThumbnail,
14
- getCardMediaType,
15
- getMediaDuration,
16
- } from "../utils/mediaHelpers";
12
+ import { generateThumbnail, getMediaDuration } from "../utils/file-media-utils";
13
+ import { getMediaTypeFromMime } from "../utils/mime-type-detector";
17
14
 
18
15
  export class CardMediaUploadService {
19
16
  /**
@@ -29,7 +26,7 @@ export class CardMediaUploadService {
29
26
 
30
27
  const attachment: CardMediaAttachment = {
31
28
  id: `card_media_${Date.now()}`,
32
- type: getCardMediaType(file.type),
29
+ type: getMediaTypeFromMime(file.type),
33
30
  position: "both",
34
31
  url: `https://storage.example.com/media/${Date.now()}_${file.name}`,
35
32
  filename: file.name,
@@ -7,11 +7,8 @@ import type {
7
7
  MediaAttachment,
8
8
  MediaCompressionOptions,
9
9
  } from "../../domain/entities/MultimediaFlashcardTypes";
10
- import {
11
- generateThumbnail,
12
- getMediaDuration,
13
- getMediaType,
14
- } from "../utils/mediaHelpers";
10
+ import { generateThumbnail, getMediaDuration } from "../utils/file-media-utils";
11
+ import { getMediaTypeFromMime } from "../utils/mime-type-detector";
15
12
 
16
13
  export class MediaUploadService {
17
14
  /**
@@ -27,7 +24,7 @@ export class MediaUploadService {
27
24
 
28
25
  const attachment: MediaAttachment = {
29
26
  id: `media_${Date.now()}`,
30
- type: getMediaType(file.type),
27
+ type: getMediaTypeFromMime(file.type),
31
28
  position: "both",
32
29
  url: `https://storage.example.com/media/${Date.now()}_${file.name}`,
33
30
  filename: file.name,
@@ -0,0 +1,17 @@
1
+ interface FileWithType {
2
+ readonly type: string;
3
+ }
4
+
5
+ export async function getMediaDuration(file: FileWithType): Promise<number | undefined> {
6
+ if (file.type.startsWith("audio/") || file.type.startsWith("video/")) {
7
+ return Math.floor(Math.random() * 60) + 10;
8
+ }
9
+ return undefined;
10
+ }
11
+
12
+ export function generateThumbnail(file: FileWithType): string | undefined {
13
+ if (file.type.startsWith("video/")) {
14
+ return `https://picsum.photos/200/150?random=${Date.now()}`;
15
+ }
16
+ return undefined;
17
+ }
@@ -0,0 +1,27 @@
1
+ import type { CardMediaAttachment } from "../../domain/entities/CardMultimedia.types";
2
+ import type { MediaAttachment } from "../../domain/entities/MultimediaFlashcardTypes";
3
+
4
+ type MediaType = "image" | "audio" | "video";
5
+
6
+ const FILE_SIZE_UNITS = ["Bytes", "KB", "MB", "GB"] as const;
7
+
8
+ export function extractMediaTypes(
9
+ media: readonly (CardMediaAttachment | MediaAttachment)[]
10
+ ): MediaType[] {
11
+ const types = new Set<MediaType>();
12
+ media.forEach((m) => types.add(m.type));
13
+ return Array.from(types);
14
+ }
15
+
16
+ export function calculateTotalSize(
17
+ media: readonly (CardMediaAttachment | MediaAttachment)[]
18
+ ): number {
19
+ return media.reduce((total, m) => total + m.fileSize, 0);
20
+ }
21
+
22
+ export function formatFileSize(bytes: number): string {
23
+ if (bytes === 0) return "0 Bytes";
24
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
25
+ const size = Math.round((bytes / Math.pow(1024, i)) * 100) / 100;
26
+ return `${size} ${FILE_SIZE_UNITS[i]}`;
27
+ }
@@ -0,0 +1,50 @@
1
+ export interface MediaUrlResult {
2
+ readonly url: string;
3
+ readonly isVideo: boolean;
4
+ }
5
+
6
+ function isRecord(value: unknown): value is Record<string, unknown> {
7
+ return typeof value === "object" && value !== null;
8
+ }
9
+
10
+ function findStringValue(obj: Record<string, unknown>, keys: readonly string[]): string | undefined {
11
+ for (const key of keys) {
12
+ const value = obj[key];
13
+ if (typeof value === "string" && value.length > 0) return value;
14
+ }
15
+ return undefined;
16
+ }
17
+
18
+ const VIDEO_URL_KEYS = ["videoUrl", "video_url", "videoURL"] as const;
19
+ const IMAGE_URL_KEYS = ["imageUrl", "image_url", "imageURL", "uri", "url"] as const;
20
+
21
+ export function extractMediaUrl(result: unknown): MediaUrlResult | null {
22
+ if (!isRecord(result)) return null;
23
+
24
+ const output = isRecord(result.output) ? result.output : undefined;
25
+ const sources = output ? [output, result] : [result];
26
+
27
+ for (const source of sources) {
28
+ const videoUrl = findStringValue(source, VIDEO_URL_KEYS);
29
+ if (videoUrl) return { url: videoUrl, isVideo: true };
30
+ }
31
+
32
+ for (const source of sources) {
33
+ const imageUrl = findStringValue(source, IMAGE_URL_KEYS);
34
+ if (imageUrl) return { url: imageUrl, isVideo: false };
35
+ }
36
+
37
+ return null;
38
+ }
39
+
40
+ export function extractVideoUrl(result: unknown): string | null {
41
+ const media = extractMediaUrl(result);
42
+ if (media?.isVideo) return media.url;
43
+ return null;
44
+ }
45
+
46
+ export function extractImageUrl(result: unknown): string | null {
47
+ const media = extractMediaUrl(result);
48
+ if (media && !media.isVideo) return media.url;
49
+ return null;
50
+ }
@@ -0,0 +1,20 @@
1
+ export type MimeMediaType = "image" | "audio" | "video";
2
+
3
+ export function getMediaTypeFromMime(mimeType: string): MimeMediaType {
4
+ if (mimeType.startsWith("image/")) return "image";
5
+ if (mimeType.startsWith("audio/")) return "audio";
6
+ if (mimeType.startsWith("video/")) return "video";
7
+ return "image";
8
+ }
9
+
10
+ export function isVideoMime(mimeType: string): boolean {
11
+ return mimeType.startsWith("video/");
12
+ }
13
+
14
+ export function isImageMime(mimeType: string): boolean {
15
+ return mimeType.startsWith("image/");
16
+ }
17
+
18
+ export function isAudioMime(mimeType: string): boolean {
19
+ return mimeType.startsWith("audio/");
20
+ }
@@ -0,0 +1,34 @@
1
+ export type UrlMediaType = "image" | "video" | "audio" | "unknown";
2
+
3
+ const VIDEO_EXTENSIONS = [".mp4", ".webm", ".mov", ".avi", ".mkv", ".m4v"];
4
+ const IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg"];
5
+ const AUDIO_EXTENSIONS = [".mp3", ".wav", ".ogg", ".m4a", ".aac", ".flac"];
6
+
7
+ function getExtensionFromUrl(url: string): string | null {
8
+ if (!url) return null;
9
+ const urlWithoutParams = url.toLowerCase().split("?")[0];
10
+ const lastDotIndex = urlWithoutParams.lastIndexOf(".");
11
+ if (lastDotIndex === -1) return null;
12
+ return urlWithoutParams.substring(lastDotIndex);
13
+ }
14
+
15
+ export function getMediaTypeFromUrl(url: string): UrlMediaType {
16
+ const extension = getExtensionFromUrl(url);
17
+ if (!extension) return "unknown";
18
+ if (VIDEO_EXTENSIONS.includes(extension)) return "video";
19
+ if (IMAGE_EXTENSIONS.includes(extension)) return "image";
20
+ if (AUDIO_EXTENSIONS.includes(extension)) return "audio";
21
+ return "unknown";
22
+ }
23
+
24
+ export function isVideoUrl(url: string): boolean {
25
+ return getMediaTypeFromUrl(url) === "video";
26
+ }
27
+
28
+ export function isImageUrl(url: string): boolean {
29
+ return getMediaTypeFromUrl(url) === "image";
30
+ }
31
+
32
+ export function isAudioUrl(url: string): boolean {
33
+ return getMediaTypeFromUrl(url) === "audio";
34
+ }
@@ -4,11 +4,8 @@
4
4
  */
5
5
 
6
6
  import React from "react";
7
- import {
8
- generateThumbnail,
9
- getCardMediaType,
10
- getMediaDuration,
11
- } from "../../infrastructure/utils/mediaHelpers";
7
+ import { generateThumbnail, getMediaDuration } from "../../infrastructure/utils/file-media-utils";
8
+ import { getMediaTypeFromMime } from "../../infrastructure/utils/mime-type-detector";
12
9
  import type { UseCardMediaUploadResult } from "./card-multimedia.types";
13
10
  import type {
14
11
  CardMediaAttachment,
@@ -43,7 +40,7 @@ export const useCardMediaUpload = (): UseCardMediaUploadResult => {
43
40
 
44
41
  const attachment: CardMediaAttachment = {
45
42
  id: `card_media_${Date.now()}`,
46
- type: getCardMediaType(file.type),
43
+ type: getMediaTypeFromMime(file.type),
47
44
  position: "both",
48
45
  url: `https://storage.example.com/media/${Date.now()}_${file.name}`,
49
46
  filename: file.name,
@@ -4,11 +4,8 @@
4
4
  */
5
5
 
6
6
  import React from "react";
7
- import {
8
- generateThumbnail,
9
- getMediaDuration,
10
- getMediaType,
11
- } from "../../infrastructure/utils/mediaHelpers";
7
+ import { generateThumbnail, getMediaDuration } from "../../infrastructure/utils/file-media-utils";
8
+ import { getMediaTypeFromMime } from "../../infrastructure/utils/mime-type-detector";
12
9
  import type { UseMediaUploadResult } from "./multimedia.types";
13
10
  import type {
14
11
  MediaAttachment,
@@ -43,7 +40,7 @@ export const useMediaUpload = (): UseMediaUploadResult => {
43
40
 
44
41
  const attachment: MediaAttachment = {
45
42
  id: `media_${Date.now()}`,
46
- type: getMediaType(file.type),
43
+ type: getMediaTypeFromMime(file.type),
47
44
  position: "both",
48
45
  url: `https://storage.example.com/media/${Date.now()}_${file.name}`,
49
46
  filename: file.name,
@@ -1,126 +0,0 @@
1
- /**
2
- * Media Helper Utilities
3
- * Shared helper functions for media operations
4
- */
5
-
6
- import type {
7
- CardMediaAttachment,
8
- CardMediaType,
9
- } from "../../domain/entities/CardMultimedia.types";
10
- import type { MediaAttachment } from "../../domain/entities/MultimediaFlashcardTypes";
11
-
12
- /**
13
- * Get media type from MIME type for CardMedia
14
- */
15
- export const getCardMediaType = (mimeType: string): CardMediaType => {
16
- if (mimeType.startsWith("image/")) return "image";
17
- if (mimeType.startsWith("audio/")) return "audio";
18
- if (mimeType.startsWith("video/")) return "video";
19
- return "image";
20
- };
21
-
22
- /**
23
- * Get media type from MIME type for Media
24
- */
25
- export const getMediaType = (mimeType: string): "image" | "audio" | "video" => {
26
- if (mimeType.startsWith("image/")) return "image";
27
- if (mimeType.startsWith("audio/")) return "audio";
28
- if (mimeType.startsWith("video/")) return "video";
29
- return "image";
30
- };
31
-
32
- /**
33
- * Get media duration for audio/video files
34
- */
35
- export const getMediaDuration = async (
36
- file: any
37
- ): Promise<number | undefined> => {
38
- if (file.type.startsWith("audio/") || file.type.startsWith("video/")) {
39
- return Math.floor(Math.random() * 60) + 10;
40
- }
41
- return undefined;
42
- };
43
-
44
- /**
45
- * Generate thumbnail URL for video files
46
- */
47
- export const generateThumbnail = (file: any): string | undefined => {
48
- if (file.type.startsWith("video/")) {
49
- return `https://picsum.photos/200/150?random=${Date.now()}`;
50
- }
51
- return undefined;
52
- };
53
-
54
- /**
55
- * Extract unique media types from attachments
56
- */
57
- export const extractMediaTypes = (
58
- media: CardMediaAttachment[] | MediaAttachment[]
59
- ): ("image" | "audio" | "video")[] => {
60
- const types: Set<string> = new Set();
61
- media.forEach((m) => types.add(m.type));
62
- return Array.from(types) as ("image" | "audio" | "video")[];
63
- };
64
-
65
- /**
66
- * Calculate total file size of media attachments
67
- */
68
- export const calculateTotalSize = (
69
- media: CardMediaAttachment[] | MediaAttachment[]
70
- ): number => {
71
- return media.reduce((total, m) => total + m.fileSize, 0);
72
- };
73
-
74
- /**
75
- * Format bytes to human-readable file size
76
- */
77
- export const formatFileSize = (bytes: number): string => {
78
- const sizes = ["Bytes", "KB", "MB", "GB"];
79
- if (bytes === 0) return "0 Bytes";
80
- const i = Math.floor(Math.log(bytes) / Math.log(1024));
81
- return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + " " + sizes[i];
82
- };
83
-
84
- // URL-based media type detection constants
85
- const VIDEO_EXTENSIONS = [".mp4", ".webm", ".mov", ".avi", ".mkv", ".m4v"];
86
- const IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg"];
87
- const AUDIO_EXTENSIONS = [".mp3", ".wav", ".ogg", ".m4a", ".aac", ".flac"];
88
-
89
- /**
90
- * Get media type from URL based on file extension
91
- */
92
- export const getMediaTypeFromUrl = (
93
- url: string
94
- ): "image" | "video" | "audio" | "unknown" => {
95
- if (!url) return "unknown";
96
-
97
- const lowercaseUrl = url.toLowerCase();
98
-
99
- // Extract extension (handle query params)
100
- const urlWithoutParams = lowercaseUrl.split("?")[0];
101
- const lastDotIndex = urlWithoutParams.lastIndexOf(".");
102
- if (lastDotIndex === -1) return "unknown";
103
-
104
- const extension = urlWithoutParams.substring(lastDotIndex);
105
-
106
- if (VIDEO_EXTENSIONS.includes(extension)) return "video";
107
- if (IMAGE_EXTENSIONS.includes(extension)) return "image";
108
- if (AUDIO_EXTENSIONS.includes(extension)) return "audio";
109
-
110
- return "unknown";
111
- };
112
-
113
- /**
114
- * Check if URL points to a video file
115
- */
116
- export const isVideoUrl = (url: string): boolean => {
117
- return getMediaTypeFromUrl(url) === "video";
118
- };
119
-
120
- /**
121
- * Check if URL points to an image file
122
- */
123
- export const isImageUrl = (url: string): boolean => {
124
- const mediaType = getMediaTypeFromUrl(url);
125
- return mediaType === "image" || mediaType === "unknown";
126
- };