@uploadista/react-native-core 0.0.3
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/.turbo/turbo-check.log +396 -0
- package/LICENSE +21 -0
- package/README.md +426 -0
- package/package.json +42 -0
- package/src/client/create-uploadista-client.ts +65 -0
- package/src/client/index.ts +4 -0
- package/src/components/CameraUploadButton.tsx +130 -0
- package/src/components/FileUploadButton.tsx +130 -0
- package/src/components/GalleryUploadButton.tsx +199 -0
- package/src/components/UploadList.tsx +214 -0
- package/src/components/UploadProgress.tsx +196 -0
- package/src/components/index.ts +19 -0
- package/src/hooks/index.ts +29 -0
- package/src/hooks/uploadista-context.ts +17 -0
- package/src/hooks/use-camera-upload.ts +38 -0
- package/src/hooks/use-file-upload.ts +40 -0
- package/src/hooks/use-flow-upload.ts +242 -0
- package/src/hooks/use-gallery-upload.ts +65 -0
- package/src/hooks/use-multi-upload.ts +363 -0
- package/src/hooks/use-upload-metrics.ts +82 -0
- package/src/hooks/use-upload.ts +378 -0
- package/src/hooks/use-uploadista-client.ts +23 -0
- package/src/hooks/use-uploadista-context.ts +20 -0
- package/src/index.ts +111 -0
- package/src/types/index.ts +2 -0
- package/src/types/types.ts +359 -0
- package/src/types/upload-input.ts +1 -0
- package/src/utils/fileHelpers.ts +201 -0
- package/src/utils/index.ts +36 -0
- package/src/utils/permissions.ts +177 -0
- package/src/utils/uriHelpers.ts +148 -0
- package/test-compile.ts +5 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for React Native Uploadista client
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for file picker operations
|
|
7
|
+
*/
|
|
8
|
+
export interface PickerOptions {
|
|
9
|
+
/** Allowed file types/MIME types */
|
|
10
|
+
allowedTypes?: string[];
|
|
11
|
+
/** Allow multiple selection */
|
|
12
|
+
allowMultiple?: boolean;
|
|
13
|
+
/** Maximum file size in bytes */
|
|
14
|
+
maxSize?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Options for camera operations
|
|
19
|
+
*/
|
|
20
|
+
export interface CameraOptions {
|
|
21
|
+
/** Camera to use: 'front' or 'back' */
|
|
22
|
+
cameraType?: "front" | "back";
|
|
23
|
+
/** Image quality (0-1) */
|
|
24
|
+
quality?: number;
|
|
25
|
+
/** Maximum width for captured image */
|
|
26
|
+
maxWidth?: number;
|
|
27
|
+
/** Maximum height for captured image */
|
|
28
|
+
maxHeight?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Result from a file pick operation
|
|
33
|
+
*/
|
|
34
|
+
export interface FilePickResult {
|
|
35
|
+
/** URI to the file (platform-specific format) */
|
|
36
|
+
uri: string;
|
|
37
|
+
/** File name with extension */
|
|
38
|
+
name: string;
|
|
39
|
+
/** File size in bytes */
|
|
40
|
+
size: number;
|
|
41
|
+
/** MIME type of the file (if available) */
|
|
42
|
+
mimeType?: string;
|
|
43
|
+
/** Local file path (if available) */
|
|
44
|
+
localPath?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Information about a file
|
|
49
|
+
*/
|
|
50
|
+
export interface FileInfo {
|
|
51
|
+
/** URI to the file */
|
|
52
|
+
uri: string;
|
|
53
|
+
/** File name */
|
|
54
|
+
name: string;
|
|
55
|
+
/** File size in bytes */
|
|
56
|
+
size: number;
|
|
57
|
+
/** MIME type (if available) */
|
|
58
|
+
mimeType?: string;
|
|
59
|
+
/** Last modified timestamp */
|
|
60
|
+
modificationTime?: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Interface for file system abstraction layer
|
|
65
|
+
* Provides pluggable access to file system APIs across different RN environments
|
|
66
|
+
*/
|
|
67
|
+
export interface FileSystemProvider {
|
|
68
|
+
/**
|
|
69
|
+
* Opens a document picker for selecting files
|
|
70
|
+
* @param options - Configuration for the picker
|
|
71
|
+
* @returns Promise resolving to picked file information
|
|
72
|
+
*/
|
|
73
|
+
pickDocument(options?: PickerOptions): Promise<FilePickResult>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Opens an image picker for selecting images from gallery
|
|
77
|
+
* @param options - Configuration for the picker
|
|
78
|
+
* @returns Promise resolving to picked image information
|
|
79
|
+
*/
|
|
80
|
+
pickImage(options?: PickerOptions): Promise<FilePickResult>;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Opens a video picker for selecting videos from gallery
|
|
84
|
+
* @param options - Configuration for the picker
|
|
85
|
+
* @returns Promise resolving to picked video information
|
|
86
|
+
*/
|
|
87
|
+
pickVideo(options?: PickerOptions): Promise<FilePickResult>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Captures a photo using the device camera
|
|
91
|
+
* @param options - Configuration for camera
|
|
92
|
+
* @returns Promise resolving to captured photo information
|
|
93
|
+
*/
|
|
94
|
+
pickCamera(options?: CameraOptions): Promise<FilePickResult>;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Gets a URI for a document that can be read
|
|
98
|
+
* @param filePath - Path to the document
|
|
99
|
+
* @returns Promise resolving to accessible URI
|
|
100
|
+
*/
|
|
101
|
+
getDocumentUri(filePath: string): Promise<string>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Reads file contents as ArrayBuffer
|
|
105
|
+
* @param uri - URI to read from
|
|
106
|
+
* @returns Promise resolving to file contents as ArrayBuffer
|
|
107
|
+
*/
|
|
108
|
+
readFile(uri: string): Promise<ArrayBuffer>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Gets information about a file
|
|
112
|
+
* @param uri - URI of the file
|
|
113
|
+
* @returns Promise resolving to file information
|
|
114
|
+
*/
|
|
115
|
+
getFileInfo(uri: string): Promise<FileInfo>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Configuration for file system provider
|
|
120
|
+
*/
|
|
121
|
+
export interface FileSystemProviderConfig {
|
|
122
|
+
/** Type of provider: 'expo' or 'native' */
|
|
123
|
+
type?: "expo" | "native";
|
|
124
|
+
/** Custom provider instance */
|
|
125
|
+
provider?: FileSystemProvider;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Upload state
|
|
130
|
+
*/
|
|
131
|
+
export type UploadState =
|
|
132
|
+
| "idle"
|
|
133
|
+
| "pending"
|
|
134
|
+
| "uploading"
|
|
135
|
+
| "success"
|
|
136
|
+
| "error"
|
|
137
|
+
| "cancelled";
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Upload progress information
|
|
141
|
+
*/
|
|
142
|
+
export interface UploadProgress {
|
|
143
|
+
/** Current state of upload */
|
|
144
|
+
state: UploadState;
|
|
145
|
+
/** Progress percentage (0-100) */
|
|
146
|
+
progress: number;
|
|
147
|
+
/** Bytes uploaded */
|
|
148
|
+
uploadedBytes: number;
|
|
149
|
+
/** Total bytes to upload */
|
|
150
|
+
totalBytes: number;
|
|
151
|
+
/** Upload speed in bytes per second */
|
|
152
|
+
uploadSpeed?: number;
|
|
153
|
+
/** Estimated time remaining in milliseconds */
|
|
154
|
+
timeRemaining?: number;
|
|
155
|
+
/** Error message if state is 'error' */
|
|
156
|
+
error?: Error;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Single file upload state
|
|
161
|
+
*/
|
|
162
|
+
export interface SingleUploadState extends UploadProgress {
|
|
163
|
+
/** File name */
|
|
164
|
+
fileName: string;
|
|
165
|
+
/** File size */
|
|
166
|
+
fileSize: number;
|
|
167
|
+
/** Result from server after successful upload */
|
|
168
|
+
result?: unknown;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Multi-file upload item
|
|
173
|
+
*/
|
|
174
|
+
export interface UploadItem {
|
|
175
|
+
/** Unique identifier for this upload */
|
|
176
|
+
id: string;
|
|
177
|
+
/** File information */
|
|
178
|
+
file: FilePickResult;
|
|
179
|
+
/** Upload progress */
|
|
180
|
+
progress: UploadProgress;
|
|
181
|
+
/** Result from server if successful */
|
|
182
|
+
result?: unknown;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Multi-file upload state
|
|
187
|
+
*/
|
|
188
|
+
export interface MultiUploadState {
|
|
189
|
+
/** All upload items */
|
|
190
|
+
items: UploadItem[];
|
|
191
|
+
/** Aggregate progress */
|
|
192
|
+
progress: number;
|
|
193
|
+
/** Total uploads */
|
|
194
|
+
total: number;
|
|
195
|
+
/** Completed uploads */
|
|
196
|
+
completed: number;
|
|
197
|
+
/** Failed uploads */
|
|
198
|
+
failed: number;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Flow upload state
|
|
203
|
+
*/
|
|
204
|
+
export interface FlowUploadState {
|
|
205
|
+
/** Job ID for the flow */
|
|
206
|
+
jobId?: string;
|
|
207
|
+
/** Overall state */
|
|
208
|
+
state: UploadState;
|
|
209
|
+
/** Progress percentage */
|
|
210
|
+
progress: number;
|
|
211
|
+
/** Flow execution results */
|
|
212
|
+
result?: unknown;
|
|
213
|
+
/** Error if failed */
|
|
214
|
+
error?: Error;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Options for single upload hook
|
|
219
|
+
*/
|
|
220
|
+
export interface UseSingleUploadOptions {
|
|
221
|
+
/** Flow ID to use for upload */
|
|
222
|
+
flowId?: string;
|
|
223
|
+
/** Field name for file */
|
|
224
|
+
fieldName?: string;
|
|
225
|
+
/** Additional form data */
|
|
226
|
+
metadata?: Record<string, string>;
|
|
227
|
+
/** Enable retry on failure */
|
|
228
|
+
autoRetry?: boolean;
|
|
229
|
+
/** Maximum retry attempts */
|
|
230
|
+
maxRetries?: number;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Options for multi-upload hook
|
|
235
|
+
*/
|
|
236
|
+
export interface UseMultiUploadOptions {
|
|
237
|
+
/** Max concurrent uploads */
|
|
238
|
+
maxConcurrent?: number;
|
|
239
|
+
/** Additional form data for all uploads */
|
|
240
|
+
metadata?: Record<string, string>;
|
|
241
|
+
/** Called when a file upload succeeds */
|
|
242
|
+
onSuccess?: (result: unknown) => void;
|
|
243
|
+
/** Called when a file upload fails */
|
|
244
|
+
onError?: (error: Error) => void;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Options for flow upload hook
|
|
249
|
+
*/
|
|
250
|
+
export interface UseFlowUploadOptions {
|
|
251
|
+
/** Flow ID to execute */
|
|
252
|
+
flowId: string;
|
|
253
|
+
/** Storage ID for the upload */
|
|
254
|
+
storageId: string;
|
|
255
|
+
/** Output node ID for the flow */
|
|
256
|
+
outputNodeId?: string;
|
|
257
|
+
/** Metadata to pass to flow */
|
|
258
|
+
metadata?: Record<string, unknown>;
|
|
259
|
+
/** Called when upload succeeds */
|
|
260
|
+
onSuccess?: (result: unknown) => void;
|
|
261
|
+
/** Called when upload fails */
|
|
262
|
+
onError?: (error: Error) => void;
|
|
263
|
+
/** Called when upload progress updates */
|
|
264
|
+
onProgress?: (
|
|
265
|
+
progress: number,
|
|
266
|
+
bytesUploaded: number,
|
|
267
|
+
totalBytes: number | null,
|
|
268
|
+
) => void;
|
|
269
|
+
/** Called when a chunk completes */
|
|
270
|
+
onChunkComplete?: (
|
|
271
|
+
chunkSize: number,
|
|
272
|
+
bytesAccepted: number,
|
|
273
|
+
bytesTotal: number | null,
|
|
274
|
+
) => void;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Options for camera upload hook
|
|
279
|
+
*/
|
|
280
|
+
export interface UseCameraUploadOptions {
|
|
281
|
+
/** Flow ID to use */
|
|
282
|
+
flowId?: string;
|
|
283
|
+
/** Camera options */
|
|
284
|
+
cameraOptions?: CameraOptions;
|
|
285
|
+
/** Additional metadata */
|
|
286
|
+
metadata?: Record<string, string>;
|
|
287
|
+
/** Called when upload succeeds */
|
|
288
|
+
onSuccess?: (result: unknown) => void;
|
|
289
|
+
/** Called when upload fails */
|
|
290
|
+
onError?: (error: Error) => void;
|
|
291
|
+
/** Called when upload progress updates */
|
|
292
|
+
onProgress?: (
|
|
293
|
+
progress: number,
|
|
294
|
+
bytesUploaded: number,
|
|
295
|
+
totalBytes: number | null,
|
|
296
|
+
) => void;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Options for gallery upload hook
|
|
301
|
+
*/
|
|
302
|
+
export interface UseGalleryUploadOptions {
|
|
303
|
+
/** Flow ID to use */
|
|
304
|
+
flowId?: string;
|
|
305
|
+
/** Allow multiple selection */
|
|
306
|
+
allowMultiple?: boolean;
|
|
307
|
+
/** Media type: 'photo', 'video', or 'mixed' */
|
|
308
|
+
mediaType?: "photo" | "video" | "mixed";
|
|
309
|
+
/** Additional metadata */
|
|
310
|
+
metadata?: Record<string, string>;
|
|
311
|
+
/** Called when upload succeeds */
|
|
312
|
+
onSuccess?: (result: unknown) => void;
|
|
313
|
+
/** Called when upload fails */
|
|
314
|
+
onError?: (error: Error) => void;
|
|
315
|
+
/** Called when upload progress updates */
|
|
316
|
+
onProgress?: (
|
|
317
|
+
progress: number,
|
|
318
|
+
bytesUploaded: number,
|
|
319
|
+
totalBytes: number | null,
|
|
320
|
+
) => void;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Options for file upload hook
|
|
325
|
+
*/
|
|
326
|
+
export interface UseFileUploadOptions {
|
|
327
|
+
/** Flow ID to use */
|
|
328
|
+
flowId?: string;
|
|
329
|
+
/** Allowed file types */
|
|
330
|
+
allowedTypes?: string[];
|
|
331
|
+
/** Additional metadata */
|
|
332
|
+
metadata?: Record<string, string>;
|
|
333
|
+
/** Called when upload succeeds */
|
|
334
|
+
onSuccess?: (result: unknown) => void;
|
|
335
|
+
/** Called when upload fails */
|
|
336
|
+
onError?: (error: Error) => void;
|
|
337
|
+
/** Called when upload progress updates */
|
|
338
|
+
onProgress?: (
|
|
339
|
+
progress: number,
|
|
340
|
+
bytesUploaded: number,
|
|
341
|
+
totalBytes: number | null,
|
|
342
|
+
) => void;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Metrics for upload performance
|
|
347
|
+
*/
|
|
348
|
+
export interface UploadMetrics {
|
|
349
|
+
/** Total bytes uploaded */
|
|
350
|
+
totalBytes: number;
|
|
351
|
+
/** Total upload duration in milliseconds */
|
|
352
|
+
durationMs: number;
|
|
353
|
+
/** Average upload speed in bytes/second */
|
|
354
|
+
avgSpeed: number;
|
|
355
|
+
/** Peak upload speed in bytes/second */
|
|
356
|
+
peakSpeed: number;
|
|
357
|
+
/** Number of retries */
|
|
358
|
+
retries: number;
|
|
359
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type ReactNativeUploadInput = Blob | File | string | { uri: string };
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File utility functions for React Native uploads
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Format file size to human readable string
|
|
7
|
+
* @param bytes - Size in bytes
|
|
8
|
+
* @returns Formatted size string (e.g., "1.5 MB")
|
|
9
|
+
*/
|
|
10
|
+
export function formatFileSize(bytes: number): string {
|
|
11
|
+
if (bytes === 0) return "0 Bytes";
|
|
12
|
+
|
|
13
|
+
const k = 1024;
|
|
14
|
+
const sizes = ["Bytes", "KB", "MB", "GB"];
|
|
15
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
16
|
+
|
|
17
|
+
return `${Math.round((bytes / k ** i) * 100) / 100} ${sizes[i]}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get MIME type from file name
|
|
22
|
+
* @param fileName - File name with extension
|
|
23
|
+
* @returns MIME type or 'application/octet-stream' as fallback
|
|
24
|
+
*/
|
|
25
|
+
export function getMimeTypeFromFileName(fileName: string): string {
|
|
26
|
+
const mimeTypes: Record<string, string> = {
|
|
27
|
+
// Images
|
|
28
|
+
".jpg": "image/jpeg",
|
|
29
|
+
".jpeg": "image/jpeg",
|
|
30
|
+
".png": "image/png",
|
|
31
|
+
".gif": "image/gif",
|
|
32
|
+
".bmp": "image/bmp",
|
|
33
|
+
".webp": "image/webp",
|
|
34
|
+
".svg": "image/svg+xml",
|
|
35
|
+
|
|
36
|
+
// Videos
|
|
37
|
+
".mp4": "video/mp4",
|
|
38
|
+
".avi": "video/x-msvideo",
|
|
39
|
+
".mov": "video/quicktime",
|
|
40
|
+
".wmv": "video/x-ms-wmv",
|
|
41
|
+
".flv": "video/x-flv",
|
|
42
|
+
".mkv": "video/x-matroska",
|
|
43
|
+
".webm": "video/webm",
|
|
44
|
+
|
|
45
|
+
// Audio
|
|
46
|
+
".mp3": "audio/mpeg",
|
|
47
|
+
".wav": "audio/wav",
|
|
48
|
+
".aac": "audio/aac",
|
|
49
|
+
".flac": "audio/flac",
|
|
50
|
+
".m4a": "audio/mp4",
|
|
51
|
+
|
|
52
|
+
// Documents
|
|
53
|
+
".pdf": "application/pdf",
|
|
54
|
+
".doc": "application/msword",
|
|
55
|
+
".docx":
|
|
56
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
57
|
+
".xls": "application/vnd.ms-excel",
|
|
58
|
+
".xlsx":
|
|
59
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
60
|
+
".ppt": "application/vnd.ms-powerpoint",
|
|
61
|
+
".pptx":
|
|
62
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
63
|
+
".txt": "text/plain",
|
|
64
|
+
".csv": "text/csv",
|
|
65
|
+
".json": "application/json",
|
|
66
|
+
".xml": "application/xml",
|
|
67
|
+
".zip": "application/zip",
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const ext = fileName.toLowerCase().slice(fileName.lastIndexOf("."));
|
|
71
|
+
return mimeTypes[ext] || "application/octet-stream";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check if file type is allowed
|
|
76
|
+
* @param fileName - File name to check
|
|
77
|
+
* @param allowedTypes - Array of allowed MIME types (e.g., ['image/jpeg', 'image/png'])
|
|
78
|
+
* @returns True if file type is allowed
|
|
79
|
+
*/
|
|
80
|
+
export function isFileTypeAllowed(
|
|
81
|
+
fileName: string,
|
|
82
|
+
allowedTypes: string[],
|
|
83
|
+
): boolean {
|
|
84
|
+
if (!allowedTypes || allowedTypes.length === 0) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const mimeType = getMimeTypeFromFileName(fileName);
|
|
89
|
+
return allowedTypes.some((allowed) => {
|
|
90
|
+
if (allowed.endsWith("/*")) {
|
|
91
|
+
// Handle wildcard patterns like 'image/*'
|
|
92
|
+
const [type] = allowed.split("/");
|
|
93
|
+
return mimeType.startsWith(`${type}/`);
|
|
94
|
+
}
|
|
95
|
+
return allowed === mimeType;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Check if file size is within limits
|
|
101
|
+
* @param fileSize - File size in bytes
|
|
102
|
+
* @param maxSize - Maximum allowed size in bytes (optional)
|
|
103
|
+
* @param minSize - Minimum allowed size in bytes (optional)
|
|
104
|
+
* @returns True if file size is within limits
|
|
105
|
+
*/
|
|
106
|
+
export function isFileSizeValid(
|
|
107
|
+
fileSize: number,
|
|
108
|
+
maxSize?: number,
|
|
109
|
+
minSize?: number,
|
|
110
|
+
): boolean {
|
|
111
|
+
if (maxSize !== undefined && fileSize > maxSize) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (minSize !== undefined && fileSize < minSize) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get file extension
|
|
124
|
+
* @param fileName - File name
|
|
125
|
+
* @returns File extension without dot (e.g., 'pdf' for 'document.pdf')
|
|
126
|
+
*/
|
|
127
|
+
export function getFileExtension(fileName: string): string {
|
|
128
|
+
const lastDot = fileName.lastIndexOf(".");
|
|
129
|
+
if (lastDot === -1) return "";
|
|
130
|
+
return fileName.slice(lastDot + 1).toLowerCase();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get file name without extension
|
|
135
|
+
* @param fileName - File name
|
|
136
|
+
* @returns File name without extension
|
|
137
|
+
*/
|
|
138
|
+
export function getFileNameWithoutExtension(fileName: string): string {
|
|
139
|
+
const lastDot = fileName.lastIndexOf(".");
|
|
140
|
+
if (lastDot === -1) return fileName;
|
|
141
|
+
return fileName.slice(0, lastDot);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Check if file is an image
|
|
146
|
+
* @param fileName - File name
|
|
147
|
+
* @returns True if file is an image
|
|
148
|
+
*/
|
|
149
|
+
export function isImageFile(fileName: string): boolean {
|
|
150
|
+
const imageExtensions = [
|
|
151
|
+
".jpg",
|
|
152
|
+
".jpeg",
|
|
153
|
+
".png",
|
|
154
|
+
".gif",
|
|
155
|
+
".bmp",
|
|
156
|
+
".webp",
|
|
157
|
+
".svg",
|
|
158
|
+
];
|
|
159
|
+
const ext = fileName.toLowerCase().slice(fileName.lastIndexOf("."));
|
|
160
|
+
return imageExtensions.includes(ext);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Check if file is a video
|
|
165
|
+
* @param fileName - File name
|
|
166
|
+
* @returns True if file is a video
|
|
167
|
+
*/
|
|
168
|
+
export function isVideoFile(fileName: string): boolean {
|
|
169
|
+
const videoExtensions = [
|
|
170
|
+
".mp4",
|
|
171
|
+
".avi",
|
|
172
|
+
".mov",
|
|
173
|
+
".wmv",
|
|
174
|
+
".flv",
|
|
175
|
+
".mkv",
|
|
176
|
+
".webm",
|
|
177
|
+
];
|
|
178
|
+
const ext = fileName.toLowerCase().slice(fileName.lastIndexOf("."));
|
|
179
|
+
return videoExtensions.includes(ext);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Check if file is a document
|
|
184
|
+
* @param fileName - File name
|
|
185
|
+
* @returns True if file is a document
|
|
186
|
+
*/
|
|
187
|
+
export function isDocumentFile(fileName: string): boolean {
|
|
188
|
+
const docExtensions = [
|
|
189
|
+
".pdf",
|
|
190
|
+
".doc",
|
|
191
|
+
".docx",
|
|
192
|
+
".xls",
|
|
193
|
+
".xlsx",
|
|
194
|
+
".ppt",
|
|
195
|
+
".pptx",
|
|
196
|
+
".txt",
|
|
197
|
+
".csv",
|
|
198
|
+
];
|
|
199
|
+
const ext = fileName.toLowerCase().slice(fileName.lastIndexOf("."));
|
|
200
|
+
return docExtensions.includes(ext);
|
|
201
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// File helpers
|
|
2
|
+
export {
|
|
3
|
+
formatFileSize,
|
|
4
|
+
getFileExtension,
|
|
5
|
+
getFileNameWithoutExtension,
|
|
6
|
+
getMimeTypeFromFileName,
|
|
7
|
+
isDocumentFile,
|
|
8
|
+
isFileSizeValid,
|
|
9
|
+
isFileTypeAllowed,
|
|
10
|
+
isImageFile,
|
|
11
|
+
isVideoFile,
|
|
12
|
+
} from "./fileHelpers";
|
|
13
|
+
// Permission helpers
|
|
14
|
+
export {
|
|
15
|
+
getPermissionStatus,
|
|
16
|
+
hasPermissions,
|
|
17
|
+
openAppSettings,
|
|
18
|
+
PermissionStatus,
|
|
19
|
+
PermissionType,
|
|
20
|
+
requestCameraPermission,
|
|
21
|
+
requestPermissions,
|
|
22
|
+
requestPhotoLibraryPermission,
|
|
23
|
+
requestStorageReadPermission,
|
|
24
|
+
requestStorageWritePermission,
|
|
25
|
+
} from "./permissions";
|
|
26
|
+
// URI helpers
|
|
27
|
+
export {
|
|
28
|
+
getDirectoryFromUri,
|
|
29
|
+
getFileNameFromUri,
|
|
30
|
+
getMimeTypeFromUri,
|
|
31
|
+
isContentUri,
|
|
32
|
+
isFileUri,
|
|
33
|
+
normalizeUri,
|
|
34
|
+
pathToUri,
|
|
35
|
+
uriToPath,
|
|
36
|
+
} from "./uriHelpers";
|