@uploadista/react-native-core 0.0.15-beta.4 → 0.0.15
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/dist/index.d.mts +18 -18
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/contexts/flow-manager-context.tsx +19 -35
- package/src/hooks/use-flow-upload.ts +40 -28
- package/src/hooks/use-multi-upload.ts +4 -1
- package/src/types/types.ts +10 -5
package/dist/index.d.mts
CHANGED
|
@@ -2,14 +2,12 @@ import * as react0 from "react";
|
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
import * as _uploadista_client_core3 from "@uploadista/client-core";
|
|
4
4
|
import { Base64Service, ConnectionMetrics, ConnectionPoolConfig, ConnectionPoolConfig as ConnectionPoolConfig$1, DetailedConnectionMetrics, FileReaderService, FlowManager, FlowManagerCallbacks, FlowUploadOptions, FlowUploadState, FlowUploadStatus, HttpClient, IdGenerationService, ServiceContainer, ServiceContainer as ServiceContainer$1, StorageService, UploadState, UploadStatus, UploadistaClientOptions, UploadistaEvent } from "@uploadista/client-core";
|
|
5
|
+
import { TypedOutput } from "@uploadista/core/flow";
|
|
5
6
|
import { UploadFile } from "@uploadista/core/types";
|
|
6
|
-
import * as
|
|
7
|
+
import * as react_jsx_runtime4 from "react/jsx-runtime";
|
|
7
8
|
import * as _uploadista_core0 from "@uploadista/core";
|
|
8
9
|
|
|
9
10
|
//#region src/types/types.d.ts
|
|
10
|
-
/**
|
|
11
|
-
* Core types for React Native Uploadista client
|
|
12
|
-
*/
|
|
13
11
|
/**
|
|
14
12
|
* Options for file picker operations
|
|
15
13
|
*/
|
|
@@ -167,7 +165,7 @@ interface UploadItem {
|
|
|
167
165
|
/** Upload progress */
|
|
168
166
|
progress: UploadProgress$1;
|
|
169
167
|
/** Result from server if successful */
|
|
170
|
-
result?:
|
|
168
|
+
result?: UploadFile;
|
|
171
169
|
}
|
|
172
170
|
/**
|
|
173
171
|
* Options for multi-upload hook
|
|
@@ -194,8 +192,10 @@ interface UseFlowUploadOptions {
|
|
|
194
192
|
outputNodeId?: string;
|
|
195
193
|
/** Metadata to pass to flow */
|
|
196
194
|
metadata?: Record<string, unknown>;
|
|
197
|
-
/** Called when upload succeeds */
|
|
198
|
-
onSuccess?: (
|
|
195
|
+
/** Called when upload succeeds (receives typed outputs from all output nodes) */
|
|
196
|
+
onSuccess?: (outputs: TypedOutput[]) => void;
|
|
197
|
+
/** Called when the flow completes successfully (receives full flow outputs) */
|
|
198
|
+
onFlowComplete?: (outputs: TypedOutput[]) => void;
|
|
199
199
|
/** Called when upload fails */
|
|
200
200
|
onError?: (error: Error) => void;
|
|
201
201
|
/** Called when upload progress updates */
|
|
@@ -218,7 +218,7 @@ interface UseCameraUploadOptions {
|
|
|
218
218
|
/** Called when upload fails */
|
|
219
219
|
onError?: (error: Error) => void;
|
|
220
220
|
/** Called when upload progress updates */
|
|
221
|
-
onProgress?: (
|
|
221
|
+
onProgress?: (uploadId: string, bytesUploaded: number, totalBytes: number | null) => void;
|
|
222
222
|
}
|
|
223
223
|
/**
|
|
224
224
|
* Options for gallery upload hook
|
|
@@ -254,7 +254,7 @@ interface UseFileUploadOptions {
|
|
|
254
254
|
/** Called when upload fails */
|
|
255
255
|
onError?: (error: Error) => void;
|
|
256
256
|
/** Called when upload progress updates */
|
|
257
|
-
onProgress?: (
|
|
257
|
+
onProgress?: (uploadId: string, bytesUploaded: number, totalBytes: number | null) => void;
|
|
258
258
|
}
|
|
259
259
|
/**
|
|
260
260
|
* Metrics for upload performance
|
|
@@ -306,7 +306,7 @@ declare function CameraUploadButton({
|
|
|
306
306
|
onError,
|
|
307
307
|
onCancel,
|
|
308
308
|
showProgress
|
|
309
|
-
}: CameraUploadButtonProps):
|
|
309
|
+
}: CameraUploadButtonProps): react_jsx_runtime4.JSX.Element;
|
|
310
310
|
//#endregion
|
|
311
311
|
//#region src/components/FileUploadButton.d.ts
|
|
312
312
|
interface FileUploadButtonProps {
|
|
@@ -337,7 +337,7 @@ declare function FileUploadButton({
|
|
|
337
337
|
onError,
|
|
338
338
|
onCancel,
|
|
339
339
|
showProgress
|
|
340
|
-
}: FileUploadButtonProps):
|
|
340
|
+
}: FileUploadButtonProps): react_jsx_runtime4.JSX.Element;
|
|
341
341
|
//#endregion
|
|
342
342
|
//#region src/components/GalleryUploadButton.d.ts
|
|
343
343
|
interface GalleryUploadButtonProps {
|
|
@@ -368,7 +368,7 @@ declare function GalleryUploadButton({
|
|
|
368
368
|
onError,
|
|
369
369
|
onCancel,
|
|
370
370
|
showProgress
|
|
371
|
-
}: GalleryUploadButtonProps):
|
|
371
|
+
}: GalleryUploadButtonProps): react_jsx_runtime4.JSX.Element;
|
|
372
372
|
//#endregion
|
|
373
373
|
//#region src/components/UploadList.d.ts
|
|
374
374
|
interface UploadListProps {
|
|
@@ -390,7 +390,7 @@ declare function UploadList({
|
|
|
390
390
|
onRemove,
|
|
391
391
|
onItemPress,
|
|
392
392
|
showRemoveButton
|
|
393
|
-
}: UploadListProps):
|
|
393
|
+
}: UploadListProps): react_jsx_runtime4.JSX.Element;
|
|
394
394
|
//#endregion
|
|
395
395
|
//#region src/components/UploadProgress.d.ts
|
|
396
396
|
interface UploadProgressProps {
|
|
@@ -405,7 +405,7 @@ interface UploadProgressProps {
|
|
|
405
405
|
declare function UploadProgress({
|
|
406
406
|
state,
|
|
407
407
|
label
|
|
408
|
-
}: UploadProgressProps):
|
|
408
|
+
}: UploadProgressProps): react_jsx_runtime4.JSX.Element;
|
|
409
409
|
//#endregion
|
|
410
410
|
//#region src/contexts/flow-manager-context.d.ts
|
|
411
411
|
/**
|
|
@@ -421,7 +421,7 @@ interface FlowManagerContextValue {
|
|
|
421
421
|
* @param options - Flow configuration options
|
|
422
422
|
* @returns FlowManager instance
|
|
423
423
|
*/
|
|
424
|
-
getManager:
|
|
424
|
+
getManager: (flowId: string, callbacks: FlowManagerCallbacks, options: FlowUploadOptions) => FlowManager<unknown>;
|
|
425
425
|
/**
|
|
426
426
|
* Release a flow manager reference.
|
|
427
427
|
* Decrements ref count and cleans up when reaching zero.
|
|
@@ -455,7 +455,7 @@ interface FlowManagerProviderProps {
|
|
|
455
455
|
*/
|
|
456
456
|
declare function FlowManagerProvider({
|
|
457
457
|
children
|
|
458
|
-
}: FlowManagerProviderProps):
|
|
458
|
+
}: FlowManagerProviderProps): react_jsx_runtime4.JSX.Element;
|
|
459
459
|
/**
|
|
460
460
|
* Hook to access the FlowManager context.
|
|
461
461
|
* Must be used within a FlowManagerProvider.
|
|
@@ -522,7 +522,7 @@ declare function createUploadistaClient(options: UploadistaClientOptions$1, serv
|
|
|
522
522
|
onError
|
|
523
523
|
}?: Omit<_uploadista_client_core3.UploadistaUploadOptions, "uploadLengthDeferred" | "uploadSize" | "metadata">) => Promise<{
|
|
524
524
|
abort: () => Promise<void>;
|
|
525
|
-
pause: () => Promise<
|
|
525
|
+
pause: () => Promise<void>;
|
|
526
526
|
jobId: string;
|
|
527
527
|
}>;
|
|
528
528
|
abort: (params: Parameters<({
|
|
@@ -729,7 +729,7 @@ declare function useFileUpload(options?: UseFileUploadOptions): {
|
|
|
729
729
|
* ```
|
|
730
730
|
*/
|
|
731
731
|
declare function useFlowUpload(options: UseFlowUploadOptions): {
|
|
732
|
-
state: FlowUploadState
|
|
732
|
+
state: FlowUploadState;
|
|
733
733
|
upload: (file: FilePickResult) => Promise<void>;
|
|
734
734
|
abort: () => void;
|
|
735
735
|
reset: () => void;
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/types.ts","../src/types/upload-input.ts","../src/components/CameraUploadButton.tsx","../src/components/FileUploadButton.tsx","../src/components/GalleryUploadButton.tsx","../src/components/UploadList.tsx","../src/components/UploadProgress.tsx","../src/contexts/flow-manager-context.tsx","../src/client/create-uploadista-client.ts","../src/hooks/use-uploadista-client.ts","../src/hooks/uploadista-context.ts","../src/hooks/use-camera-upload.ts","../src/hooks/use-file-upload.ts","../src/hooks/use-flow-upload.ts","../src/hooks/use-multi-upload.ts","../src/hooks/use-gallery-upload.ts","../src/hooks/use-upload-metrics.ts","../src/hooks/use-uploadista-context.ts","../src/utils/fileHelpers.ts","../src/utils/permissions.ts","../src/utils/uriHelpers.ts"],"sourcesContent":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/types.ts","../src/types/upload-input.ts","../src/components/CameraUploadButton.tsx","../src/components/FileUploadButton.tsx","../src/components/GalleryUploadButton.tsx","../src/components/UploadList.tsx","../src/components/UploadProgress.tsx","../src/contexts/flow-manager-context.tsx","../src/client/create-uploadista-client.ts","../src/hooks/use-uploadista-client.ts","../src/hooks/uploadista-context.ts","../src/hooks/use-camera-upload.ts","../src/hooks/use-file-upload.ts","../src/hooks/use-flow-upload.ts","../src/hooks/use-multi-upload.ts","../src/hooks/use-gallery-upload.ts","../src/hooks/use-upload-metrics.ts","../src/hooks/use-uploadista-context.ts","../src/utils/fileHelpers.ts","../src/utils/permissions.ts","../src/utils/uriHelpers.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e,{createContext as t,useCallback as n,useContext as r,useEffect as i,useRef as a,useState as o}from"react";import{ActivityIndicator as s,FlatList as c,Pressable as l,StyleSheet as u,Text as d,View as f}from"react-native";import{FlowManager as p,UploadManager as m}from"@uploadista/client-core";import{EventType as h}from"@uploadista/core/flow";import{UploadEventType as g}from"@uploadista/core/types";import{jsx as _,jsxs as v}from"react/jsx-runtime";const y=t(void 0);function b(e,t){let n=e instanceof ArrayBuffer?new Uint8Array(e):e;return new Blob([n],t)}function x(){let e=r(y);if(!e)throw Error(`useUploadistaClient must be used within an UploadistaProvider`);return e}const ee={status:`idle`,progress:0,bytesUploaded:0,totalBytes:null,error:null,result:null};function S(e={}){let{client:t,fileSystemProvider:r}=x(),[s,c]=o(ee),l=a(null),u=a(null);return i(()=>(l.current=new m(async(e,n)=>{let i=e;if(i.status===`success`){let e=b(await r.readFile(i.data.uri),{type:i.data.mimeType||`application/octet-stream`});return t.upload(e,n)}return Promise.resolve({abort:()=>{}})},{onStateChange:c,onProgress:e.onProgress,onChunkComplete:e.onChunkComplete,onSuccess:e.onSuccess,onError:e.onError,onAbort:e.onAbort},{metadata:e.metadata,uploadLengthDeferred:e.uploadLengthDeferred,uploadSize:e.uploadSize,onShouldRetry:e.onShouldRetry}),()=>{l.current?.cleanup()}),[t,r,e]),{state:s,upload:n(async e=>{u.current=e,await l.current?.upload(e)},[]),abort:n(()=>{l.current?.abort()},[]),reset:n(()=>{l.current?.reset(),u.current=null},[]),retry:n(()=>{u.current&&l.current?.canRetry()&&l.current.retry()},[]),isUploading:s.status===`uploading`,canRetry:l.current?.canRetry()??!1,metrics:{getInsights:()=>t.getChunkingInsights(),exportMetrics:()=>t.exportMetrics(),getNetworkMetrics:()=>t.getNetworkMetrics(),getNetworkCondition:()=>t.getNetworkCondition(),resetMetrics:()=>t.resetMetrics()}}}function C(e){let{fileSystemProvider:t}=x(),r=S({metadata:e?.metadata,onSuccess:e?.onSuccess,onError:e?.onError,onProgress:e?.onProgress}),i=n(async()=>{try{let n=await t.pickCamera(e?.cameraOptions);await r.upload(n)}catch(e){console.error(`Camera capture error:`,e)}},[t,e?.cameraOptions,r]);return{...r,captureAndUpload:i}}function w(e){let{fileSystemProvider:t}=x(),r=S({metadata:e?.metadata,onSuccess:e?.onSuccess,onError:e?.onError,onProgress:e?.onProgress}),i=n(async()=>{try{let n=await t.pickDocument({allowedTypes:e?.allowedTypes});await r.upload(n)}catch(e){throw console.error(`File selection error:`,e),e}},[t,e?.allowedTypes,r]);return{...r,pickAndUpload:i}}function te(e){let t=e;return t.eventType===h.FlowStart||t.eventType===h.FlowEnd||t.eventType===h.FlowError||t.eventType===h.NodeStart||t.eventType===h.NodeEnd||t.eventType===h.NodePause||t.eventType===h.NodeResume||t.eventType===h.NodeError}const T=t(void 0);function E({children:e}){let{client:t,subscribeToEvents:r}=x(),o=a(new Map);i(()=>r(e=>{if(te(e)){for(let t of o.current.values())t.manager.handleFlowEvent(e);return}if(`type`in e&&e.type===g.UPLOAD_PROGRESS&&`data`in e){let t=e;for(let e of o.current.values())e.manager.handleUploadProgress(t.uploadId,t.data.progress,t.data.total)}}),[r]);let s=n((e,n,r)=>{let i=o.current.get(e);if(i)return i.refCount++,i.manager;let a=new p((e,n,r)=>t.uploadWithFlow(e,n,r),n,r);return o.current.set(e,{manager:a,refCount:1,flowId:e}),a},[t]),c=n(e=>{let t=o.current.get(e);t&&(t.refCount--,t.refCount<=0&&(t.manager.cleanup(),o.current.delete(e)))},[]);return _(T.Provider,{value:{getManager:s,releaseManager:c},children:e})}function D(){let e=r(T);if(e===void 0)throw Error(`useFlowManagerContext must be used within a FlowManagerProvider. Make sure to wrap your component tree with <FlowManagerProvider>.`);return e}const ne={status:`idle`,progress:0,bytesUploaded:0,totalBytes:null,error:null,result:null,jobId:null,flowStarted:!1,currentNodeName:null,currentNodeType:null,flowOutputs:null};function re(e){let{getManager:t,releaseManager:r}=D(),{fileSystemProvider:s}=x(),[c,l]=o(ne),u=a(null),d=a(null),f=a(e);i(()=>{f.current=e}),i(()=>{let n=e.flowId;return u.current=t(n,{onStateChange:l,onProgress:(e,t,n)=>{if(f.current.onProgress){let e=n?Math.round(t/n*100):0;f.current.onProgress(e,t,n)}},onChunkComplete:(e,t,n)=>{f.current.onChunkComplete?.(e,t,n)},onFlowComplete:e=>{f.current.onFlowComplete?.(e)},onSuccess:e=>{f.current.onSuccess?.(e)},onError:e=>{f.current.onError?.(e)},onAbort:()=>{f.current.onAbort?.()}},{flowConfig:{flowId:e.flowId,storageId:e.storageId,outputNodeId:e.outputNodeId,metadata:e.metadata},onChunkComplete:e.onChunkComplete,onSuccess:e.onSuccess,onError:e.onError}),()=>{r(n),u.current=null}},[e.flowId,e.storageId,e.outputNodeId,t,r]);let p=n(async t=>{if(t.status!==`cancelled`){if(t.status===`error`){e.onError?.(t.error);return}d.current=t;try{let e=b(await s.readFile(t.data.uri),{type:t.data.mimeType||`application/octet-stream`});await u.current?.upload(e)}catch(t){e.onError?.(t)}}},[s,e]),m=n(()=>{u.current?.reset(),d.current=null},[]);return{state:c,upload:p,abort:n(()=>{u.current?.abort()},[]),reset:m,retry:n(()=>{d.current&&(c.status===`error`||c.status===`aborted`)&&p(d.current)},[p,c.status]),isActive:c.status===`uploading`||c.status===`processing`,canRetry:(c.status===`error`||c.status===`aborted`)&&d.current!==null}}const O={items:[],totalProgress:0,totalUploaded:0,totalBytes:0,activeCount:0,completedCount:0,failedCount:0};function k(e={}){let{client:t}=x(),[r,i]=o(O),s=a(new Map),c=a(0),l=a([]),u=n(()=>`upload-${Date.now()}-${c.current++}`,[]),d=n(e=>{let t=e.reduce((e,t)=>e+t.totalBytes,0),n=e.reduce((e,t)=>e+t.bytesUploaded,0),r=t>0?Math.round(n/t*100):0,a=e.filter(e=>e.status===`uploading`).length,o=e.filter(e=>e.status===`success`).length,s=e.filter(e=>e.status===`error`).length;l.current=e,i(i=>({...i,items:e,totalProgress:r,totalUploaded:n,totalBytes:t,activeCount:a,completedCount:o,failedCount:s}))},[]),f=n(e=>{let t=e.filter(e=>e.status===`success`).map(e=>({id:u(),file:e,status:`idle`,progress:0,bytesUploaded:0,totalBytes:e.data.size,error:null,result:null})),n=[...l.current,...t];return l.current=n,i(e=>{let t=n.reduce((e,t)=>e+t.totalBytes,0);return{...e,items:n,totalBytes:t}}),t.map(e=>e.id)},[u]),p=n(async n=>{try{console.log(`Uploading item:`,n.file.data.name),d(l.current.map(e=>e.id===n.id?{...e,status:`uploading`}:e));let r=await(await fetch(n.file.data.uri)).blob(),i=n.file.data.mimeType?new Blob([r],{type:n.file.data.mimeType}):r;console.log(`Uploading input:`,i);let a=await t.upload(i,{metadata:e.metadata,onProgress:(e,t,r)=>{let i=r?Math.round(t/r*100):0;d(l.current.map(e=>e.id===n.id?{...e,progress:i,bytesUploaded:t,totalBytes:r||e.totalBytes}:e))},onSuccess:t=>{d(l.current.map(e=>e.id===n.id?{...e,status:`success`,progress:100,result:t,bytesUploaded:t.size||e.totalBytes}:e)),e.onSuccess?.(t),s.current.delete(n.id)},onError:t=>{d(l.current.map(e=>e.id===n.id?{...e,status:`error`,error:t}:e)),e.onError?.(t),s.current.delete(n.id)}});s.current.set(n.id,a)}catch(t){console.error(`Error uploading item:`,t),d(l.current.map(e=>e.id===n.id?{...e,status:`error`,error:t}:e)),e.onError?.(t),s.current.delete(n.id)}},[t,e,d]),m=n(async t=>{let n=e.maxConcurrent||3,r=t?l.current.filter(e=>t.includes(e.id)&&e.status===`idle`):l.current.filter(e=>e.status===`idle`);console.log(`Items to upload:`,r.length,r);for(let e=0;e<r.length;e+=n){let t=r.slice(e,e+n);await Promise.all(t.map(e=>p(e)))}},[e.maxConcurrent,p]),h=n(e=>{let t=s.current.get(e);t&&(t.abort(),s.current.delete(e)),d(l.current.filter(t=>t.id!==e))},[d]),g=n(e=>{let t=s.current.get(e);t&&(t.abort(),s.current.delete(e)),d(l.current.map(t=>t.id===e?{...t,status:`aborted`}:t))},[d]),_=n(()=>{s.current.forEach(e=>{e.abort()}),s.current.clear(),l.current=[],i(O)},[]);return{state:r,addFiles:f,startUploads:m,removeItem:h,abortItem:g,retryItem:n(async e=>{let t=l.current.find(t=>t.id===e);if(t&&(t.status===`error`||t.status===`aborted`)){d(l.current.map(t=>t.id===e?{...t,status:`idle`,progress:0,bytesUploaded:0,error:null}:t));let t=l.current.find(t=>t.id===e);t&&await p(t)}},[p,d]),clear:_}}function A(e){let{fileSystemProvider:t}=x(),r=k({maxConcurrent:3,metadata:e?.metadata,onSuccess:e?.onSuccess,onError:e?.onError}),i=n(async()=>{let n;if(n=e?.mediaType===`video`?await t.pickVideo({allowMultiple:e?.allowMultiple??!0}):(e?.mediaType,await t.pickImage({allowMultiple:e?.allowMultiple??!0})),n.status===`cancelled`)return[];if(n.status===`error`)return console.error(`Gallery selection error:`,n.error),e?.onError?.(n.error),[];let i=r.addFiles([n]);return await r.startUploads(i),i},[t,e?.allowMultiple,e?.mediaType,e?.onError,r]);return{...r,selectAndUpload:i}}function ie(){let e=a(null),t=a(0),r=a(0),[i,s]=o({totalBytes:0,durationMs:0,avgSpeed:0,peakSpeed:0,retries:0});return{metrics:i,start:n(()=>{e.current=Date.now(),t.current=0,r.current=0},[]),update:n((t,n,i=0)=>{if(!e.current)return;let a=Date.now()-e.current,o=a>0?t/a*1e3:0;o>r.current&&(r.current=o),s({totalBytes:t,durationMs:a,avgSpeed:a>0?t/a*1e3:0,peakSpeed:r.current,retries:i})},[]),end:n(()=>{let t=i;return e.current=null,t},[i]),reset:n(()=>{e.current=null,t.current=0,r.current=0,s({totalBytes:0,durationMs:0,avgSpeed:0,peakSpeed:0,retries:0})},[])}}function j(e){if(e===0)return`0 Bytes`;let t=1024,n=[`Bytes`,`KB`,`MB`,`GB`],r=Math.floor(Math.log(e)/Math.log(t));return`${Math.round(e/t**r*100)/100} ${n[r]}`}function M(e){return{".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".png":`image/png`,".gif":`image/gif`,".bmp":`image/bmp`,".webp":`image/webp`,".svg":`image/svg+xml`,".mp4":`video/mp4`,".avi":`video/x-msvideo`,".mov":`video/quicktime`,".wmv":`video/x-ms-wmv`,".flv":`video/x-flv`,".mkv":`video/x-matroska`,".webm":`video/webm`,".mp3":`audio/mpeg`,".wav":`audio/wav`,".aac":`audio/aac`,".flac":`audio/flac`,".m4a":`audio/mp4`,".pdf":`application/pdf`,".doc":`application/msword`,".docx":`application/vnd.openxmlformats-officedocument.wordprocessingml.document`,".xls":`application/vnd.ms-excel`,".xlsx":`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`,".ppt":`application/vnd.ms-powerpoint`,".pptx":`application/vnd.openxmlformats-officedocument.presentationml.presentation`,".txt":`text/plain`,".csv":`text/csv`,".json":`application/json`,".xml":`application/xml`,".zip":`application/zip`}[e.toLowerCase().slice(e.lastIndexOf(`.`))]||`application/octet-stream`}function N(e,t){if(!t||t.length===0)return!0;let n=M(e);return t.some(e=>{if(e.endsWith(`/*`)){let[t]=e.split(`/`);return n.startsWith(`${t}/`)}return e===n})}function P(e,t,n){return!(t!==void 0&&e>t||n!==void 0&&e<n)}function F(e){let t=e.lastIndexOf(`.`);return t===-1?``:e.slice(t+1).toLowerCase()}function ae(e){let t=e.lastIndexOf(`.`);return t===-1?e:e.slice(0,t)}function oe(e){let t=[`.jpg`,`.jpeg`,`.png`,`.gif`,`.bmp`,`.webp`,`.svg`],n=e.toLowerCase().slice(e.lastIndexOf(`.`));return t.includes(n)}function se(e){let t=[`.mp4`,`.avi`,`.mov`,`.wmv`,`.flv`,`.mkv`,`.webm`],n=e.toLowerCase().slice(e.lastIndexOf(`.`));return t.includes(n)}function ce(e){let t=[`.pdf`,`.doc`,`.docx`,`.xls`,`.xlsx`,`.ppt`,`.pptx`,`.txt`,`.csv`],n=e.toLowerCase().slice(e.lastIndexOf(`.`));return t.includes(n)}let I=function(e){return e.CAMERA=`CAMERA`,e.PHOTO_LIBRARY=`PHOTO_LIBRARY`,e.WRITE_STORAGE=`WRITE_STORAGE`,e.READ_STORAGE=`READ_STORAGE`,e}({}),L=function(e){return e.GRANTED=`granted`,e.DENIED=`denied`,e.NOT_DETERMINED=`not_determined`,e.RESTRICTED=`restricted`,e}({});async function R(){try{return console.log(`Camera permission requested (handled by file system provider)`),!0}catch(e){return console.error(`Failed to request camera permission:`,e),!1}}async function z(){try{return console.log(`Photo library permission requested (handled by file system provider)`),!0}catch(e){return console.error(`Failed to request photo library permission:`,e),!1}}async function B(){try{return console.log(`Storage read permission requested (handled by file system provider)`),!0}catch(e){return console.error(`Failed to request storage read permission:`,e),!1}}async function V(){try{return console.log(`Storage write permission requested (handled by file system provider)`),!0}catch(e){return console.error(`Failed to request storage write permission:`,e),!1}}async function H(e){try{return(await Promise.all(e.map(async e=>{switch(e){case I.CAMERA:return R();case I.PHOTO_LIBRARY:return z();case I.READ_STORAGE:return B();case I.WRITE_STORAGE:return V();default:return!1}}))).every(e=>e)}catch(e){return console.error(`Failed to request permissions:`,e),!1}}async function U(e){try{return!0}catch(e){return console.error(`Failed to check permissions:`,e),!1}}async function W(e){try{return L.GRANTED}catch(e){return console.error(`Failed to get permission status:`,e),L.DENIED}}function le(){try{console.log(`Opening app settings (requires react-native-app-settings or platform implementation)`)}catch(e){console.error(`Failed to open app settings:`,e)}}function G(e){try{if(e.startsWith(`file://`))return e.replace(`file://`,``).split(`/`).pop()||`file`;if(e.startsWith(`content://`)){let t=e.split(`/`);return t[t.length-1]||`file`}let t=e.split(`/`);return t[t.length-1]||`file`}catch{return`file`}}function ue(e){return e.startsWith(`file://`)||e.startsWith(`content://`)?e:`file://${e}`}function K(e){return e.startsWith(`file://`)?e.replace(`file://`,``):(e.startsWith(`content://`),e)}function de(e){try{let t=K(e).split(`/`);return t.pop(),t.join(`/`)}catch{return``}}function fe(e){return e.startsWith(`content://`)}function pe(e){return e.startsWith(`file://`)}function me(e){return e.replace(/([^:]\/)\/+/g,`$1`)}function he(e){let t=G(e);return{".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".png":`image/png`,".gif":`image/gif`,".bmp":`image/bmp`,".webp":`image/webp`,".mp4":`video/mp4`,".mov":`video/quicktime`,".avi":`video/x-msvideo`,".mp3":`audio/mpeg`,".wav":`audio/wav`,".aac":`audio/aac`,".pdf":`application/pdf`,".txt":`text/plain`,".json":`application/json`}[t.toLowerCase().slice(t.lastIndexOf(`.`))]||`application/octet-stream`}function q({state:e,label:t}){let n=()=>{switch(e.status){case`uploading`:return`#007AFF`;case`success`:return`#34C759`;case`error`:case`aborted`:return`#FF3B30`;default:return`#999999`}};return v(f,{style:[J.wrapper,{borderLeftColor:n()}],children:[e.status===`uploading`&&_(s,{size:`small`,color:n(),style:J.spinner}),(()=>{switch(e.status){case`idle`:return _(f,{style:J.container,children:_(d,{style:J.label,children:t||`Ready to upload`})});case`uploading`:return v(f,{style:J.container,children:[v(f,{style:J.headerRow,children:[_(d,{style:J.label,children:t||`Uploading`}),v(d,{style:J.percentage,children:[e.progress,`%`]})]}),_(f,{style:J.progressBarContainer,children:_(f,{style:[J.progressBar,{width:`${e.progress}%`,backgroundColor:n()}]})}),_(f,{style:J.detailsRow,children:v(d,{style:J.detail,children:[j(e.bytesUploaded),` /`,` `,j(e.totalBytes||0)]})})]});case`success`:return v(f,{style:J.container,children:[v(f,{style:J.headerRow,children:[_(d,{style:[J.label,{color:n()}],children:t||`Upload complete`}),_(d,{style:[J.percentage,{color:n()}],children:`✓`})]}),_(d,{style:[J.detail,{color:n()}],children:j(e.totalBytes||0)})]});case`error`:return v(f,{style:J.container,children:[v(f,{style:J.headerRow,children:[_(d,{style:[J.label,{color:n()}],children:t||`Upload failed`}),_(d,{style:[J.percentage,{color:n()}],children:`✕`})]}),e.error&&_(d,{style:[J.detail,{color:n()}],children:e.error.message})]});case`aborted`:return _(f,{style:J.container,children:_(d,{style:[J.label,{color:n()}],children:t||`Upload cancelled`})});default:return null}})()]})}const J=u.create({wrapper:{flexDirection:`row`,alignItems:`flex-start`,paddingVertical:8,paddingHorizontal:12,borderLeftWidth:4,backgroundColor:`#f5f5f5`,borderRadius:4,gap:8},spinner:{marginTop:4},container:{flex:1,gap:4},headerRow:{flexDirection:`row`,justifyContent:`space-between`,alignItems:`center`},label:{fontSize:14,fontWeight:`600`,color:`#333333`,flex:1},percentage:{fontSize:14,fontWeight:`600`,color:`#007AFF`,minWidth:36,textAlign:`right`},progressBarContainer:{height:4,backgroundColor:`#e0e0e0`,borderRadius:2,overflow:`hidden`},progressBar:{height:`100%`,borderRadius:2},detailsRow:{flexDirection:`row`,justifyContent:`space-between`,alignItems:`center`},detail:{fontSize:12,color:`#666666`}});function ge({options:e,label:t=`Take Photo`,children:n,onSuccess:r,onError:a,onCancel:o,showProgress:c=!0}){let{state:u,captureAndUpload:p}=C(e),m=async()=>{try{await p()}catch(e){e instanceof Error&&(e.message.includes(`cancelled`)||e.message.includes(`aborted`)?o?.():a?.(e))}},h=u.status===`uploading`,g=h||u.status===`aborted`;return i(()=>{u.status===`success`&&u.result&&r?.(u.result)},[u.status,u.result,r]),i(()=>{u.status===`error`&&u.error&&a?.(u.error)},[u.status,u.error,a]),v(f,{style:Y.container,children:[v(l,{style:[Y.button,g&&Y.buttonDisabled],onPress:m,disabled:g,children:[h&&_(s,{size:`small`,color:`#FFFFFF`,style:Y.spinner}),_(d,{style:Y.buttonText,children:n||t})]}),c&&u.status!==`idle`&&_(f,{style:Y.progressContainer,children:_(q,{state:u,label:`Camera upload`})})]})}const Y=u.create({container:{gap:8},button:{flexDirection:`row`,alignItems:`center`,justifyContent:`center`,paddingVertical:12,paddingHorizontal:16,backgroundColor:`#007AFF`,borderRadius:8,gap:8},buttonDisabled:{opacity:.6},buttonText:{fontSize:16,fontWeight:`600`,color:`#FFFFFF`},spinner:{marginRight:4},progressContainer:{marginTop:4}});function _e({options:e,label:t=`Choose File`,children:n,onSuccess:r,onError:a,onCancel:o,showProgress:c=!0}){let{state:u,pickAndUpload:p}=w(e),m=async()=>{try{await p()}catch(e){e instanceof Error&&(e.message.includes(`cancelled`)||e.message.includes(`aborted`)?o?.():a?.(e))}},h=u.status===`uploading`,g=h||u.status===`aborted`;return i(()=>{u.status===`success`&&u.result&&r?.(u.result)},[u.status,u.result,r]),i(()=>{u.status===`error`&&u.error&&a?.(u.error)},[u.status,u.error,a]),v(f,{style:X.container,children:[v(l,{style:[X.button,g&&X.buttonDisabled],onPress:m,disabled:g,children:[h&&_(s,{size:`small`,color:`#FFFFFF`,style:X.spinner}),_(d,{style:X.buttonText,children:n||t})]}),c&&u.status!==`idle`&&_(f,{style:X.progressContainer,children:_(q,{state:u,label:`File upload`})})]})}const X=u.create({container:{gap:8},button:{flexDirection:`row`,alignItems:`center`,justifyContent:`center`,paddingVertical:12,paddingHorizontal:16,backgroundColor:`#FF9500`,borderRadius:8,gap:8},buttonDisabled:{opacity:.6},buttonText:{fontSize:16,fontWeight:`600`,color:`#FFFFFF`},spinner:{marginRight:4},progressContainer:{marginTop:4}});function Z({options:t,label:n=`Select from Gallery`,children:r,onSuccess:i,onError:a,onCancel:o,showProgress:u=!0}){let{state:p,selectAndUpload:m}=A(t),h=async()=>{try{await m()}catch(e){e instanceof Error&&(e.message.includes(`cancelled`)||e.message.includes(`aborted`)?o?.():a?.(e))}},g=p.items.some(e=>e.status===`uploading`),y=p.items.length>0,b=y&&p.items.every(e=>e.status!==`uploading`&&e.status!==`idle`);return e.useEffect(()=>{if(b){let e=p.items.filter(e=>e.status===`success`).map(e=>e.result);e.length>0&&i?.(e)}},[b,p.items,i]),e.useEffect(()=>{let e=p.items.filter(e=>e.status===`error`)[0]?.error;e&&a?.(e)},[p.items,a]),v(f,{style:Q.container,children:[v(l,{style:[Q.button,g&&Q.buttonDisabled],onPress:h,disabled:g,children:[g&&_(s,{size:`small`,color:`#FFFFFF`,style:Q.spinner}),v(d,{style:Q.buttonText,children:[r||n,y&&` (${p.items.length})`]})]}),y&&v(f,{style:Q.statsContainer,children:[v(d,{style:Q.statsText,children:[`Progress: `,p.items.filter(e=>e.status===`success`).length,`/`,p.items.length,` uploaded`]}),v(d,{style:Q.statsText,children:[`Overall: `,p.totalProgress,`%`]})]}),u&&y&&_(c,{scrollEnabled:!1,data:p.items,renderItem:({item:e})=>_(f,{style:Q.itemContainer,children:_(q,{state:{status:e.status,progress:e.progress,bytesUploaded:e.bytesUploaded,totalBytes:e.totalBytes,error:e.error,result:e.result},label:e.file.data.name})},e.id),keyExtractor:e=>e.id,style:Q.listContainer,contentContainerStyle:Q.listContent,ItemSeparatorComponent:()=>_(f,{style:Q.separator})})]})}const Q=u.create({container:{gap:8},button:{flexDirection:`row`,alignItems:`center`,justifyContent:`center`,paddingVertical:12,paddingHorizontal:16,backgroundColor:`#34C759`,borderRadius:8,gap:8},buttonDisabled:{opacity:.6},buttonText:{fontSize:16,fontWeight:`600`,color:`#FFFFFF`},spinner:{marginRight:4},statsContainer:{paddingVertical:8,paddingHorizontal:12,backgroundColor:`#f5f5f5`,borderRadius:4,gap:4},statsText:{fontSize:12,color:`#666666`},listContainer:{maxHeight:400},listContent:{gap:8},itemContainer:{paddingHorizontal:0},separator:{height:4}});function ve({items:e,onRemove:t,onItemPress:n,showRemoveButton:r=!0}){return e.length===0?_(f,{style:$.emptyContainer,children:_(d,{style:$.emptyText,children:`No uploads`})}):v(f,{style:$.container,children:[v(f,{style:$.headerRow,children:[v(d,{style:$.headerText,children:[`Uploads (`,e.length,`)`]}),v(d,{style:$.headerSubtext,children:[e.filter(e=>e.progress.state===`success`).length,` complete`]})]}),_(c,{scrollEnabled:!1,data:e,renderItem:({item:e})=>v(l,{style:[$.itemContainer,{borderLeftColor:ye(e.progress.state)}],onPress:()=>n?.(e),children:[v(f,{style:$.itemContent,children:[e.file.status===`success`&&v(f,{style:$.itemHeader,children:[_(d,{style:$.fileName,numberOfLines:1,children:e.file.data.name}),_(d,{style:$.fileSize,children:be(e.file.data.size)})]}),e.file.status===`error`&&_(d,{style:$.errorText,children:e.progress.error?.message}),_(f,{style:$.progressWrapper,children:_(q,{state:{status:e.progress.state===`pending`?`idle`:e.progress.state===`cancelled`?`aborted`:e.progress.state,progress:e.progress.progress,bytesUploaded:e.progress.uploadedBytes,totalBytes:e.progress.totalBytes,error:e.progress.error||null,result:e.result??null}})})]}),r&&e.progress.state!==`uploading`&&e.progress.state!==`pending`&&_(l,{style:$.removeButton,onPress:()=>t?.(e.id),hitSlop:{top:8,right:8,bottom:8,left:8},children:_(d,{style:$.removeButtonText,children:`✕`})})]}),keyExtractor:e=>e.id,ItemSeparatorComponent:()=>_(f,{style:$.separator}),contentContainerStyle:$.listContent})]})}function ye(e){switch(e){case`success`:return`#34C759`;case`error`:case`cancelled`:return`#FF3B30`;case`uploading`:case`pending`:return`#007AFF`;default:return`#999999`}}function be(e){if(e===0)return`0 B`;let t=1024,n=[`B`,`KB`,`MB`,`GB`],r=Math.floor(Math.log(e)/Math.log(t));return`${Math.round(e/t**r*10)/10} ${n[r]}`}const $=u.create({container:{gap:8},headerRow:{flexDirection:`row`,justifyContent:`space-between`,alignItems:`center`,paddingHorizontal:12,paddingVertical:8,backgroundColor:`#f9f9f9`,borderRadius:4},headerText:{fontSize:16,fontWeight:`600`,color:`#333333`},errorText:{fontSize:14,color:`#FF3B30`},headerSubtext:{fontSize:14,color:`#666666`},listContent:{gap:8},itemContainer:{flexDirection:`row`,alignItems:`center`,paddingVertical:8,paddingHorizontal:12,borderLeftWidth:4,backgroundColor:`#f5f5f5`,borderRadius:4,gap:8},itemContent:{flex:1,gap:6},itemHeader:{flexDirection:`row`,justifyContent:`space-between`,alignItems:`center`},fileName:{fontSize:14,fontWeight:`500`,color:`#333333`,flex:1},fileSize:{fontSize:12,color:`#999999`,marginLeft:8},progressWrapper:{marginTop:2},removeButton:{width:32,height:32,justifyContent:`center`,alignItems:`center`,borderRadius:16,backgroundColor:`#FFE5E5`},removeButtonText:{fontSize:16,fontWeight:`600`,color:`#FF3B30`},separator:{height:4},emptyContainer:{paddingVertical:24,paddingHorizontal:12,backgroundColor:`#f5f5f5`,borderRadius:4,alignItems:`center`,justifyContent:`center`},emptyText:{fontSize:14,color:`#999999`,fontStyle:`italic`}});export{ge as CameraUploadButton,_e as FileUploadButton,E as FlowManagerProvider,Z as GalleryUploadButton,L as PermissionStatus,I as PermissionType,ve as UploadList,q as UploadProgress,y as UploadistaContext,j as formatFileSize,de as getDirectoryFromUri,F as getFileExtension,G as getFileNameFromUri,ae as getFileNameWithoutExtension,M as getMimeTypeFromFileName,he as getMimeTypeFromUri,W as getPermissionStatus,U as hasPermissions,fe as isContentUri,ce as isDocumentFile,P as isFileSizeValid,N as isFileTypeAllowed,pe as isFileUri,oe as isImageFile,se as isVideoFile,me as normalizeUri,le as openAppSettings,ue as pathToUri,R as requestCameraPermission,H as requestPermissions,z as requestPhotoLibraryPermission,B as requestStorageReadPermission,V as requestStorageWritePermission,K as uriToPath,C as useCameraUpload,w as useFileUpload,D as useFlowManagerContext,re as useFlowUpload,A as useGalleryUpload,k as useMultiUpload,ie as useUploadMetrics,x as useUploadistaContext};
|
|
1
|
+
import e,{createContext as t,useCallback as n,useContext as r,useEffect as i,useRef as a,useState as o}from"react";import{ActivityIndicator as s,FlatList as c,Pressable as l,StyleSheet as u,Text as d,View as f}from"react-native";import{FlowManager as p,UploadManager as m}from"@uploadista/client-core";import{EventType as h}from"@uploadista/core/flow";import{UploadEventType as g}from"@uploadista/core/types";import{jsx as _,jsxs as v}from"react/jsx-runtime";const y=t(void 0);function b(e,t){let n=e instanceof ArrayBuffer?new Uint8Array(e):e;return new Blob([n],t)}function x(){let e=r(y);if(!e)throw Error(`useUploadistaClient must be used within an UploadistaProvider`);return e}const ee={status:`idle`,progress:0,bytesUploaded:0,totalBytes:null,error:null,result:null};function S(e={}){let{client:t,fileSystemProvider:r}=x(),[s,c]=o(ee),l=a(null),u=a(null);return i(()=>(l.current=new m(async(e,n)=>{let i=e;if(i.status===`success`){let e=b(await r.readFile(i.data.uri),{type:i.data.mimeType||`application/octet-stream`});return t.upload(e,n)}return Promise.resolve({abort:()=>{}})},{onStateChange:c,onProgress:e.onProgress,onChunkComplete:e.onChunkComplete,onSuccess:e.onSuccess,onError:e.onError,onAbort:e.onAbort},{metadata:e.metadata,uploadLengthDeferred:e.uploadLengthDeferred,uploadSize:e.uploadSize,onShouldRetry:e.onShouldRetry}),()=>{l.current?.cleanup()}),[t,r,e]),{state:s,upload:n(async e=>{u.current=e,await l.current?.upload(e)},[]),abort:n(()=>{l.current?.abort()},[]),reset:n(()=>{l.current?.reset(),u.current=null},[]),retry:n(()=>{u.current&&l.current?.canRetry()&&l.current.retry()},[]),isUploading:s.status===`uploading`,canRetry:l.current?.canRetry()??!1,metrics:{getInsights:()=>t.getChunkingInsights(),exportMetrics:()=>t.exportMetrics(),getNetworkMetrics:()=>t.getNetworkMetrics(),getNetworkCondition:()=>t.getNetworkCondition(),resetMetrics:()=>t.resetMetrics()}}}function C(e){let{fileSystemProvider:t}=x(),r=S({metadata:e?.metadata,onSuccess:e?.onSuccess,onError:e?.onError,onProgress:e?.onProgress}),i=n(async()=>{try{let n=await t.pickCamera(e?.cameraOptions);await r.upload(n)}catch(e){console.error(`Camera capture error:`,e)}},[t,e?.cameraOptions,r]);return{...r,captureAndUpload:i}}function w(e){let{fileSystemProvider:t}=x(),r=S({metadata:e?.metadata,onSuccess:e?.onSuccess,onError:e?.onError,onProgress:e?.onProgress}),i=n(async()=>{try{let n=await t.pickDocument({allowedTypes:e?.allowedTypes});await r.upload(n)}catch(e){throw console.error(`File selection error:`,e),e}},[t,e?.allowedTypes,r]);return{...r,pickAndUpload:i}}function te(e){let t=e;return t.eventType===h.FlowStart||t.eventType===h.FlowEnd||t.eventType===h.FlowError||t.eventType===h.NodeStart||t.eventType===h.NodeEnd||t.eventType===h.NodePause||t.eventType===h.NodeResume||t.eventType===h.NodeError}const T=t(void 0);function E({children:e}){let{client:t,subscribeToEvents:r}=x(),o=a(new Map);i(()=>r(e=>{if(te(e)){for(let t of o.current.values())t.manager.handleFlowEvent(e);return}if(`type`in e&&e.type===g.UPLOAD_PROGRESS&&`data`in e)for(let t of o.current.values())t.manager.handleUploadProgress(e.data.id,e.data.progress,e.data.total)}),[r]);let s=n((e,n,r)=>{let i=o.current.get(e);if(i)return i.refCount++,i.manager;let a=new p(t.uploadWithFlow,n,r);return o.current.set(e,{manager:a,refCount:1,flowId:e}),a},[t]),c=n(e=>{let t=o.current.get(e);t&&(t.refCount--,t.refCount<=0&&(t.manager.cleanup(),o.current.delete(e)))},[]);return _(T.Provider,{value:{getManager:s,releaseManager:c},children:e})}function D(){let e=r(T);if(e===void 0)throw Error(`useFlowManagerContext must be used within a FlowManagerProvider. Make sure to wrap your component tree with <FlowManagerProvider>.`);return e}const ne={status:`idle`,progress:0,bytesUploaded:0,totalBytes:null,error:null,jobId:null,flowStarted:!1,currentNodeName:null,currentNodeType:null,flowOutputs:null};function re(e){let{getManager:t,releaseManager:r}=D(),{fileSystemProvider:s}=x(),[c,l]=o(ne),u=a(null),d=a(null),f=a(e);i(()=>{f.current=e}),i(()=>{let n=e.flowId;return u.current=t(n,{onStateChange:l,onProgress:(e,t,n)=>{if(f.current.onProgress){let e=n?Math.round(t/n*100):0;f.current.onProgress(e,t,n)}},onChunkComplete:(e,t,n)=>{f.current.onChunkComplete?.(e,t,n)},onFlowComplete:e=>{f.current.onFlowComplete?.(e)},onSuccess:e=>{f.current.onSuccess?.(e)},onError:e=>{f.current.onError?.(e)},onAbort:()=>{}},{flowConfig:{flowId:e.flowId,storageId:e.storageId,outputNodeId:e.outputNodeId,metadata:e.metadata},onChunkComplete:e.onChunkComplete,onSuccess:e.onSuccess,onError:e.onError}),()=>{r(n),u.current=null}},[e.flowId,e.storageId,e.outputNodeId,t,r]);let p=n(async t=>{if(t.status!==`cancelled`){if(t.status===`error`){e.onError?.(t.error);return}d.current=t;try{let e=b(await s.readFile(t.data.uri),{type:t.data.mimeType||`application/octet-stream`});await u.current?.upload(e)}catch(t){e.onError?.(t)}}},[s,e]),m=n(()=>{u.current?.reset(),d.current=null},[]);return{state:c,upload:p,abort:n(()=>{u.current?.abort()},[]),reset:m,retry:n(()=>{d.current&&(c.status===`error`||c.status===`aborted`)&&p(d.current)},[p,c.status]),isActive:c.status===`uploading`||c.status===`processing`,canRetry:(c.status===`error`||c.status===`aborted`)&&d.current!==null}}const O={items:[],totalProgress:0,totalUploaded:0,totalBytes:0,activeCount:0,completedCount:0,failedCount:0};function k(e={}){let{client:t}=x(),[r,i]=o(O),s=a(new Map),c=a(0),l=a([]),u=n(()=>`upload-${Date.now()}-${c.current++}`,[]),d=n(e=>{let t=e.reduce((e,t)=>e+t.totalBytes,0),n=e.reduce((e,t)=>e+t.bytesUploaded,0),r=t>0?Math.round(n/t*100):0,a=e.filter(e=>e.status===`uploading`).length,o=e.filter(e=>e.status===`success`).length,s=e.filter(e=>e.status===`error`).length;l.current=e,i(i=>({...i,items:e,totalProgress:r,totalUploaded:n,totalBytes:t,activeCount:a,completedCount:o,failedCount:s}))},[]),f=n(e=>{let t=e.filter(e=>e.status===`success`).map(e=>({id:u(),file:e,status:`idle`,progress:0,bytesUploaded:0,totalBytes:e.data.size,error:null,result:null})),n=[...l.current,...t];return l.current=n,i(e=>{let t=n.reduce((e,t)=>e+t.totalBytes,0);return{...e,items:n,totalBytes:t}}),t.map(e=>e.id)},[u]),p=n(async n=>{try{console.log(`Uploading item:`,n.file.data.name),d(l.current.map(e=>e.id===n.id?{...e,status:`uploading`}:e));let r=await(await fetch(n.file.data.uri)).blob(),i=n.file.data.mimeType?new Blob([r],{type:n.file.data.mimeType,lastModified:Date.now()}):r;console.log(`Uploading input:`,i);let a=await t.upload(i,{metadata:e.metadata,onProgress:(e,t,r)=>{let i=r?Math.round(t/r*100):0;d(l.current.map(e=>e.id===n.id?{...e,progress:i,bytesUploaded:t,totalBytes:r||e.totalBytes}:e))},onSuccess:t=>{d(l.current.map(e=>e.id===n.id?{...e,status:`success`,progress:100,result:t,bytesUploaded:t.size||e.totalBytes}:e)),e.onSuccess?.(t),s.current.delete(n.id)},onError:t=>{d(l.current.map(e=>e.id===n.id?{...e,status:`error`,error:t}:e)),e.onError?.(t),s.current.delete(n.id)}});s.current.set(n.id,a)}catch(t){console.error(`Error uploading item:`,t),d(l.current.map(e=>e.id===n.id?{...e,status:`error`,error:t}:e)),e.onError?.(t),s.current.delete(n.id)}},[t,e,d]),m=n(async t=>{let n=e.maxConcurrent||3,r=t?l.current.filter(e=>t.includes(e.id)&&e.status===`idle`):l.current.filter(e=>e.status===`idle`);console.log(`Items to upload:`,r.length,r);for(let e=0;e<r.length;e+=n){let t=r.slice(e,e+n);await Promise.all(t.map(e=>p(e)))}},[e.maxConcurrent,p]),h=n(e=>{let t=s.current.get(e);t&&(t.abort(),s.current.delete(e)),d(l.current.filter(t=>t.id!==e))},[d]),g=n(e=>{let t=s.current.get(e);t&&(t.abort(),s.current.delete(e)),d(l.current.map(t=>t.id===e?{...t,status:`aborted`}:t))},[d]),_=n(()=>{s.current.forEach(e=>{e.abort()}),s.current.clear(),l.current=[],i(O)},[]);return{state:r,addFiles:f,startUploads:m,removeItem:h,abortItem:g,retryItem:n(async e=>{let t=l.current.find(t=>t.id===e);if(t&&(t.status===`error`||t.status===`aborted`)){d(l.current.map(t=>t.id===e?{...t,status:`idle`,progress:0,bytesUploaded:0,error:null}:t));let t=l.current.find(t=>t.id===e);t&&await p(t)}},[p,d]),clear:_}}function A(e){let{fileSystemProvider:t}=x(),r=k({maxConcurrent:3,metadata:e?.metadata,onSuccess:e?.onSuccess,onError:e?.onError}),i=n(async()=>{let n;if(n=e?.mediaType===`video`?await t.pickVideo({allowMultiple:e?.allowMultiple??!0}):(e?.mediaType,await t.pickImage({allowMultiple:e?.allowMultiple??!0})),n.status===`cancelled`)return[];if(n.status===`error`)return console.error(`Gallery selection error:`,n.error),e?.onError?.(n.error),[];let i=r.addFiles([n]);return await r.startUploads(i),i},[t,e?.allowMultiple,e?.mediaType,e?.onError,r]);return{...r,selectAndUpload:i}}function ie(){let e=a(null),t=a(0),r=a(0),[i,s]=o({totalBytes:0,durationMs:0,avgSpeed:0,peakSpeed:0,retries:0});return{metrics:i,start:n(()=>{e.current=Date.now(),t.current=0,r.current=0},[]),update:n((t,n,i=0)=>{if(!e.current)return;let a=Date.now()-e.current,o=a>0?t/a*1e3:0;o>r.current&&(r.current=o),s({totalBytes:t,durationMs:a,avgSpeed:a>0?t/a*1e3:0,peakSpeed:r.current,retries:i})},[]),end:n(()=>{let t=i;return e.current=null,t},[i]),reset:n(()=>{e.current=null,t.current=0,r.current=0,s({totalBytes:0,durationMs:0,avgSpeed:0,peakSpeed:0,retries:0})},[])}}function j(e){if(e===0)return`0 Bytes`;let t=1024,n=[`Bytes`,`KB`,`MB`,`GB`],r=Math.floor(Math.log(e)/Math.log(t));return`${Math.round(e/t**r*100)/100} ${n[r]}`}function M(e){return{".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".png":`image/png`,".gif":`image/gif`,".bmp":`image/bmp`,".webp":`image/webp`,".svg":`image/svg+xml`,".mp4":`video/mp4`,".avi":`video/x-msvideo`,".mov":`video/quicktime`,".wmv":`video/x-ms-wmv`,".flv":`video/x-flv`,".mkv":`video/x-matroska`,".webm":`video/webm`,".mp3":`audio/mpeg`,".wav":`audio/wav`,".aac":`audio/aac`,".flac":`audio/flac`,".m4a":`audio/mp4`,".pdf":`application/pdf`,".doc":`application/msword`,".docx":`application/vnd.openxmlformats-officedocument.wordprocessingml.document`,".xls":`application/vnd.ms-excel`,".xlsx":`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`,".ppt":`application/vnd.ms-powerpoint`,".pptx":`application/vnd.openxmlformats-officedocument.presentationml.presentation`,".txt":`text/plain`,".csv":`text/csv`,".json":`application/json`,".xml":`application/xml`,".zip":`application/zip`}[e.toLowerCase().slice(e.lastIndexOf(`.`))]||`application/octet-stream`}function N(e,t){if(!t||t.length===0)return!0;let n=M(e);return t.some(e=>{if(e.endsWith(`/*`)){let[t]=e.split(`/`);return n.startsWith(`${t}/`)}return e===n})}function P(e,t,n){return!(t!==void 0&&e>t||n!==void 0&&e<n)}function F(e){let t=e.lastIndexOf(`.`);return t===-1?``:e.slice(t+1).toLowerCase()}function ae(e){let t=e.lastIndexOf(`.`);return t===-1?e:e.slice(0,t)}function oe(e){let t=[`.jpg`,`.jpeg`,`.png`,`.gif`,`.bmp`,`.webp`,`.svg`],n=e.toLowerCase().slice(e.lastIndexOf(`.`));return t.includes(n)}function se(e){let t=[`.mp4`,`.avi`,`.mov`,`.wmv`,`.flv`,`.mkv`,`.webm`],n=e.toLowerCase().slice(e.lastIndexOf(`.`));return t.includes(n)}function ce(e){let t=[`.pdf`,`.doc`,`.docx`,`.xls`,`.xlsx`,`.ppt`,`.pptx`,`.txt`,`.csv`],n=e.toLowerCase().slice(e.lastIndexOf(`.`));return t.includes(n)}let I=function(e){return e.CAMERA=`CAMERA`,e.PHOTO_LIBRARY=`PHOTO_LIBRARY`,e.WRITE_STORAGE=`WRITE_STORAGE`,e.READ_STORAGE=`READ_STORAGE`,e}({}),L=function(e){return e.GRANTED=`granted`,e.DENIED=`denied`,e.NOT_DETERMINED=`not_determined`,e.RESTRICTED=`restricted`,e}({});async function R(){try{return console.log(`Camera permission requested (handled by file system provider)`),!0}catch(e){return console.error(`Failed to request camera permission:`,e),!1}}async function z(){try{return console.log(`Photo library permission requested (handled by file system provider)`),!0}catch(e){return console.error(`Failed to request photo library permission:`,e),!1}}async function B(){try{return console.log(`Storage read permission requested (handled by file system provider)`),!0}catch(e){return console.error(`Failed to request storage read permission:`,e),!1}}async function V(){try{return console.log(`Storage write permission requested (handled by file system provider)`),!0}catch(e){return console.error(`Failed to request storage write permission:`,e),!1}}async function H(e){try{return(await Promise.all(e.map(async e=>{switch(e){case I.CAMERA:return R();case I.PHOTO_LIBRARY:return z();case I.READ_STORAGE:return B();case I.WRITE_STORAGE:return V();default:return!1}}))).every(e=>e)}catch(e){return console.error(`Failed to request permissions:`,e),!1}}async function U(e){try{return!0}catch(e){return console.error(`Failed to check permissions:`,e),!1}}async function W(e){try{return L.GRANTED}catch(e){return console.error(`Failed to get permission status:`,e),L.DENIED}}function le(){try{console.log(`Opening app settings (requires react-native-app-settings or platform implementation)`)}catch(e){console.error(`Failed to open app settings:`,e)}}function G(e){try{if(e.startsWith(`file://`))return e.replace(`file://`,``).split(`/`).pop()||`file`;if(e.startsWith(`content://`)){let t=e.split(`/`);return t[t.length-1]||`file`}let t=e.split(`/`);return t[t.length-1]||`file`}catch{return`file`}}function ue(e){return e.startsWith(`file://`)||e.startsWith(`content://`)?e:`file://${e}`}function K(e){return e.startsWith(`file://`)?e.replace(`file://`,``):(e.startsWith(`content://`),e)}function de(e){try{let t=K(e).split(`/`);return t.pop(),t.join(`/`)}catch{return``}}function fe(e){return e.startsWith(`content://`)}function pe(e){return e.startsWith(`file://`)}function me(e){return e.replace(/([^:]\/)\/+/g,`$1`)}function he(e){let t=G(e);return{".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".png":`image/png`,".gif":`image/gif`,".bmp":`image/bmp`,".webp":`image/webp`,".mp4":`video/mp4`,".mov":`video/quicktime`,".avi":`video/x-msvideo`,".mp3":`audio/mpeg`,".wav":`audio/wav`,".aac":`audio/aac`,".pdf":`application/pdf`,".txt":`text/plain`,".json":`application/json`}[t.toLowerCase().slice(t.lastIndexOf(`.`))]||`application/octet-stream`}function q({state:e,label:t}){let n=()=>{switch(e.status){case`uploading`:return`#007AFF`;case`success`:return`#34C759`;case`error`:case`aborted`:return`#FF3B30`;default:return`#999999`}};return v(f,{style:[J.wrapper,{borderLeftColor:n()}],children:[e.status===`uploading`&&_(s,{size:`small`,color:n(),style:J.spinner}),(()=>{switch(e.status){case`idle`:return _(f,{style:J.container,children:_(d,{style:J.label,children:t||`Ready to upload`})});case`uploading`:return v(f,{style:J.container,children:[v(f,{style:J.headerRow,children:[_(d,{style:J.label,children:t||`Uploading`}),v(d,{style:J.percentage,children:[e.progress,`%`]})]}),_(f,{style:J.progressBarContainer,children:_(f,{style:[J.progressBar,{width:`${e.progress}%`,backgroundColor:n()}]})}),_(f,{style:J.detailsRow,children:v(d,{style:J.detail,children:[j(e.bytesUploaded),` /`,` `,j(e.totalBytes||0)]})})]});case`success`:return v(f,{style:J.container,children:[v(f,{style:J.headerRow,children:[_(d,{style:[J.label,{color:n()}],children:t||`Upload complete`}),_(d,{style:[J.percentage,{color:n()}],children:`✓`})]}),_(d,{style:[J.detail,{color:n()}],children:j(e.totalBytes||0)})]});case`error`:return v(f,{style:J.container,children:[v(f,{style:J.headerRow,children:[_(d,{style:[J.label,{color:n()}],children:t||`Upload failed`}),_(d,{style:[J.percentage,{color:n()}],children:`✕`})]}),e.error&&_(d,{style:[J.detail,{color:n()}],children:e.error.message})]});case`aborted`:return _(f,{style:J.container,children:_(d,{style:[J.label,{color:n()}],children:t||`Upload cancelled`})});default:return null}})()]})}const J=u.create({wrapper:{flexDirection:`row`,alignItems:`flex-start`,paddingVertical:8,paddingHorizontal:12,borderLeftWidth:4,backgroundColor:`#f5f5f5`,borderRadius:4,gap:8},spinner:{marginTop:4},container:{flex:1,gap:4},headerRow:{flexDirection:`row`,justifyContent:`space-between`,alignItems:`center`},label:{fontSize:14,fontWeight:`600`,color:`#333333`,flex:1},percentage:{fontSize:14,fontWeight:`600`,color:`#007AFF`,minWidth:36,textAlign:`right`},progressBarContainer:{height:4,backgroundColor:`#e0e0e0`,borderRadius:2,overflow:`hidden`},progressBar:{height:`100%`,borderRadius:2},detailsRow:{flexDirection:`row`,justifyContent:`space-between`,alignItems:`center`},detail:{fontSize:12,color:`#666666`}});function ge({options:e,label:t=`Take Photo`,children:n,onSuccess:r,onError:a,onCancel:o,showProgress:c=!0}){let{state:u,captureAndUpload:p}=C(e),m=async()=>{try{await p()}catch(e){e instanceof Error&&(e.message.includes(`cancelled`)||e.message.includes(`aborted`)?o?.():a?.(e))}},h=u.status===`uploading`,g=h||u.status===`aborted`;return i(()=>{u.status===`success`&&u.result&&r?.(u.result)},[u.status,u.result,r]),i(()=>{u.status===`error`&&u.error&&a?.(u.error)},[u.status,u.error,a]),v(f,{style:Y.container,children:[v(l,{style:[Y.button,g&&Y.buttonDisabled],onPress:m,disabled:g,children:[h&&_(s,{size:`small`,color:`#FFFFFF`,style:Y.spinner}),_(d,{style:Y.buttonText,children:n||t})]}),c&&u.status!==`idle`&&_(f,{style:Y.progressContainer,children:_(q,{state:u,label:`Camera upload`})})]})}const Y=u.create({container:{gap:8},button:{flexDirection:`row`,alignItems:`center`,justifyContent:`center`,paddingVertical:12,paddingHorizontal:16,backgroundColor:`#007AFF`,borderRadius:8,gap:8},buttonDisabled:{opacity:.6},buttonText:{fontSize:16,fontWeight:`600`,color:`#FFFFFF`},spinner:{marginRight:4},progressContainer:{marginTop:4}});function _e({options:e,label:t=`Choose File`,children:n,onSuccess:r,onError:a,onCancel:o,showProgress:c=!0}){let{state:u,pickAndUpload:p}=w(e),m=async()=>{try{await p()}catch(e){e instanceof Error&&(e.message.includes(`cancelled`)||e.message.includes(`aborted`)?o?.():a?.(e))}},h=u.status===`uploading`,g=h||u.status===`aborted`;return i(()=>{u.status===`success`&&u.result&&r?.(u.result)},[u.status,u.result,r]),i(()=>{u.status===`error`&&u.error&&a?.(u.error)},[u.status,u.error,a]),v(f,{style:X.container,children:[v(l,{style:[X.button,g&&X.buttonDisabled],onPress:m,disabled:g,children:[h&&_(s,{size:`small`,color:`#FFFFFF`,style:X.spinner}),_(d,{style:X.buttonText,children:n||t})]}),c&&u.status!==`idle`&&_(f,{style:X.progressContainer,children:_(q,{state:u,label:`File upload`})})]})}const X=u.create({container:{gap:8},button:{flexDirection:`row`,alignItems:`center`,justifyContent:`center`,paddingVertical:12,paddingHorizontal:16,backgroundColor:`#FF9500`,borderRadius:8,gap:8},buttonDisabled:{opacity:.6},buttonText:{fontSize:16,fontWeight:`600`,color:`#FFFFFF`},spinner:{marginRight:4},progressContainer:{marginTop:4}});function Z({options:t,label:n=`Select from Gallery`,children:r,onSuccess:i,onError:a,onCancel:o,showProgress:u=!0}){let{state:p,selectAndUpload:m}=A(t),h=async()=>{try{await m()}catch(e){e instanceof Error&&(e.message.includes(`cancelled`)||e.message.includes(`aborted`)?o?.():a?.(e))}},g=p.items.some(e=>e.status===`uploading`),y=p.items.length>0,b=y&&p.items.every(e=>e.status!==`uploading`&&e.status!==`idle`);return e.useEffect(()=>{if(b){let e=p.items.filter(e=>e.status===`success`).map(e=>e.result);e.length>0&&i?.(e)}},[b,p.items,i]),e.useEffect(()=>{let e=p.items.filter(e=>e.status===`error`)[0]?.error;e&&a?.(e)},[p.items,a]),v(f,{style:Q.container,children:[v(l,{style:[Q.button,g&&Q.buttonDisabled],onPress:h,disabled:g,children:[g&&_(s,{size:`small`,color:`#FFFFFF`,style:Q.spinner}),v(d,{style:Q.buttonText,children:[r||n,y&&` (${p.items.length})`]})]}),y&&v(f,{style:Q.statsContainer,children:[v(d,{style:Q.statsText,children:[`Progress: `,p.items.filter(e=>e.status===`success`).length,`/`,p.items.length,` uploaded`]}),v(d,{style:Q.statsText,children:[`Overall: `,p.totalProgress,`%`]})]}),u&&y&&_(c,{scrollEnabled:!1,data:p.items,renderItem:({item:e})=>_(f,{style:Q.itemContainer,children:_(q,{state:{status:e.status,progress:e.progress,bytesUploaded:e.bytesUploaded,totalBytes:e.totalBytes,error:e.error,result:e.result},label:e.file.data.name})},e.id),keyExtractor:e=>e.id,style:Q.listContainer,contentContainerStyle:Q.listContent,ItemSeparatorComponent:()=>_(f,{style:Q.separator})})]})}const Q=u.create({container:{gap:8},button:{flexDirection:`row`,alignItems:`center`,justifyContent:`center`,paddingVertical:12,paddingHorizontal:16,backgroundColor:`#34C759`,borderRadius:8,gap:8},buttonDisabled:{opacity:.6},buttonText:{fontSize:16,fontWeight:`600`,color:`#FFFFFF`},spinner:{marginRight:4},statsContainer:{paddingVertical:8,paddingHorizontal:12,backgroundColor:`#f5f5f5`,borderRadius:4,gap:4},statsText:{fontSize:12,color:`#666666`},listContainer:{maxHeight:400},listContent:{gap:8},itemContainer:{paddingHorizontal:0},separator:{height:4}});function ve({items:e,onRemove:t,onItemPress:n,showRemoveButton:r=!0}){return e.length===0?_(f,{style:$.emptyContainer,children:_(d,{style:$.emptyText,children:`No uploads`})}):v(f,{style:$.container,children:[v(f,{style:$.headerRow,children:[v(d,{style:$.headerText,children:[`Uploads (`,e.length,`)`]}),v(d,{style:$.headerSubtext,children:[e.filter(e=>e.progress.state===`success`).length,` complete`]})]}),_(c,{scrollEnabled:!1,data:e,renderItem:({item:e})=>v(l,{style:[$.itemContainer,{borderLeftColor:ye(e.progress.state)}],onPress:()=>n?.(e),children:[v(f,{style:$.itemContent,children:[e.file.status===`success`&&v(f,{style:$.itemHeader,children:[_(d,{style:$.fileName,numberOfLines:1,children:e.file.data.name}),_(d,{style:$.fileSize,children:be(e.file.data.size)})]}),e.file.status===`error`&&_(d,{style:$.errorText,children:e.progress.error?.message}),_(f,{style:$.progressWrapper,children:_(q,{state:{status:e.progress.state===`pending`?`idle`:e.progress.state===`cancelled`?`aborted`:e.progress.state,progress:e.progress.progress,bytesUploaded:e.progress.uploadedBytes,totalBytes:e.progress.totalBytes,error:e.progress.error||null,result:e.result??null}})})]}),r&&e.progress.state!==`uploading`&&e.progress.state!==`pending`&&_(l,{style:$.removeButton,onPress:()=>t?.(e.id),hitSlop:{top:8,right:8,bottom:8,left:8},children:_(d,{style:$.removeButtonText,children:`✕`})})]}),keyExtractor:e=>e.id,ItemSeparatorComponent:()=>_(f,{style:$.separator}),contentContainerStyle:$.listContent})]})}function ye(e){switch(e){case`success`:return`#34C759`;case`error`:case`cancelled`:return`#FF3B30`;case`uploading`:case`pending`:return`#007AFF`;default:return`#999999`}}function be(e){if(e===0)return`0 B`;let t=1024,n=[`B`,`KB`,`MB`,`GB`],r=Math.floor(Math.log(e)/Math.log(t));return`${Math.round(e/t**r*10)/10} ${n[r]}`}const $=u.create({container:{gap:8},headerRow:{flexDirection:`row`,justifyContent:`space-between`,alignItems:`center`,paddingHorizontal:12,paddingVertical:8,backgroundColor:`#f9f9f9`,borderRadius:4},headerText:{fontSize:16,fontWeight:`600`,color:`#333333`},errorText:{fontSize:14,color:`#FF3B30`},headerSubtext:{fontSize:14,color:`#666666`},listContent:{gap:8},itemContainer:{flexDirection:`row`,alignItems:`center`,paddingVertical:8,paddingHorizontal:12,borderLeftWidth:4,backgroundColor:`#f5f5f5`,borderRadius:4,gap:8},itemContent:{flex:1,gap:6},itemHeader:{flexDirection:`row`,justifyContent:`space-between`,alignItems:`center`},fileName:{fontSize:14,fontWeight:`500`,color:`#333333`,flex:1},fileSize:{fontSize:12,color:`#999999`,marginLeft:8},progressWrapper:{marginTop:2},removeButton:{width:32,height:32,justifyContent:`center`,alignItems:`center`,borderRadius:16,backgroundColor:`#FFE5E5`},removeButtonText:{fontSize:16,fontWeight:`600`,color:`#FF3B30`},separator:{height:4},emptyContainer:{paddingVertical:24,paddingHorizontal:12,backgroundColor:`#f5f5f5`,borderRadius:4,alignItems:`center`,justifyContent:`center`},emptyText:{fontSize:14,color:`#999999`,fontStyle:`italic`}});export{ge as CameraUploadButton,_e as FileUploadButton,E as FlowManagerProvider,Z as GalleryUploadButton,L as PermissionStatus,I as PermissionType,ve as UploadList,q as UploadProgress,y as UploadistaContext,j as formatFileSize,de as getDirectoryFromUri,F as getFileExtension,G as getFileNameFromUri,ae as getFileNameWithoutExtension,M as getMimeTypeFromFileName,he as getMimeTypeFromUri,W as getPermissionStatus,U as hasPermissions,fe as isContentUri,ce as isDocumentFile,P as isFileSizeValid,N as isFileTypeAllowed,pe as isFileUri,oe as isImageFile,se as isVideoFile,me as normalizeUri,le as openAppSettings,ue as pathToUri,R as requestCameraPermission,H as requestPermissions,z as requestPhotoLibraryPermission,B as requestStorageReadPermission,V as requestStorageWritePermission,K as uriToPath,C as useCameraUpload,w as useFileUpload,D as useFlowManagerContext,re as useFlowUpload,A as useGalleryUpload,k as useMultiUpload,ie as useUploadMetrics,x as useUploadistaContext};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["initialState: UploadState","initialState","initialState: FlowUploadState","initialState","initialState: MultiUploadState","newItems: UploadItemState[]","result: FilePickResult","parts","getStatusColor","styles","styles","styles","styles"],"sources":["../src/hooks/uploadista-context.ts","../src/types/platform-types.ts","../src/hooks/use-uploadista-context.ts","../src/hooks/use-upload.ts","../src/hooks/use-camera-upload.ts","../src/hooks/use-file-upload.ts","../src/contexts/flow-manager-context.tsx","../src/hooks/use-flow-upload.ts","../src/hooks/use-multi-upload.ts","../src/hooks/use-gallery-upload.ts","../src/hooks/use-upload-metrics.ts","../src/utils/fileHelpers.ts","../src/utils/permissions.ts","../src/utils/uriHelpers.ts","../src/components/UploadProgress.tsx","../src/components/CameraUploadButton.tsx","../src/components/FileUploadButton.tsx","../src/components/GalleryUploadButton.tsx","../src/components/UploadList.tsx"],"sourcesContent":["import type { UploadistaEvent } from \"@uploadista/client-core\";\nimport { createContext } from \"react\";\nimport type { FileSystemProvider } from \"../types\";\nimport type { UseUploadistaClientReturn } from \"./use-uploadista-client\";\n\nexport interface UploadistaContextType extends UseUploadistaClientReturn {\n fileSystemProvider: FileSystemProvider;\n /**\n * Subscribe to events (used internally by hooks)\n * @internal\n */\n subscribeToEvents: (handler: (event: UploadistaEvent) => void) => () => void;\n}\n\nexport const UploadistaContext = createContext<\n UploadistaContextType | undefined\n>(undefined);\n","/**\n * Platform-specific type definitions for React Native\n *\n * React Native's Blob implementation differs from the browser's Blob API.\n * This file provides proper type definitions and guards for platform-specific behavior.\n */\n\n/**\n * BufferSource represents data that can be passed to Blob constructor\n * Includes both ArrayBuffer and typed arrays (Uint8Array, etc.)\n */\nexport type BufferSource = ArrayBuffer | ArrayBufferView;\n\n/**\n * React Native Blob constructor options\n * Extends standard BlobPropertyBag with platform-specific properties\n */\nexport interface ReactNativeBlobOptions {\n /** MIME type of the blob */\n type?: string;\n /** Platform-specific: file path for optimization (React Native only) */\n path?: string;\n}\n\n/**\n * React Native Blob constructor type\n * Unlike browser Blob, accepts BufferSource in the parts array\n */\nexport interface ReactNativeBlobConstructor {\n new (\n parts?: Array<BufferSource | Blob | string>,\n options?: ReactNativeBlobOptions,\n ): Blob;\n prototype: Blob;\n}\n\n/**\n * Type guard to check if a value is ArrayBuffer\n */\nexport function isArrayBuffer(value: unknown): value is ArrayBuffer {\n return value instanceof ArrayBuffer;\n}\n\n/**\n * Type guard to check if a value is ArrayBufferView (typed array)\n */\nexport function isArrayBufferView(value: unknown): value is ArrayBufferView {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"buffer\" in value &&\n value.buffer instanceof ArrayBuffer\n );\n}\n\n/**\n * Type guard to check if a value is BufferSource\n */\nexport function isBufferSource(value: unknown): value is BufferSource {\n return isArrayBuffer(value) || isArrayBufferView(value);\n}\n\n/**\n * Type guard to check if we're in React Native environment\n * (checks for global.navigator.product === 'ReactNative')\n */\nexport function isReactNativeEnvironment(): boolean {\n return (\n typeof global !== \"undefined\" &&\n typeof global.navigator !== \"undefined\" &&\n global.navigator.product === \"ReactNative\"\n );\n}\n\n/**\n * Create a Blob from BufferSource with proper typing for React Native\n *\n * This function handles the platform differences between browser and React Native Blob APIs.\n * React Native's Blob constructor accepts BufferSource directly, while browser Blob requires\n * conversion to Uint8Array first in some cases.\n *\n * @param data - ArrayBuffer or typed array to convert to Blob\n * @param options - Blob options including MIME type\n * @returns Platform-appropriate Blob instance\n *\n * @example\n * ```typescript\n * const arrayBuffer = await fileSystemProvider.readFile(uri);\n * const blob = createBlobFromBuffer(arrayBuffer, {\n * type: 'image/jpeg'\n * });\n * ```\n */\nexport function createBlobFromBuffer(\n data: BufferSource,\n options?: ReactNativeBlobOptions,\n): Blob {\n // Convert ArrayBuffer to Uint8Array for consistent handling\n const uint8Array = data instanceof ArrayBuffer ? new Uint8Array(data) : data;\n\n // In React Native, Blob constructor accepts BufferSource\n // Cast to ReactNativeBlobConstructor to use the correct signature\n const BlobConstructor = Blob as unknown as ReactNativeBlobConstructor;\n return new BlobConstructor([uint8Array], options);\n}\n","import { useContext } from \"react\";\nimport { UploadistaContext } from \"./uploadista-context\";\n\n/**\n * Hook to access the Uploadista client instance\n * Must be used within an UploadistaProvider\n * @throws Error if used outside of UploadistaProvider\n * @returns The Uploadista client and file system provider\n */\nexport function useUploadistaContext() {\n const context = useContext(UploadistaContext);\n\n if (!context) {\n throw new Error(\n \"useUploadistaClient must be used within an UploadistaProvider\",\n );\n }\n\n return context;\n}\n","import type {\n UploadistaUploadOptions,\n UploadMetrics,\n} from \"@uploadista/client-core\";\nimport {\n UploadManager,\n type UploadState,\n type UploadStatus,\n} from \"@uploadista/client-core\";\nimport type { UploadFile } from \"@uploadista/core/types\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { FilePickResult } from \"../types\";\nimport { createBlobFromBuffer } from \"../types/platform-types\";\nimport { useUploadistaContext } from \"./use-uploadista-context\";\n\n// Re-export types from core for convenience\nexport type { UploadState, UploadStatus };\n\nexport interface UseUploadOptions {\n /**\n * Upload metadata to attach to the file\n */\n metadata?: Record<string, string>;\n\n /**\n * Whether to defer the upload size calculation\n */\n uploadLengthDeferred?: boolean;\n\n /**\n * Manual upload size override\n */\n uploadSize?: number;\n\n /**\n * Called when upload progress updates\n */\n onProgress?: (\n uploadId: string,\n bytesUploaded: number,\n totalBytes: number | null,\n ) => void;\n\n /**\n * Called when a chunk completes\n */\n onChunkComplete?: (\n chunkSize: number,\n bytesAccepted: number,\n bytesTotal: number | null,\n ) => void;\n\n /**\n * Called when upload succeeds\n */\n onSuccess?: (result: UploadFile) => void;\n\n /**\n * Called when upload fails\n */\n onError?: (error: Error) => void;\n\n /**\n * Called when upload is aborted\n */\n onAbort?: () => void;\n\n /**\n * Custom retry logic\n */\n onShouldRetry?: (error: Error, retryAttempt: number) => boolean;\n}\n\nexport interface UseUploadReturn {\n /**\n * Current upload state\n */\n state: UploadState;\n\n /**\n * Start uploading a file from a file pick result\n */\n upload: (file: FilePickResult) => Promise<void>;\n\n /**\n * Abort the current upload\n */\n abort: () => void;\n\n /**\n * Reset the upload state to idle\n */\n reset: () => void;\n\n /**\n * Retry the last failed upload\n */\n retry: () => void;\n\n /**\n * Whether an upload is currently active\n */\n isUploading: boolean;\n\n /**\n * Whether the upload can be retried\n */\n canRetry: boolean;\n\n /**\n * Upload metrics and performance insights from the client\n */\n metrics: UploadMetrics;\n}\n\nconst initialState: UploadState = {\n status: \"idle\",\n progress: 0,\n bytesUploaded: 0,\n totalBytes: null,\n error: null,\n result: null,\n};\n\n/**\n * React Native hook for managing individual file uploads with full state management.\n * Provides upload progress tracking, error handling, abort functionality, and retry logic.\n *\n * Must be used within an UploadistaProvider.\n *\n * @param options - Upload configuration and event handlers\n * @returns Upload state and control methods\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const upload = useUpload({\n * onSuccess: (result) => console.log('Upload complete:', result),\n * onError: (error) => console.error('Upload failed:', error),\n * onProgress: (progress) => console.log('Progress:', progress + '%'),\n * });\n *\n * const handleFilePick = async () => {\n * const file = await pickFile();\n * if (file) await upload.upload(file);\n * };\n *\n * return (\n * <View>\n * <Button title=\"Pick File\" onPress={handleFilePick} />\n * {upload.isUploading && <Text>Progress: {upload.state.progress}%</Text>}\n * {upload.state.error && <Text>Error: {upload.state.error.message}</Text>}\n * {upload.canRetry && <Button title=\"Retry\" onPress={upload.retry} />}\n * <Button title=\"Abort\" onPress={upload.abort} disabled={!upload.isUploading} />\n * </View>\n * );\n * }\n * ```\n */\nexport function useUpload(options: UseUploadOptions = {}): UseUploadReturn {\n const { client, fileSystemProvider } = useUploadistaContext();\n const [state, setState] = useState<UploadState>(initialState);\n const managerRef = useRef<UploadManager | null>(null);\n const lastFileRef = useRef<FilePickResult | null>(null);\n\n // Create UploadManager instance\n useEffect(() => {\n // Create upload function that handles React Native file reading\n const uploadFn = async (input: unknown, opts: UploadistaUploadOptions) => {\n const file = input as FilePickResult;\n\n if (file.status === \"success\") {\n // Read file content from React Native file system\n const fileContent = await fileSystemProvider.readFile(file.data.uri);\n\n // Create a Blob from the file content using platform-aware utility\n const blob = createBlobFromBuffer(fileContent, {\n type: file.data.mimeType || \"application/octet-stream\",\n });\n\n // Upload the Blob\n return client.upload(blob, opts);\n }\n\n return Promise.resolve({ abort: () => {} });\n };\n\n managerRef.current = new UploadManager(\n uploadFn,\n {\n onStateChange: setState,\n onProgress: options.onProgress,\n onChunkComplete: options.onChunkComplete,\n onSuccess: options.onSuccess,\n onError: options.onError,\n onAbort: options.onAbort,\n },\n {\n metadata: options.metadata,\n uploadLengthDeferred: options.uploadLengthDeferred,\n uploadSize: options.uploadSize,\n onShouldRetry: options.onShouldRetry,\n },\n );\n\n return () => {\n managerRef.current?.cleanup();\n };\n }, [client, fileSystemProvider, options]);\n\n // Upload function - stores file reference for retry\n const upload = useCallback(async (file: FilePickResult) => {\n lastFileRef.current = file;\n await managerRef.current?.upload(file);\n }, []);\n\n // Abort function\n const abort = useCallback(() => {\n managerRef.current?.abort();\n }, []);\n\n // Reset function\n const reset = useCallback(() => {\n managerRef.current?.reset();\n lastFileRef.current = null;\n }, []);\n\n // Retry function\n const retry = useCallback(() => {\n if (lastFileRef.current && managerRef.current?.canRetry()) {\n managerRef.current.retry();\n }\n }, []);\n\n // Derive computed values from state\n const isUploading = state.status === \"uploading\";\n const canRetry = managerRef.current?.canRetry() ?? false;\n\n // Create metrics object that delegates to the upload client\n const metrics: UploadMetrics = {\n getInsights: () => client.getChunkingInsights(),\n exportMetrics: () => client.exportMetrics(),\n getNetworkMetrics: () => client.getNetworkMetrics(),\n getNetworkCondition: () => client.getNetworkCondition(),\n resetMetrics: () => client.resetMetrics(),\n };\n\n return {\n state,\n upload,\n abort,\n reset,\n retry,\n isUploading,\n canRetry,\n metrics,\n };\n}\n","import { useCallback } from \"react\";\nimport type { UseCameraUploadOptions } from \"../types\";\nimport { useUpload } from \"./use-upload\";\nimport { useUploadistaContext } from \"./use-uploadista-context\";\n\n/**\n * Hook for capturing photos and uploading them\n * Handles camera permissions and capture flow\n * @param options - Camera upload configuration\n * @returns Upload state and camera capture/upload function\n */\nexport function useCameraUpload(options?: UseCameraUploadOptions) {\n const { fileSystemProvider } = useUploadistaContext();\n const uploadHook = useUpload({\n metadata: options?.metadata,\n onSuccess: options?.onSuccess,\n onError: options?.onError,\n onProgress: options?.onProgress,\n });\n\n // Capture and upload photo\n const captureAndUpload = useCallback(async () => {\n try {\n // Capture photo with camera\n const photo = await fileSystemProvider.pickCamera(options?.cameraOptions);\n\n // Upload captured photo\n await uploadHook.upload(photo);\n } catch (error) {\n console.error(\"Camera capture error:\", error);\n }\n }, [fileSystemProvider, options?.cameraOptions, uploadHook]);\n\n return {\n ...uploadHook,\n captureAndUpload,\n };\n}\n","import { useCallback } from \"react\";\nimport type { UseFileUploadOptions } from \"../types\";\nimport { useUpload } from \"./use-upload\";\nimport { useUploadistaContext } from \"./use-uploadista-context\";\n\n/**\n * Hook for selecting and uploading generic files (documents, etc.)\n * @param options - File upload configuration\n * @returns Upload state and file picker/upload function\n */\nexport function useFileUpload(options?: UseFileUploadOptions) {\n const { fileSystemProvider } = useUploadistaContext();\n const uploadHook = useUpload({\n metadata: options?.metadata,\n onSuccess: options?.onSuccess,\n onError: options?.onError,\n onProgress: options?.onProgress,\n });\n\n // Pick and upload file\n const pickAndUpload = useCallback(async () => {\n try {\n // Pick file\n const file = await fileSystemProvider.pickDocument({\n allowedTypes: options?.allowedTypes,\n });\n\n // Upload file\n await uploadHook.upload(file);\n } catch (error) {\n console.error(\"File selection error:\", error);\n throw error;\n }\n }, [fileSystemProvider, options?.allowedTypes, uploadHook]);\n\n return {\n ...uploadHook,\n pickAndUpload,\n };\n}\n","import type { UploadistaEvent } from \"@uploadista/client-core\";\nimport {\n FlowManager,\n type FlowManagerCallbacks,\n type FlowUploadOptions,\n type FlowUploadState,\n} from \"@uploadista/client-core\";\nimport { EventType, type FlowEvent } from \"@uploadista/core/flow\";\nimport { UploadEventType } from \"@uploadista/core/types\";\nimport type { ReactNode } from \"react\";\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n} from \"react\";\nimport { useUploadistaContext } from \"../hooks/use-uploadista-context\";\n\n/**\n * Type guard to check if an event is a flow event\n */\nfunction isFlowEvent(event: UploadistaEvent): event is FlowEvent {\n const flowEvent = event as FlowEvent;\n return (\n flowEvent.eventType === EventType.FlowStart ||\n flowEvent.eventType === EventType.FlowEnd ||\n flowEvent.eventType === EventType.FlowError ||\n flowEvent.eventType === EventType.NodeStart ||\n flowEvent.eventType === EventType.NodeEnd ||\n flowEvent.eventType === EventType.NodePause ||\n flowEvent.eventType === EventType.NodeResume ||\n flowEvent.eventType === EventType.NodeError\n );\n}\n\n/**\n * Internal manager registry entry with ref counting\n */\ninterface ManagerEntry<TOutput> {\n manager: FlowManager<unknown, TOutput>;\n refCount: number;\n flowId: string;\n}\n\n/**\n * Context value providing access to flow managers\n */\ninterface FlowManagerContextValue {\n /**\n * Get or create a flow manager for the given flow ID.\n * Increments ref count - must call releaseManager when done.\n *\n * @param flowId - Unique identifier for the flow\n * @param callbacks - Callbacks for state changes and lifecycle events\n * @param options - Flow configuration options\n * @returns FlowManager instance\n */\n getManager: <TOutput = unknown>(\n flowId: string,\n callbacks: FlowManagerCallbacks<TOutput>,\n options: FlowUploadOptions<TOutput>,\n ) => FlowManager<unknown, TOutput>;\n\n /**\n * Release a flow manager reference.\n * Decrements ref count and cleans up when reaching zero.\n *\n * @param flowId - Unique identifier for the flow to release\n */\n releaseManager: (flowId: string) => void;\n}\n\nconst FlowManagerContext = createContext<FlowManagerContextValue | undefined>(\n undefined,\n);\n\n/**\n * Props for FlowManagerProvider\n */\ninterface FlowManagerProviderProps {\n children: ReactNode;\n}\n\n/**\n * Provider that manages FlowManager instances with ref counting and event routing.\n * Ensures managers persist across component re-renders and are only cleaned up\n * when all consuming components unmount.\n *\n * This provider should be nested inside UploadistaProvider to access the upload client\n * and event subscription system.\n *\n * @example\n * ```tsx\n * <UploadistaProvider baseUrl=\"https://api.example.com\" storageId=\"default\">\n * <FlowManagerProvider>\n * <App />\n * </FlowManagerProvider>\n * </UploadistaProvider>\n * ```\n */\nexport function FlowManagerProvider({ children }: FlowManagerProviderProps) {\n const { client, subscribeToEvents } = useUploadistaContext();\n const managersRef = useRef(\n new Map<string, ManagerEntry<unknown>>(),\n );\n\n // Subscribe to all events and route to appropriate managers\n useEffect(() => {\n const unsubscribe = subscribeToEvents((event: UploadistaEvent) => {\n // Route flow events to all managers (they filter by jobId internally)\n if (isFlowEvent(event)) {\n for (const entry of managersRef.current.values()) {\n entry.manager.handleFlowEvent(event);\n }\n return;\n }\n\n // Route upload progress events to all managers\n if (\n \"type\" in event &&\n event.type === UploadEventType.UPLOAD_PROGRESS &&\n \"data\" in event\n ) {\n const uploadEvent = event as {\n type: UploadEventType;\n uploadId: string;\n data: { progress: number; total: number | null };\n };\n\n for (const entry of managersRef.current.values()) {\n entry.manager.handleUploadProgress(\n uploadEvent.uploadId,\n uploadEvent.data.progress,\n uploadEvent.data.total,\n );\n }\n }\n });\n\n return unsubscribe;\n }, [subscribeToEvents]);\n\n const getManager = useCallback(\n <TOutput,>(\n flowId: string,\n callbacks: FlowManagerCallbacks<TOutput>,\n options: FlowUploadOptions<TOutput>,\n ): FlowManager<unknown, TOutput> => {\n const existing = managersRef.current.get(flowId);\n\n if (existing) {\n // Increment ref count for existing manager\n existing.refCount++;\n return existing.manager as FlowManager<unknown, TOutput>;\n }\n\n // Create new manager using client from hook scope\n const flowUploadFn = (\n input: unknown,\n flowConfig: FlowUploadOptions<TOutput>[\"flowConfig\"],\n internalOptions: unknown,\n ) => {\n return client.uploadWithFlow(input, flowConfig, internalOptions);\n };\n\n const manager = new FlowManager<unknown, TOutput>(\n flowUploadFn,\n callbacks,\n options,\n );\n\n managersRef.current.set(flowId, {\n manager: manager as FlowManager<unknown, unknown>,\n refCount: 1,\n flowId,\n });\n\n return manager;\n },\n [client],\n );\n\n const releaseManager = useCallback((flowId: string) => {\n const existing = managersRef.current.get(flowId);\n if (!existing) return;\n\n existing.refCount--;\n\n // Clean up when no more refs\n if (existing.refCount <= 0) {\n existing.manager.cleanup();\n managersRef.current.delete(flowId);\n }\n }, []);\n\n return (\n <FlowManagerContext.Provider value={{ getManager, releaseManager }}>\n {children}\n </FlowManagerContext.Provider>\n );\n}\n\n/**\n * Hook to access the FlowManager context.\n * Must be used within a FlowManagerProvider.\n *\n * @returns FlowManager context value with getManager and releaseManager functions\n * @throws Error if used outside of FlowManagerProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { getManager, releaseManager } = useFlowManagerContext();\n * // Use to create managers...\n * }\n * ```\n */\nexport function useFlowManagerContext(): FlowManagerContextValue {\n const context = useContext(FlowManagerContext);\n\n if (context === undefined) {\n throw new Error(\n \"useFlowManagerContext must be used within a FlowManagerProvider. \" +\n \"Make sure to wrap your component tree with <FlowManagerProvider>.\",\n );\n }\n\n return context;\n}\n","import {\n type FlowManager,\n type FlowUploadState,\n type FlowUploadStatus,\n} from \"@uploadista/client-core\";\nimport type { TypedOutput } from \"@uploadista/core/flow\";\nimport type { UploadFile } from \"@uploadista/core/types\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useFlowManagerContext } from \"../contexts/flow-manager-context\";\nimport type { FilePickResult, UseFlowUploadOptions } from \"../types\";\nimport { createBlobFromBuffer } from \"../types/platform-types\";\nimport { useUploadistaContext } from \"./use-uploadista-context\";\n\n// Re-export types from core for convenience\nexport type { FlowUploadState, FlowUploadStatus };\n\nconst initialState: FlowUploadState = {\n status: \"idle\",\n progress: 0,\n bytesUploaded: 0,\n totalBytes: null,\n error: null,\n result: null,\n jobId: null,\n flowStarted: false,\n currentNodeName: null,\n currentNodeType: null,\n flowOutputs: null,\n};\n\n/**\n * Hook for uploading files through a flow pipeline with full state management.\n * Provides upload progress tracking, flow execution monitoring, error handling, and abort functionality.\n *\n * Must be used within FlowManagerProvider (which must be within UploadistaProvider).\n * Flow events are automatically routed by the provider to the appropriate manager.\n *\n * @param options - Flow upload configuration\n * @returns Flow upload state and control methods\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const flowUpload = useFlowUpload({\n * flowId: 'image-processing-flow',\n * storageId: 'my-storage',\n * onSuccess: (result) => console.log('Flow complete:', result),\n * onError: (error) => console.error('Flow failed:', error),\n * onProgress: (progress) => console.log('Progress:', progress + '%'),\n * });\n *\n * const handlePickFile = async () => {\n * const file = await fileSystemProvider.pickDocument();\n * if (file) {\n * await flowUpload.upload(file);\n * }\n * };\n *\n * return (\n * <View>\n * <Button title=\"Pick File\" onPress={handlePickFile} />\n * {flowUpload.isUploading && <Text>Progress: {flowUpload.state.progress}%</Text>}\n * {flowUpload.state.jobId && <Text>Job ID: {flowUpload.state.jobId}</Text>}\n * {flowUpload.state.error && <Text>Error: {flowUpload.state.error.message}</Text>}\n * <Button title=\"Abort\" onPress={flowUpload.abort} disabled={!flowUpload.isActive} />\n * </View>\n * );\n * }\n * ```\n */\nexport function useFlowUpload(options: UseFlowUploadOptions) {\n const { getManager, releaseManager } = useFlowManagerContext();\n const { fileSystemProvider } = useUploadistaContext();\n const [state, setState] = useState<FlowUploadState>(initialState);\n const managerRef = useRef<FlowManager<unknown, UploadFile> | null>(null);\n const lastFileRef = useRef<FilePickResult | null>(null);\n\n // Store callbacks in refs so they can be updated without recreating the manager\n const callbacksRef = useRef(options);\n\n // Update refs on every render to capture latest callbacks\n useEffect(() => {\n callbacksRef.current = options;\n });\n\n // Get or create manager from context when component mounts\n // Manager lifecycle is now handled by FlowManagerProvider\n useEffect(() => {\n const flowId = options.flowId;\n\n // Create stable callback wrappers that call the latest callbacks via refs\n const stableCallbacks = {\n onStateChange: setState,\n onProgress: (_uploadId: string, bytesUploaded: number, totalBytes: number | null) => {\n if (callbacksRef.current.onProgress) {\n const progress = totalBytes\n ? Math.round((bytesUploaded / totalBytes) * 100)\n : 0;\n callbacksRef.current.onProgress(progress, bytesUploaded, totalBytes);\n }\n },\n onChunkComplete: (chunkSize: number, bytesAccepted: number, bytesTotal: number | null) => {\n callbacksRef.current.onChunkComplete?.(chunkSize, bytesAccepted, bytesTotal);\n },\n onFlowComplete: (outputs: TypedOutput[]) => {\n callbacksRef.current.onFlowComplete?.(outputs);\n },\n onSuccess: (result: UploadFile) => {\n callbacksRef.current.onSuccess?.(result);\n },\n onError: (error: Error) => {\n callbacksRef.current.onError?.(error);\n },\n onAbort: () => {\n callbacksRef.current.onAbort?.();\n },\n };\n\n // Get manager from context (creates if doesn't exist, increments ref count)\n managerRef.current = getManager(\n flowId,\n stableCallbacks,\n {\n flowConfig: {\n flowId: options.flowId,\n storageId: options.storageId,\n outputNodeId: options.outputNodeId,\n metadata: options.metadata as Record<string, string> | undefined,\n },\n onChunkComplete: options.onChunkComplete,\n onSuccess: options.onSuccess,\n onError: options.onError,\n },\n );\n\n // Release manager when component unmounts or flowId changes\n return () => {\n releaseManager(flowId);\n managerRef.current = null;\n };\n }, [options.flowId, options.storageId, options.outputNodeId, getManager, releaseManager]);\n\n const upload = useCallback(\n async (file: FilePickResult) => {\n // Handle cancelled picker\n if (file.status === \"cancelled\") {\n return;\n }\n\n // Handle picker error\n if (file.status === \"error\") {\n options.onError?.(file.error);\n return;\n }\n\n lastFileRef.current = file;\n\n try {\n // Read file content\n const fileContent = await fileSystemProvider.readFile(file.data.uri);\n\n // Create a Blob from the file content using platform-aware utility\n // Handles differences between React Native and browser Blob APIs\n const blob = createBlobFromBuffer(fileContent, {\n type: file.data.mimeType || \"application/octet-stream\",\n });\n\n // Start the upload using the manager\n await managerRef.current?.upload(blob);\n } catch (error) {\n options.onError?.(error as Error);\n }\n },\n [fileSystemProvider, options],\n );\n\n const reset = useCallback(() => {\n managerRef.current?.reset();\n lastFileRef.current = null;\n }, []);\n\n const abort = useCallback(() => {\n managerRef.current?.abort();\n }, []);\n\n const retry = useCallback(() => {\n if (\n lastFileRef.current &&\n (state.status === \"error\" || state.status === \"aborted\")\n ) {\n upload(lastFileRef.current);\n }\n }, [upload, state.status]);\n\n // Derive computed values from state (reactive to state changes)\n const isActive =\n state.status === \"uploading\" || state.status === \"processing\";\n const canRetry =\n (state.status === \"error\" || state.status === \"aborted\") &&\n lastFileRef.current !== null;\n\n return {\n state,\n upload,\n abort,\n reset,\n retry,\n isActive,\n canRetry,\n };\n}\n","import type { UploadFile } from \"@uploadista/core/types\";\nimport { useCallback, useRef, useState } from \"react\";\nimport type { FilePickResult, UseMultiUploadOptions } from \"../types\";\nimport { useUploadistaContext } from \"./use-uploadista-context\";\n\nexport interface UploadItemState {\n id: string;\n file: Extract<FilePickResult, { status: \"success\" }>;\n status: \"idle\" | \"uploading\" | \"success\" | \"error\" | \"aborted\";\n progress: number;\n bytesUploaded: number;\n totalBytes: number;\n error: Error | null;\n result: UploadFile | null;\n}\n\nexport interface MultiUploadState {\n items: UploadItemState[];\n totalProgress: number;\n totalUploaded: number;\n totalBytes: number;\n activeCount: number;\n completedCount: number;\n failedCount: number;\n}\n\nconst initialState: MultiUploadState = {\n items: [],\n totalProgress: 0,\n totalUploaded: 0,\n totalBytes: 0,\n activeCount: 0,\n completedCount: 0,\n failedCount: 0,\n};\n\n/**\n * Hook for managing multiple concurrent file uploads with progress tracking.\n * Each file is uploaded independently using the core upload client.\n *\n * Must be used within an UploadistaProvider.\n *\n * @param options - Multi-upload configuration options\n * @returns Multi-upload state and control methods\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const multiUpload = useMultiUpload({\n * maxConcurrent: 3,\n * onSuccess: (result) => console.log('File uploaded:', result),\n * onError: (error) => console.error('Upload failed:', error),\n * });\n *\n * const handlePickFiles = async () => {\n * const files = await fileSystemProvider.pickImage({ allowMultiple: true });\n * multiUpload.addFiles(files);\n * await multiUpload.startUploads();\n * };\n *\n * return (\n * <View>\n * <Button title=\"Pick Files\" onPress={handlePickFiles} />\n * <Text>Progress: {multiUpload.state.totalProgress}%</Text>\n * <Text>Active: {multiUpload.state.activeCount}</Text>\n * <Text>Completed: {multiUpload.state.completedCount}/{multiUpload.state.items.length}</Text>\n * </View>\n * );\n * }\n * ```\n */\nexport function useMultiUpload(options: UseMultiUploadOptions = {}) {\n const { client } = useUploadistaContext();\n const [state, setState] = useState<MultiUploadState>(initialState);\n const abortControllersRef = useRef<Map<string, { abort: () => void }>>(\n new Map(),\n );\n const nextIdRef = useRef(0);\n // Use ref to track items synchronously\n const itemsRef = useRef<UploadItemState[]>([]);\n\n const generateId = useCallback(() => {\n return `upload-${Date.now()}-${nextIdRef.current++}`;\n }, []);\n\n const updateAggregateStats = useCallback((items: UploadItemState[]) => {\n const totalBytes = items.reduce((sum, item) => sum + item.totalBytes, 0);\n const totalUploaded = items.reduce(\n (sum, item) => sum + item.bytesUploaded,\n 0,\n );\n const totalProgress =\n totalBytes > 0 ? Math.round((totalUploaded / totalBytes) * 100) : 0;\n const activeCount = items.filter(\n (item) => item.status === \"uploading\",\n ).length;\n const completedCount = items.filter(\n (item) => item.status === \"success\",\n ).length;\n const failedCount = items.filter((item) => item.status === \"error\").length;\n\n // Update ref synchronously\n itemsRef.current = items;\n\n setState((prev) => ({\n ...prev,\n items,\n totalProgress,\n totalUploaded,\n totalBytes,\n activeCount,\n completedCount,\n failedCount,\n }));\n }, []);\n\n const addFiles = useCallback(\n (files: FilePickResult[]) => {\n // Filter out cancelled and error results, only keep successful picks\n const successfulFiles = files.filter(\n (file): file is Extract<FilePickResult, { status: \"success\" }> =>\n file.status === \"success\",\n );\n\n const newItems: UploadItemState[] = successfulFiles.map((file) => ({\n id: generateId(),\n file,\n status: \"idle\" as const,\n progress: 0,\n bytesUploaded: 0,\n totalBytes: file.data.size,\n error: null,\n result: null,\n }));\n\n // Update ref synchronously\n const updatedItems = [...itemsRef.current, ...newItems];\n itemsRef.current = updatedItems;\n\n setState((prev) => {\n const totalBytes = updatedItems.reduce(\n (sum, item) => sum + item.totalBytes,\n 0,\n );\n return {\n ...prev,\n items: updatedItems,\n totalBytes,\n };\n });\n\n return newItems.map((item) => item.id);\n },\n [generateId],\n );\n\n const uploadSingleItem = useCallback(\n async (item: UploadItemState) => {\n try {\n console.log(\"Uploading item:\", item.file.data.name);\n // Update status to uploading\n const updatedItems = itemsRef.current.map((i) =>\n i.id === item.id ? { ...i, status: \"uploading\" as const } : i,\n );\n updateAggregateStats(updatedItems);\n\n // Convert file URI to Blob using fetch (React Native compatible)\n // React Native's Blob doesn't support ArrayBuffer/Uint8Array constructor\n const response = await fetch(item.file.data.uri);\n const blob = await response.blob();\n\n // Override blob type if we have mimeType from picker\n const uploadInput = item.file.data.mimeType\n ? new Blob([blob], { type: item.file.data.mimeType })\n : blob;\n\n // Start upload using the client\n console.log(\"Uploading input:\", uploadInput);\n const uploadPromise = client.upload(uploadInput, {\n metadata: options.metadata,\n\n onProgress: (\n _uploadId: string,\n bytesUploaded: number,\n totalBytes: number | null,\n ) => {\n const progress = totalBytes\n ? Math.round((bytesUploaded / totalBytes) * 100)\n : 0;\n\n const updatedItems = itemsRef.current.map((i) =>\n i.id === item.id\n ? {\n ...i,\n progress,\n bytesUploaded,\n totalBytes: totalBytes || i.totalBytes,\n }\n : i,\n );\n updateAggregateStats(updatedItems);\n },\n\n onSuccess: (result: UploadFile) => {\n const updatedItems = itemsRef.current.map((i) =>\n i.id === item.id\n ? {\n ...i,\n status: \"success\" as const,\n progress: 100,\n result,\n bytesUploaded: result.size || i.totalBytes,\n }\n : i,\n );\n updateAggregateStats(updatedItems);\n\n options.onSuccess?.(result);\n abortControllersRef.current.delete(item.id);\n },\n\n onError: (error: Error) => {\n const updatedItems = itemsRef.current.map((i) =>\n i.id === item.id ? { ...i, status: \"error\" as const, error } : i,\n );\n updateAggregateStats(updatedItems);\n\n options.onError?.(error);\n abortControllersRef.current.delete(item.id);\n },\n });\n\n // Store abort controller\n const controller = await uploadPromise;\n abortControllersRef.current.set(item.id, controller);\n } catch (error) {\n console.error(\"Error uploading item:\", error);\n const updatedItems = itemsRef.current.map((i) =>\n i.id === item.id\n ? {\n ...i,\n status: \"error\" as const,\n error: error as Error,\n }\n : i,\n );\n updateAggregateStats(updatedItems);\n\n options.onError?.(error as Error);\n abortControllersRef.current.delete(item.id);\n }\n },\n [client, options, updateAggregateStats],\n );\n\n const startUploads = useCallback(\n async (itemIds?: string[]) => {\n const maxConcurrent = options.maxConcurrent || 3;\n\n // Get items from ref (synchronous access to latest items)\n const itemsToUpload = itemIds\n ? itemsRef.current.filter(\n (item) => itemIds.includes(item.id) && item.status === \"idle\",\n )\n : itemsRef.current.filter((item) => item.status === \"idle\");\n\n console.log(\"Items to upload:\", itemsToUpload.length, itemsToUpload);\n\n // Process items in batches\n for (let i = 0; i < itemsToUpload.length; i += maxConcurrent) {\n const batch = itemsToUpload.slice(i, i + maxConcurrent);\n await Promise.all(batch.map((item) => uploadSingleItem(item)));\n }\n },\n [options.maxConcurrent, uploadSingleItem],\n );\n\n const removeItem = useCallback(\n (id: string) => {\n const controller = abortControllersRef.current.get(id);\n if (controller) {\n controller.abort();\n abortControllersRef.current.delete(id);\n }\n\n const updatedItems = itemsRef.current.filter((item) => item.id !== id);\n updateAggregateStats(updatedItems);\n },\n [updateAggregateStats],\n );\n\n const abortItem = useCallback(\n (id: string) => {\n const controller = abortControllersRef.current.get(id);\n if (controller) {\n controller.abort();\n abortControllersRef.current.delete(id);\n }\n\n const updatedItems = itemsRef.current.map((item) =>\n item.id === id ? { ...item, status: \"aborted\" as const } : item,\n );\n updateAggregateStats(updatedItems);\n },\n [updateAggregateStats],\n );\n\n const clear = useCallback(() => {\n // Abort all active uploads\n abortControllersRef.current.forEach((controller) => {\n controller.abort();\n });\n abortControllersRef.current.clear();\n\n // Clear ref\n itemsRef.current = [];\n\n setState(initialState);\n }, []);\n\n const retryItem = useCallback(\n async (id: string) => {\n const item = itemsRef.current.find((i) => i.id === id);\n if (item && (item.status === \"error\" || item.status === \"aborted\")) {\n // Reset item status to idle\n const updatedItems = itemsRef.current.map((i) =>\n i.id === id\n ? {\n ...i,\n status: \"idle\" as const,\n progress: 0,\n bytesUploaded: 0,\n error: null,\n }\n : i,\n );\n updateAggregateStats(updatedItems);\n\n // Upload it (get the reset item from the updated items)\n const resetItem = itemsRef.current.find((i) => i.id === id);\n if (resetItem) {\n await uploadSingleItem(resetItem);\n }\n }\n },\n [uploadSingleItem, updateAggregateStats],\n );\n\n return {\n state,\n addFiles,\n startUploads,\n removeItem,\n abortItem,\n retryItem,\n clear,\n };\n}\n","import { useCallback } from \"react\";\nimport type { FilePickResult, UseGalleryUploadOptions } from \"../types\";\nimport { useMultiUpload } from \"./use-multi-upload\";\nimport { useUploadistaContext } from \"./use-uploadista-context\";\n\n/**\n * Hook for selecting and uploading photos/videos from gallery\n * Handles batch selection and concurrent uploads\n * @param options - Gallery upload configuration\n * @returns Upload state and gallery selection/upload function\n */\nexport function useGalleryUpload(options?: UseGalleryUploadOptions) {\n const { fileSystemProvider } = useUploadistaContext();\n const uploadHook = useMultiUpload({\n maxConcurrent: 3,\n metadata: options?.metadata,\n onSuccess: options?.onSuccess,\n onError: options?.onError,\n });\n\n // Select and upload media from gallery\n const selectAndUpload = useCallback(async () => {\n let result: FilePickResult;\n\n // Select appropriate media type\n if (options?.mediaType === \"video\") {\n result = await fileSystemProvider.pickVideo({\n allowMultiple: options?.allowMultiple ?? true,\n });\n } else if (options?.mediaType === \"photo\") {\n result = await fileSystemProvider.pickImage({\n allowMultiple: options?.allowMultiple ?? true,\n });\n } else {\n // For 'mixed' or default, use pickImage first (can be extended to support both)\n result = await fileSystemProvider.pickImage({\n allowMultiple: options?.allowMultiple ?? true,\n });\n }\n\n // Handle cancelled picker\n if (result.status === \"cancelled\") {\n return [];\n }\n\n // Handle picker error\n if (result.status === \"error\") {\n console.error(\"Gallery selection error:\", result.error);\n options?.onError?.(result.error);\n return [];\n }\n\n // Success - add file and start upload\n const itemIds = uploadHook.addFiles([result]);\n await uploadHook.startUploads(itemIds);\n\n return itemIds;\n }, [\n fileSystemProvider,\n options?.allowMultiple,\n options?.mediaType,\n options?.onError,\n uploadHook,\n ]);\n\n return {\n ...uploadHook,\n selectAndUpload,\n };\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport type { UploadMetrics } from \"../types\";\n\n/**\n * Hook for tracking upload performance metrics\n * @returns Metrics object and methods to track uploads\n */\nexport function useUploadMetrics() {\n const startTimeRef = useRef<number | null>(null);\n const startBytesRef = useRef<number>(0);\n const peakSpeedRef = useRef<number>(0);\n\n const [metrics, setMetrics] = useState<UploadMetrics>({\n totalBytes: 0,\n durationMs: 0,\n avgSpeed: 0,\n peakSpeed: 0,\n retries: 0,\n });\n\n // Start tracking\n const start = useCallback(() => {\n startTimeRef.current = Date.now();\n startBytesRef.current = 0;\n peakSpeedRef.current = 0;\n }, []);\n\n // Update metrics based on current progress\n const update = useCallback(\n (uploadedBytes: number, _totalBytes: number, currentRetries = 0) => {\n if (!startTimeRef.current) {\n return;\n }\n\n const now = Date.now();\n const durationMs = now - startTimeRef.current;\n const speed = durationMs > 0 ? (uploadedBytes / durationMs) * 1000 : 0;\n\n if (speed > peakSpeedRef.current) {\n peakSpeedRef.current = speed;\n }\n\n setMetrics({\n totalBytes: uploadedBytes,\n durationMs,\n avgSpeed: durationMs > 0 ? (uploadedBytes / durationMs) * 1000 : 0,\n peakSpeed: peakSpeedRef.current,\n retries: currentRetries,\n });\n },\n [],\n );\n\n // End tracking and return final metrics\n const end = useCallback(() => {\n const finalMetrics = metrics;\n startTimeRef.current = null;\n return finalMetrics;\n }, [metrics]);\n\n // Reset metrics\n const reset = useCallback(() => {\n startTimeRef.current = null;\n startBytesRef.current = 0;\n peakSpeedRef.current = 0;\n setMetrics({\n totalBytes: 0,\n durationMs: 0,\n avgSpeed: 0,\n peakSpeed: 0,\n retries: 0,\n });\n }, []);\n\n return {\n metrics,\n start,\n update,\n end,\n reset,\n };\n}\n","/**\n * File utility functions for React Native uploads\n */\n\n/**\n * Format file size to human readable string\n * @param bytes - Size in bytes\n * @returns Formatted size string (e.g., \"1.5 MB\")\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return \"0 Bytes\";\n\n const k = 1024;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${Math.round((bytes / k ** i) * 100) / 100} ${sizes[i]}`;\n}\n\n/**\n * Get MIME type from file name\n * @param fileName - File name with extension\n * @returns MIME type or 'application/octet-stream' as fallback\n */\nexport function getMimeTypeFromFileName(fileName: string): string {\n const mimeTypes: Record<string, string> = {\n // Images\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".gif\": \"image/gif\",\n \".bmp\": \"image/bmp\",\n \".webp\": \"image/webp\",\n \".svg\": \"image/svg+xml\",\n\n // Videos\n \".mp4\": \"video/mp4\",\n \".avi\": \"video/x-msvideo\",\n \".mov\": \"video/quicktime\",\n \".wmv\": \"video/x-ms-wmv\",\n \".flv\": \"video/x-flv\",\n \".mkv\": \"video/x-matroska\",\n \".webm\": \"video/webm\",\n\n // Audio\n \".mp3\": \"audio/mpeg\",\n \".wav\": \"audio/wav\",\n \".aac\": \"audio/aac\",\n \".flac\": \"audio/flac\",\n \".m4a\": \"audio/mp4\",\n\n // Documents\n \".pdf\": \"application/pdf\",\n \".doc\": \"application/msword\",\n \".docx\":\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \".xls\": \"application/vnd.ms-excel\",\n \".xlsx\":\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \".ppt\": \"application/vnd.ms-powerpoint\",\n \".pptx\":\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \".txt\": \"text/plain\",\n \".csv\": \"text/csv\",\n \".json\": \"application/json\",\n \".xml\": \"application/xml\",\n \".zip\": \"application/zip\",\n };\n\n const ext = fileName.toLowerCase().slice(fileName.lastIndexOf(\".\"));\n return mimeTypes[ext] || \"application/octet-stream\";\n}\n\n/**\n * Check if file type is allowed\n * @param fileName - File name to check\n * @param allowedTypes - Array of allowed MIME types (e.g., ['image/jpeg', 'image/png'])\n * @returns True if file type is allowed\n */\nexport function isFileTypeAllowed(\n fileName: string,\n allowedTypes: string[],\n): boolean {\n if (!allowedTypes || allowedTypes.length === 0) {\n return true;\n }\n\n const mimeType = getMimeTypeFromFileName(fileName);\n return allowedTypes.some((allowed) => {\n if (allowed.endsWith(\"/*\")) {\n // Handle wildcard patterns like 'image/*'\n const [type] = allowed.split(\"/\");\n return mimeType.startsWith(`${type}/`);\n }\n return allowed === mimeType;\n });\n}\n\n/**\n * Check if file size is within limits\n * @param fileSize - File size in bytes\n * @param maxSize - Maximum allowed size in bytes (optional)\n * @param minSize - Minimum allowed size in bytes (optional)\n * @returns True if file size is within limits\n */\nexport function isFileSizeValid(\n fileSize: number,\n maxSize?: number,\n minSize?: number,\n): boolean {\n if (maxSize !== undefined && fileSize > maxSize) {\n return false;\n }\n\n if (minSize !== undefined && fileSize < minSize) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Get file extension\n * @param fileName - File name\n * @returns File extension without dot (e.g., 'pdf' for 'document.pdf')\n */\nexport function getFileExtension(fileName: string): string {\n const lastDot = fileName.lastIndexOf(\".\");\n if (lastDot === -1) return \"\";\n return fileName.slice(lastDot + 1).toLowerCase();\n}\n\n/**\n * Get file name without extension\n * @param fileName - File name\n * @returns File name without extension\n */\nexport function getFileNameWithoutExtension(fileName: string): string {\n const lastDot = fileName.lastIndexOf(\".\");\n if (lastDot === -1) return fileName;\n return fileName.slice(0, lastDot);\n}\n\n/**\n * Check if file is an image\n * @param fileName - File name\n * @returns True if file is an image\n */\nexport function isImageFile(fileName: string): boolean {\n const imageExtensions = [\n \".jpg\",\n \".jpeg\",\n \".png\",\n \".gif\",\n \".bmp\",\n \".webp\",\n \".svg\",\n ];\n const ext = fileName.toLowerCase().slice(fileName.lastIndexOf(\".\"));\n return imageExtensions.includes(ext);\n}\n\n/**\n * Check if file is a video\n * @param fileName - File name\n * @returns True if file is a video\n */\nexport function isVideoFile(fileName: string): boolean {\n const videoExtensions = [\n \".mp4\",\n \".avi\",\n \".mov\",\n \".wmv\",\n \".flv\",\n \".mkv\",\n \".webm\",\n ];\n const ext = fileName.toLowerCase().slice(fileName.lastIndexOf(\".\"));\n return videoExtensions.includes(ext);\n}\n\n/**\n * Check if file is a document\n * @param fileName - File name\n * @returns True if file is a document\n */\nexport function isDocumentFile(fileName: string): boolean {\n const docExtensions = [\n \".pdf\",\n \".doc\",\n \".docx\",\n \".xls\",\n \".xlsx\",\n \".ppt\",\n \".pptx\",\n \".txt\",\n \".csv\",\n ];\n const ext = fileName.toLowerCase().slice(fileName.lastIndexOf(\".\"));\n return docExtensions.includes(ext);\n}\n","/**\n * Permission utility functions for React Native uploads\n * Handles camera, gallery, and file access permissions\n */\n\n/**\n * Permission types\n */\nexport enum PermissionType {\n CAMERA = \"CAMERA\",\n PHOTO_LIBRARY = \"PHOTO_LIBRARY\",\n WRITE_STORAGE = \"WRITE_STORAGE\",\n READ_STORAGE = \"READ_STORAGE\",\n}\n\n/**\n * Permission status\n */\nexport enum PermissionStatus {\n GRANTED = \"granted\",\n DENIED = \"denied\",\n NOT_DETERMINED = \"not_determined\",\n RESTRICTED = \"restricted\",\n}\n\n/**\n * Request camera permission\n * This is primarily used to check if we should attempt camera operations\n * Actual permission requests are handled by the file system providers\n *\n * @returns Promise resolving to true if permission is granted or already granted\n */\nexport async function requestCameraPermission(): Promise<boolean> {\n try {\n // Permission requests are handled by the file system provider implementations\n // This function serves as a placeholder for app-level permission handling\n console.log(\n \"Camera permission requested (handled by file system provider)\",\n );\n return true;\n } catch (error) {\n console.error(\"Failed to request camera permission:\", error);\n return false;\n }\n}\n\n/**\n * Request photo library permission\n * @returns Promise resolving to true if permission is granted\n */\nexport async function requestPhotoLibraryPermission(): Promise<boolean> {\n try {\n console.log(\n \"Photo library permission requested (handled by file system provider)\",\n );\n return true;\n } catch (error) {\n console.error(\"Failed to request photo library permission:\", error);\n return false;\n }\n}\n\n/**\n * Request storage read permission\n * @returns Promise resolving to true if permission is granted\n */\nexport async function requestStorageReadPermission(): Promise<boolean> {\n try {\n console.log(\n \"Storage read permission requested (handled by file system provider)\",\n );\n return true;\n } catch (error) {\n console.error(\"Failed to request storage read permission:\", error);\n return false;\n }\n}\n\n/**\n * Request storage write permission\n * @returns Promise resolving to true if permission is granted\n */\nexport async function requestStorageWritePermission(): Promise<boolean> {\n try {\n console.log(\n \"Storage write permission requested (handled by file system provider)\",\n );\n return true;\n } catch (error) {\n console.error(\"Failed to request storage write permission:\", error);\n return false;\n }\n}\n\n/**\n * Request multiple permissions at once\n * @param permissions - Array of permission types to request\n * @returns Promise resolving to true if all permissions are granted\n */\nexport async function requestPermissions(\n permissions: PermissionType[],\n): Promise<boolean> {\n try {\n const results = await Promise.all(\n permissions.map(async (permission) => {\n switch (permission) {\n case PermissionType.CAMERA:\n return requestCameraPermission();\n case PermissionType.PHOTO_LIBRARY:\n return requestPhotoLibraryPermission();\n case PermissionType.READ_STORAGE:\n return requestStorageReadPermission();\n case PermissionType.WRITE_STORAGE:\n return requestStorageWritePermission();\n default:\n return false;\n }\n }),\n );\n\n return results.every((result) => result);\n } catch (error) {\n console.error(\"Failed to request permissions:\", error);\n return false;\n }\n}\n\n/**\n * Check if all required permissions are granted\n * @param permissions - Array of permission types to check\n * @returns Promise resolving to true if all permissions are granted\n */\nexport async function hasPermissions(\n _permissions: PermissionType[],\n): Promise<boolean> {\n try {\n // In React Native, permission checking is typically handled by the platform\n // This is a placeholder that assumes permissions will be handled by the file system provider\n return true;\n } catch (error) {\n console.error(\"Failed to check permissions:\", error);\n return false;\n }\n}\n\n/**\n * Get permission status\n * @param permission - Permission type to check\n * @returns Promise resolving to permission status\n */\nexport async function getPermissionStatus(\n _permission: PermissionType,\n): Promise<PermissionStatus> {\n try {\n // This is a placeholder implementation\n // Real implementation would use platform-specific APIs\n return PermissionStatus.GRANTED;\n } catch (error) {\n console.error(\"Failed to get permission status:\", error);\n return PermissionStatus.DENIED;\n }\n}\n\n/**\n * Open app settings to request permissions\n * Guides user to app settings where they can manually enable permissions\n */\nexport function openAppSettings(): void {\n try {\n // This would typically use react-native-app-settings or similar\n console.log(\n \"Opening app settings (requires react-native-app-settings or platform implementation)\",\n );\n } catch (error) {\n console.error(\"Failed to open app settings:\", error);\n }\n}\n","/**\n * URI utility functions for React Native file handling\n */\n\n/**\n * Extract file name from URI\n * @param uri - File URI\n * @returns File name extracted from URI\n */\nexport function getFileNameFromUri(uri: string): string {\n try {\n // Handle different URI formats\n if (uri.startsWith(\"file://\")) {\n // File URI format\n const path = uri.replace(\"file://\", \"\");\n return path.split(\"/\").pop() || \"file\";\n }\n\n if (uri.startsWith(\"content://\")) {\n // Content URI format (Android)\n const parts = uri.split(\"/\");\n return parts[parts.length - 1] || \"file\";\n }\n\n // Assume it's a path or other format\n const parts = uri.split(\"/\");\n return parts[parts.length - 1] || \"file\";\n } catch {\n return \"file\";\n }\n}\n\n/**\n * Convert file path to file URI\n * @param filePath - File path\n * @returns File URI\n */\nexport function pathToUri(filePath: string): string {\n if (filePath.startsWith(\"file://\")) {\n return filePath;\n }\n\n if (filePath.startsWith(\"content://\")) {\n return filePath;\n }\n\n // Convert to file URI\n return `file://${filePath}`;\n}\n\n/**\n * Convert file URI to file path\n * @param uri - File URI\n * @returns File path\n */\nexport function uriToPath(uri: string): string {\n if (uri.startsWith(\"file://\")) {\n return uri.replace(\"file://\", \"\");\n }\n\n if (uri.startsWith(\"content://\")) {\n // Content URIs cannot be converted to paths directly\n return uri;\n }\n\n return uri;\n}\n\n/**\n * Get directory from URI\n * @param uri - File URI\n * @returns Directory path\n */\nexport function getDirectoryFromUri(uri: string): string {\n try {\n const path = uriToPath(uri);\n const parts = path.split(\"/\");\n parts.pop(); // Remove file name\n return parts.join(\"/\");\n } catch {\n return \"\";\n }\n}\n\n/**\n * Check if URI is a content URI (Android specific)\n * @param uri - URI to check\n * @returns True if URI is a content URI\n */\nexport function isContentUri(uri: string): boolean {\n return uri.startsWith(\"content://\");\n}\n\n/**\n * Check if URI is a file URI\n * @param uri - URI to check\n * @returns True if URI is a file URI\n */\nexport function isFileUri(uri: string): boolean {\n return uri.startsWith(\"file://\");\n}\n\n/**\n * Normalize URI for cross-platform compatibility\n * @param uri - URI to normalize\n * @returns Normalized URI\n */\nexport function normalizeUri(uri: string): string {\n // Remove duplicate slashes (but keep protocol slashes)\n return uri.replace(/([^:]\\/)\\/+/g, \"$1\");\n}\n\n/**\n * Get MIME type hint from URI\n * @param uri - File URI\n * @returns MIME type hint based on file extension\n */\nexport function getMimeTypeFromUri(uri: string): string {\n const fileName = getFileNameFromUri(uri);\n\n const mimeTypes: Record<string, string> = {\n // Images\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".gif\": \"image/gif\",\n \".bmp\": \"image/bmp\",\n \".webp\": \"image/webp\",\n\n // Videos\n \".mp4\": \"video/mp4\",\n \".mov\": \"video/quicktime\",\n \".avi\": \"video/x-msvideo\",\n\n // Audio\n \".mp3\": \"audio/mpeg\",\n \".wav\": \"audio/wav\",\n \".aac\": \"audio/aac\",\n\n // Documents\n \".pdf\": \"application/pdf\",\n \".txt\": \"text/plain\",\n \".json\": \"application/json\",\n };\n\n const ext = fileName.toLowerCase().slice(fileName.lastIndexOf(\".\"));\n return mimeTypes[ext] || \"application/octet-stream\";\n}\n","import { ActivityIndicator, StyleSheet, Text, View } from \"react-native\";\nimport type { UploadState } from \"../hooks/use-upload\";\nimport { formatFileSize } from \"../utils\";\n\nexport interface UploadProgressProps {\n /** Upload state information */\n state: UploadState;\n /** Optional custom label */\n label?: string;\n}\n\n/**\n * Component to display upload progress with percentage, size, and speed\n */\nexport function UploadProgress({ state, label }: UploadProgressProps) {\n const getStatusColor = () => {\n switch (state.status) {\n case \"uploading\":\n return \"#007AFF\";\n case \"success\":\n return \"#34C759\";\n case \"error\":\n case \"aborted\":\n return \"#FF3B30\";\n default:\n return \"#999999\";\n }\n };\n\n const renderContent = () => {\n switch (state.status) {\n case \"idle\":\n return (\n <View style={styles.container}>\n <Text style={styles.label}>{label || \"Ready to upload\"}</Text>\n </View>\n );\n\n case \"uploading\":\n return (\n <View style={styles.container}>\n <View style={styles.headerRow}>\n <Text style={styles.label}>{label || \"Uploading\"}</Text>\n <Text style={styles.percentage}>{state.progress}%</Text>\n </View>\n\n {/* Progress bar */}\n <View style={styles.progressBarContainer}>\n <View\n style={[\n styles.progressBar,\n {\n width: `${state.progress}%`,\n backgroundColor: getStatusColor(),\n },\n ]}\n />\n </View>\n\n {/* Details row */}\n <View style={styles.detailsRow}>\n <Text style={styles.detail}>\n {formatFileSize(state.bytesUploaded)} /{\" \"}\n {formatFileSize(state.totalBytes || 0)}\n </Text>\n </View>\n </View>\n );\n\n case \"success\":\n return (\n <View style={styles.container}>\n <View style={styles.headerRow}>\n <Text style={[styles.label, { color: getStatusColor() }]}>\n {label || \"Upload complete\"}\n </Text>\n <Text style={[styles.percentage, { color: getStatusColor() }]}>\n ✓\n </Text>\n </View>\n <Text style={[styles.detail, { color: getStatusColor() }]}>\n {formatFileSize(state.totalBytes || 0)}\n </Text>\n </View>\n );\n\n case \"error\":\n return (\n <View style={styles.container}>\n <View style={styles.headerRow}>\n <Text style={[styles.label, { color: getStatusColor() }]}>\n {label || \"Upload failed\"}\n </Text>\n <Text style={[styles.percentage, { color: getStatusColor() }]}>\n ✕\n </Text>\n </View>\n {state.error && (\n <Text style={[styles.detail, { color: getStatusColor() }]}>\n {state.error.message}\n </Text>\n )}\n </View>\n );\n\n case \"aborted\":\n return (\n <View style={styles.container}>\n <Text style={[styles.label, { color: getStatusColor() }]}>\n {label || \"Upload cancelled\"}\n </Text>\n </View>\n );\n\n default:\n return null;\n }\n };\n\n return (\n <View\n style={[\n styles.wrapper,\n {\n borderLeftColor: getStatusColor(),\n },\n ]}\n >\n {state.status === \"uploading\" && (\n <ActivityIndicator\n size=\"small\"\n color={getStatusColor()}\n style={styles.spinner}\n />\n )}\n {renderContent()}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n wrapper: {\n flexDirection: \"row\",\n alignItems: \"flex-start\",\n paddingVertical: 8,\n paddingHorizontal: 12,\n borderLeftWidth: 4,\n backgroundColor: \"#f5f5f5\",\n borderRadius: 4,\n gap: 8,\n },\n spinner: {\n marginTop: 4,\n },\n container: {\n flex: 1,\n gap: 4,\n },\n headerRow: {\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n },\n label: {\n fontSize: 14,\n fontWeight: \"600\",\n color: \"#333333\",\n flex: 1,\n },\n percentage: {\n fontSize: 14,\n fontWeight: \"600\",\n color: \"#007AFF\",\n minWidth: 36,\n textAlign: \"right\",\n },\n progressBarContainer: {\n height: 4,\n backgroundColor: \"#e0e0e0\",\n borderRadius: 2,\n overflow: \"hidden\",\n },\n progressBar: {\n height: \"100%\",\n borderRadius: 2,\n },\n detailsRow: {\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n },\n detail: {\n fontSize: 12,\n color: \"#666666\",\n },\n});\n","import { type ReactNode, useEffect } from \"react\";\nimport {\n ActivityIndicator,\n Pressable,\n StyleSheet,\n Text,\n View,\n} from \"react-native\";\nimport { useCameraUpload } from \"../hooks\";\nimport type { UseCameraUploadOptions } from \"../types\";\nimport { UploadProgress } from \"./UploadProgress\";\n\nexport interface CameraUploadButtonProps {\n /** Options for camera upload */\n options?: UseCameraUploadOptions;\n /** Button label text */\n label?: string;\n /** Custom button content */\n children?: ReactNode;\n /** Callback when upload completes successfully */\n onSuccess?: (result: unknown) => void;\n /** Callback when upload fails */\n onError?: (error: Error) => void;\n /** Callback when upload is cancelled */\n onCancel?: () => void;\n /** Whether to show progress inline */\n showProgress?: boolean;\n}\n\n/**\n * Button component for camera capture and upload\n * Triggers camera on press and handles upload with progress display\n */\nexport function CameraUploadButton({\n options,\n label = \"Take Photo\",\n children,\n onSuccess,\n onError,\n onCancel,\n showProgress = true,\n}: CameraUploadButtonProps) {\n const { state, captureAndUpload } = useCameraUpload(options);\n\n const handlePress = async () => {\n try {\n await captureAndUpload();\n } catch (error) {\n if (error instanceof Error) {\n if (\n error.message.includes(\"cancelled\") ||\n error.message.includes(\"aborted\")\n ) {\n onCancel?.();\n } else {\n onError?.(error);\n }\n }\n }\n };\n\n const isLoading = state.status === \"uploading\";\n const isDisabled = isLoading || state.status === \"aborted\";\n\n useEffect(() => {\n if (state.status === \"success\" && state.result) {\n onSuccess?.(state.result);\n }\n }, [state.status, state.result, onSuccess]);\n\n useEffect(() => {\n if (state.status === \"error\" && state.error) {\n onError?.(state.error);\n }\n }, [state.status, state.error, onError]);\n\n return (\n <View style={styles.container}>\n <Pressable\n style={[styles.button, isDisabled && styles.buttonDisabled]}\n onPress={handlePress}\n disabled={isDisabled}\n >\n {isLoading && (\n <ActivityIndicator\n size=\"small\"\n color=\"#FFFFFF\"\n style={styles.spinner}\n />\n )}\n <Text style={styles.buttonText}>{children || label}</Text>\n </Pressable>\n {showProgress && state.status !== \"idle\" && (\n <View style={styles.progressContainer}>\n <UploadProgress state={state} label=\"Camera upload\" />\n </View>\n )}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n gap: 8,\n },\n button: {\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"center\",\n paddingVertical: 12,\n paddingHorizontal: 16,\n backgroundColor: \"#007AFF\",\n borderRadius: 8,\n gap: 8,\n },\n buttonDisabled: {\n opacity: 0.6,\n },\n buttonText: {\n fontSize: 16,\n fontWeight: \"600\",\n color: \"#FFFFFF\",\n },\n spinner: {\n marginRight: 4,\n },\n progressContainer: {\n marginTop: 4,\n },\n});\n","import { type ReactNode, useEffect } from \"react\";\nimport {\n ActivityIndicator,\n Pressable,\n StyleSheet,\n Text,\n View,\n} from \"react-native\";\nimport { useFileUpload } from \"../hooks\";\nimport type { UseFileUploadOptions } from \"../types\";\nimport { UploadProgress } from \"./UploadProgress\";\n\nexport interface FileUploadButtonProps {\n /** Options for file upload */\n options?: UseFileUploadOptions;\n /** Button label text */\n label?: string;\n /** Custom button content */\n children?: ReactNode;\n /** Callback when upload completes successfully */\n onSuccess?: (result: unknown) => void;\n /** Callback when upload fails */\n onError?: (error: Error) => void;\n /** Callback when upload is cancelled */\n onCancel?: () => void;\n /** Whether to show progress inline */\n showProgress?: boolean;\n}\n\n/**\n * Button component for document/file selection and upload\n * Generic file picker with progress display\n */\nexport function FileUploadButton({\n options,\n label = \"Choose File\",\n children,\n onSuccess,\n onError,\n onCancel,\n showProgress = true,\n}: FileUploadButtonProps) {\n const { state, pickAndUpload } = useFileUpload(options);\n\n const handlePress = async () => {\n try {\n await pickAndUpload();\n } catch (error) {\n if (error instanceof Error) {\n if (\n error.message.includes(\"cancelled\") ||\n error.message.includes(\"aborted\")\n ) {\n onCancel?.();\n } else {\n onError?.(error);\n }\n }\n }\n };\n\n const isLoading = state.status === \"uploading\";\n const isDisabled = isLoading || state.status === \"aborted\";\n\n useEffect(() => {\n if (state.status === \"success\" && state.result) {\n onSuccess?.(state.result);\n }\n }, [state.status, state.result, onSuccess]);\n\n useEffect(() => {\n if (state.status === \"error\" && state.error) {\n onError?.(state.error);\n }\n }, [state.status, state.error, onError]);\n\n return (\n <View style={styles.container}>\n <Pressable\n style={[styles.button, isDisabled && styles.buttonDisabled]}\n onPress={handlePress}\n disabled={isDisabled}\n >\n {isLoading && (\n <ActivityIndicator\n size=\"small\"\n color=\"#FFFFFF\"\n style={styles.spinner}\n />\n )}\n <Text style={styles.buttonText}>{children || label}</Text>\n </Pressable>\n {showProgress && state.status !== \"idle\" && (\n <View style={styles.progressContainer}>\n <UploadProgress state={state} label=\"File upload\" />\n </View>\n )}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n gap: 8,\n },\n button: {\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"center\",\n paddingVertical: 12,\n paddingHorizontal: 16,\n backgroundColor: \"#FF9500\",\n borderRadius: 8,\n gap: 8,\n },\n buttonDisabled: {\n opacity: 0.6,\n },\n buttonText: {\n fontSize: 16,\n fontWeight: \"600\",\n color: \"#FFFFFF\",\n },\n spinner: {\n marginRight: 4,\n },\n progressContainer: {\n marginTop: 4,\n },\n});\n","import React, { type ReactNode } from \"react\";\nimport {\n ActivityIndicator,\n FlatList,\n Pressable,\n StyleSheet,\n Text,\n View,\n} from \"react-native\";\nimport { useGalleryUpload } from \"../hooks\";\nimport type { UseGalleryUploadOptions } from \"../types\";\nimport { UploadProgress } from \"./UploadProgress\";\n\nexport interface GalleryUploadButtonProps {\n /** Options for gallery upload */\n options?: UseGalleryUploadOptions;\n /** Button label text */\n label?: string;\n /** Custom button content */\n children?: ReactNode;\n /** Callback when all uploads complete successfully */\n onSuccess?: (results: unknown[]) => void;\n /** Callback when any upload fails */\n onError?: (error: Error) => void;\n /** Callback when upload is cancelled */\n onCancel?: () => void;\n /** Whether to show individual progress for each file */\n showProgress?: boolean;\n}\n\n/**\n * Button component for gallery selection and batch upload\n * Triggers gallery picker on press and handles concurrent uploads\n */\nexport function GalleryUploadButton({\n options,\n label = \"Select from Gallery\",\n children,\n onSuccess,\n onError,\n onCancel,\n showProgress = true,\n}: GalleryUploadButtonProps) {\n const { state, selectAndUpload } = useGalleryUpload(options);\n\n const handlePress = async () => {\n try {\n await selectAndUpload();\n } catch (error) {\n if (error instanceof Error) {\n if (\n error.message.includes(\"cancelled\") ||\n error.message.includes(\"aborted\")\n ) {\n onCancel?.();\n } else {\n onError?.(error);\n }\n }\n }\n };\n\n const isLoading = state.items.some((item) => item.status === \"uploading\");\n const hasItems = state.items.length > 0;\n const allComplete =\n hasItems &&\n state.items.every(\n (item) => item.status !== \"uploading\" && item.status !== \"idle\",\n );\n\n React.useEffect(() => {\n if (allComplete) {\n const results = state.items\n .filter((item) => item.status === \"success\")\n .map((item) => item.result);\n if (results.length > 0) {\n onSuccess?.(results);\n }\n }\n }, [allComplete, state.items, onSuccess]);\n\n React.useEffect(() => {\n const errors = state.items.filter((item) => item.status === \"error\");\n const firstError = errors[0]?.error;\n if (firstError) {\n onError?.(firstError);\n }\n }, [state.items, onError]);\n\n const renderItem = ({ item }: { item: (typeof state.items)[0] }) => (\n <View key={item.id} style={styles.itemContainer}>\n <UploadProgress\n state={{\n status: item.status,\n progress: item.progress,\n bytesUploaded: item.bytesUploaded,\n totalBytes: item.totalBytes,\n error: item.error,\n result: item.result,\n }}\n label={item.file.data.name}\n />\n </View>\n );\n\n return (\n <View style={styles.container}>\n <Pressable\n style={[styles.button, isLoading && styles.buttonDisabled]}\n onPress={handlePress}\n disabled={isLoading}\n >\n {isLoading && (\n <ActivityIndicator\n size=\"small\"\n color=\"#FFFFFF\"\n style={styles.spinner}\n />\n )}\n <Text style={styles.buttonText}>\n {children || label}\n {hasItems && ` (${state.items.length})`}\n </Text>\n </Pressable>\n\n {hasItems && (\n <View style={styles.statsContainer}>\n <Text style={styles.statsText}>\n Progress: {state.items.filter((i) => i.status === \"success\").length}\n /{state.items.length} uploaded\n </Text>\n <Text style={styles.statsText}>Overall: {state.totalProgress}%</Text>\n </View>\n )}\n\n {showProgress && hasItems && (\n <FlatList\n scrollEnabled={false}\n data={state.items}\n renderItem={renderItem}\n keyExtractor={(item) => item.id}\n style={styles.listContainer}\n contentContainerStyle={styles.listContent}\n ItemSeparatorComponent={() => <View style={styles.separator} />}\n />\n )}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n gap: 8,\n },\n button: {\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"center\",\n paddingVertical: 12,\n paddingHorizontal: 16,\n backgroundColor: \"#34C759\",\n borderRadius: 8,\n gap: 8,\n },\n buttonDisabled: {\n opacity: 0.6,\n },\n buttonText: {\n fontSize: 16,\n fontWeight: \"600\",\n color: \"#FFFFFF\",\n },\n spinner: {\n marginRight: 4,\n },\n statsContainer: {\n paddingVertical: 8,\n paddingHorizontal: 12,\n backgroundColor: \"#f5f5f5\",\n borderRadius: 4,\n gap: 4,\n },\n statsText: {\n fontSize: 12,\n color: \"#666666\",\n },\n listContainer: {\n maxHeight: 400,\n },\n listContent: {\n gap: 8,\n },\n itemContainer: {\n paddingHorizontal: 0,\n },\n separator: {\n height: 4,\n },\n});\n","import { FlatList, Pressable, StyleSheet, Text, View } from \"react-native\";\nimport type { UploadItem } from \"../types\";\nimport { UploadProgress } from \"./UploadProgress\";\n\nexport interface UploadListProps {\n /** List of upload items to display */\n items: UploadItem[];\n /** Callback when remove item is pressed */\n onRemove?: (id: string) => void;\n /** Callback when item is pressed */\n onItemPress?: (item: UploadItem) => void;\n /** Whether to show remove button */\n showRemoveButton?: boolean;\n}\n\n/**\n * Component to display a list of upload items with individual progress\n * Shows status indicators and allows removal of items\n */\nexport function UploadList({\n items,\n onRemove,\n onItemPress,\n showRemoveButton = true,\n}: UploadListProps) {\n const renderItem = ({ item }: { item: UploadItem }) => (\n <Pressable\n style={[\n styles.itemContainer,\n { borderLeftColor: getStatusColor(item.progress.state) },\n ]}\n onPress={() => onItemPress?.(item)}\n >\n <View style={styles.itemContent}>\n {item.file.status === \"success\" && (\n <View style={styles.itemHeader}>\n <Text style={styles.fileName} numberOfLines={1}>\n {item.file.data.name}\n </Text>\n <Text style={styles.fileSize}>\n {getFileSizeDisplay(item.file.data.size)}\n </Text>\n </View>\n )}\n {item.file.status === \"error\" && (\n <Text style={styles.errorText}>{item.progress.error?.message}</Text>\n )}\n <View style={styles.progressWrapper}>\n <UploadProgress\n state={{\n status:\n item.progress.state === \"pending\"\n ? \"idle\"\n : item.progress.state === \"cancelled\"\n ? \"aborted\"\n : item.progress.state,\n progress: item.progress.progress,\n bytesUploaded: item.progress.uploadedBytes,\n totalBytes: item.progress.totalBytes,\n error: item.progress.error || null,\n result: item.result ?? null,\n }}\n />\n </View>\n </View>\n {showRemoveButton &&\n item.progress.state !== \"uploading\" &&\n item.progress.state !== \"pending\" && (\n <Pressable\n style={styles.removeButton}\n onPress={() => onRemove?.(item.id)}\n hitSlop={{ top: 8, right: 8, bottom: 8, left: 8 }}\n >\n <Text style={styles.removeButtonText}>✕</Text>\n </Pressable>\n )}\n </Pressable>\n );\n\n if (items.length === 0) {\n return (\n <View style={styles.emptyContainer}>\n <Text style={styles.emptyText}>No uploads</Text>\n </View>\n );\n }\n\n return (\n <View style={styles.container}>\n <View style={styles.headerRow}>\n <Text style={styles.headerText}>Uploads ({items.length})</Text>\n <Text style={styles.headerSubtext}>\n {items.filter((i) => i.progress.state === \"success\").length} complete\n </Text>\n </View>\n <FlatList\n scrollEnabled={false}\n data={items}\n renderItem={renderItem}\n keyExtractor={(item) => item.id}\n ItemSeparatorComponent={() => <View style={styles.separator} />}\n contentContainerStyle={styles.listContent}\n />\n </View>\n );\n}\n\n// Helper functions\nfunction getStatusColor(state: string): string {\n switch (state) {\n case \"success\":\n return \"#34C759\";\n case \"error\":\n case \"cancelled\":\n return \"#FF3B30\";\n case \"uploading\":\n case \"pending\":\n return \"#007AFF\";\n default:\n return \"#999999\";\n }\n}\n\nfunction getFileSizeDisplay(bytes: number): string {\n if (bytes === 0) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${Math.round((bytes / k ** i) * 10) / 10} ${sizes[i]}`;\n}\n\nconst styles = StyleSheet.create({\n container: {\n gap: 8,\n },\n headerRow: {\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n paddingHorizontal: 12,\n paddingVertical: 8,\n backgroundColor: \"#f9f9f9\",\n borderRadius: 4,\n },\n headerText: {\n fontSize: 16,\n fontWeight: \"600\",\n color: \"#333333\",\n },\n errorText: {\n fontSize: 14,\n color: \"#FF3B30\",\n },\n headerSubtext: {\n fontSize: 14,\n color: \"#666666\",\n },\n listContent: {\n gap: 8,\n },\n itemContainer: {\n flexDirection: \"row\",\n alignItems: \"center\",\n paddingVertical: 8,\n paddingHorizontal: 12,\n borderLeftWidth: 4,\n backgroundColor: \"#f5f5f5\",\n borderRadius: 4,\n gap: 8,\n },\n itemContent: {\n flex: 1,\n gap: 6,\n },\n itemHeader: {\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n },\n fileName: {\n fontSize: 14,\n fontWeight: \"500\",\n color: \"#333333\",\n flex: 1,\n },\n fileSize: {\n fontSize: 12,\n color: \"#999999\",\n marginLeft: 8,\n },\n progressWrapper: {\n marginTop: 2,\n },\n removeButton: {\n width: 32,\n height: 32,\n justifyContent: \"center\",\n alignItems: \"center\",\n borderRadius: 16,\n backgroundColor: \"#FFE5E5\",\n },\n removeButtonText: {\n fontSize: 16,\n fontWeight: \"600\",\n color: \"#FF3B30\",\n },\n separator: {\n height: 4,\n },\n emptyContainer: {\n paddingVertical: 24,\n paddingHorizontal: 12,\n backgroundColor: \"#f5f5f5\",\n borderRadius: 4,\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n emptyText: {\n fontSize: 14,\n color: \"#999999\",\n fontStyle: \"italic\",\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["initialState: UploadState","initialState","initialState: FlowUploadState","initialState","initialState: MultiUploadState","newItems: UploadItemState[]","result: FilePickResult","parts","getStatusColor","styles","styles","styles","styles"],"sources":["../src/hooks/uploadista-context.ts","../src/types/platform-types.ts","../src/hooks/use-uploadista-context.ts","../src/hooks/use-upload.ts","../src/hooks/use-camera-upload.ts","../src/hooks/use-file-upload.ts","../src/contexts/flow-manager-context.tsx","../src/hooks/use-flow-upload.ts","../src/hooks/use-multi-upload.ts","../src/hooks/use-gallery-upload.ts","../src/hooks/use-upload-metrics.ts","../src/utils/fileHelpers.ts","../src/utils/permissions.ts","../src/utils/uriHelpers.ts","../src/components/UploadProgress.tsx","../src/components/CameraUploadButton.tsx","../src/components/FileUploadButton.tsx","../src/components/GalleryUploadButton.tsx","../src/components/UploadList.tsx"],"sourcesContent":["import type { UploadistaEvent } from \"@uploadista/client-core\";\nimport { createContext } from \"react\";\nimport type { FileSystemProvider } from \"../types\";\nimport type { UseUploadistaClientReturn } from \"./use-uploadista-client\";\n\nexport interface UploadistaContextType extends UseUploadistaClientReturn {\n fileSystemProvider: FileSystemProvider;\n /**\n * Subscribe to events (used internally by hooks)\n * @internal\n */\n subscribeToEvents: (handler: (event: UploadistaEvent) => void) => () => void;\n}\n\nexport const UploadistaContext = createContext<\n UploadistaContextType | undefined\n>(undefined);\n","/**\n * Platform-specific type definitions for React Native\n *\n * React Native's Blob implementation differs from the browser's Blob API.\n * This file provides proper type definitions and guards for platform-specific behavior.\n */\n\n/**\n * BufferSource represents data that can be passed to Blob constructor\n * Includes both ArrayBuffer and typed arrays (Uint8Array, etc.)\n */\nexport type BufferSource = ArrayBuffer | ArrayBufferView;\n\n/**\n * React Native Blob constructor options\n * Extends standard BlobPropertyBag with platform-specific properties\n */\nexport interface ReactNativeBlobOptions {\n /** MIME type of the blob */\n type?: string;\n /** Platform-specific: file path for optimization (React Native only) */\n path?: string;\n}\n\n/**\n * React Native Blob constructor type\n * Unlike browser Blob, accepts BufferSource in the parts array\n */\nexport interface ReactNativeBlobConstructor {\n new (\n parts?: Array<BufferSource | Blob | string>,\n options?: ReactNativeBlobOptions,\n ): Blob;\n prototype: Blob;\n}\n\n/**\n * Type guard to check if a value is ArrayBuffer\n */\nexport function isArrayBuffer(value: unknown): value is ArrayBuffer {\n return value instanceof ArrayBuffer;\n}\n\n/**\n * Type guard to check if a value is ArrayBufferView (typed array)\n */\nexport function isArrayBufferView(value: unknown): value is ArrayBufferView {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"buffer\" in value &&\n value.buffer instanceof ArrayBuffer\n );\n}\n\n/**\n * Type guard to check if a value is BufferSource\n */\nexport function isBufferSource(value: unknown): value is BufferSource {\n return isArrayBuffer(value) || isArrayBufferView(value);\n}\n\n/**\n * Type guard to check if we're in React Native environment\n * (checks for global.navigator.product === 'ReactNative')\n */\nexport function isReactNativeEnvironment(): boolean {\n return (\n typeof global !== \"undefined\" &&\n typeof global.navigator !== \"undefined\" &&\n global.navigator.product === \"ReactNative\"\n );\n}\n\n/**\n * Create a Blob from BufferSource with proper typing for React Native\n *\n * This function handles the platform differences between browser and React Native Blob APIs.\n * React Native's Blob constructor accepts BufferSource directly, while browser Blob requires\n * conversion to Uint8Array first in some cases.\n *\n * @param data - ArrayBuffer or typed array to convert to Blob\n * @param options - Blob options including MIME type\n * @returns Platform-appropriate Blob instance\n *\n * @example\n * ```typescript\n * const arrayBuffer = await fileSystemProvider.readFile(uri);\n * const blob = createBlobFromBuffer(arrayBuffer, {\n * type: 'image/jpeg'\n * });\n * ```\n */\nexport function createBlobFromBuffer(\n data: BufferSource,\n options?: ReactNativeBlobOptions,\n): Blob {\n // Convert ArrayBuffer to Uint8Array for consistent handling\n const uint8Array = data instanceof ArrayBuffer ? new Uint8Array(data) : data;\n\n // In React Native, Blob constructor accepts BufferSource\n // Cast to ReactNativeBlobConstructor to use the correct signature\n const BlobConstructor = Blob as unknown as ReactNativeBlobConstructor;\n return new BlobConstructor([uint8Array], options);\n}\n","import { useContext } from \"react\";\nimport { UploadistaContext } from \"./uploadista-context\";\n\n/**\n * Hook to access the Uploadista client instance\n * Must be used within an UploadistaProvider\n * @throws Error if used outside of UploadistaProvider\n * @returns The Uploadista client and file system provider\n */\nexport function useUploadistaContext() {\n const context = useContext(UploadistaContext);\n\n if (!context) {\n throw new Error(\n \"useUploadistaClient must be used within an UploadistaProvider\",\n );\n }\n\n return context;\n}\n","import type {\n UploadistaUploadOptions,\n UploadMetrics,\n} from \"@uploadista/client-core\";\nimport {\n UploadManager,\n type UploadState,\n type UploadStatus,\n} from \"@uploadista/client-core\";\nimport type { UploadFile } from \"@uploadista/core/types\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { FilePickResult } from \"../types\";\nimport { createBlobFromBuffer } from \"../types/platform-types\";\nimport { useUploadistaContext } from \"./use-uploadista-context\";\n\n// Re-export types from core for convenience\nexport type { UploadState, UploadStatus };\n\nexport interface UseUploadOptions {\n /**\n * Upload metadata to attach to the file\n */\n metadata?: Record<string, string>;\n\n /**\n * Whether to defer the upload size calculation\n */\n uploadLengthDeferred?: boolean;\n\n /**\n * Manual upload size override\n */\n uploadSize?: number;\n\n /**\n * Called when upload progress updates\n */\n onProgress?: (\n uploadId: string,\n bytesUploaded: number,\n totalBytes: number | null,\n ) => void;\n\n /**\n * Called when a chunk completes\n */\n onChunkComplete?: (\n chunkSize: number,\n bytesAccepted: number,\n bytesTotal: number | null,\n ) => void;\n\n /**\n * Called when upload succeeds\n */\n onSuccess?: (result: UploadFile) => void;\n\n /**\n * Called when upload fails\n */\n onError?: (error: Error) => void;\n\n /**\n * Called when upload is aborted\n */\n onAbort?: () => void;\n\n /**\n * Custom retry logic\n */\n onShouldRetry?: (error: Error, retryAttempt: number) => boolean;\n}\n\nexport interface UseUploadReturn {\n /**\n * Current upload state\n */\n state: UploadState;\n\n /**\n * Start uploading a file from a file pick result\n */\n upload: (file: FilePickResult) => Promise<void>;\n\n /**\n * Abort the current upload\n */\n abort: () => void;\n\n /**\n * Reset the upload state to idle\n */\n reset: () => void;\n\n /**\n * Retry the last failed upload\n */\n retry: () => void;\n\n /**\n * Whether an upload is currently active\n */\n isUploading: boolean;\n\n /**\n * Whether the upload can be retried\n */\n canRetry: boolean;\n\n /**\n * Upload metrics and performance insights from the client\n */\n metrics: UploadMetrics;\n}\n\nconst initialState: UploadState = {\n status: \"idle\",\n progress: 0,\n bytesUploaded: 0,\n totalBytes: null,\n error: null,\n result: null,\n};\n\n/**\n * React Native hook for managing individual file uploads with full state management.\n * Provides upload progress tracking, error handling, abort functionality, and retry logic.\n *\n * Must be used within an UploadistaProvider.\n *\n * @param options - Upload configuration and event handlers\n * @returns Upload state and control methods\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const upload = useUpload({\n * onSuccess: (result) => console.log('Upload complete:', result),\n * onError: (error) => console.error('Upload failed:', error),\n * onProgress: (progress) => console.log('Progress:', progress + '%'),\n * });\n *\n * const handleFilePick = async () => {\n * const file = await pickFile();\n * if (file) await upload.upload(file);\n * };\n *\n * return (\n * <View>\n * <Button title=\"Pick File\" onPress={handleFilePick} />\n * {upload.isUploading && <Text>Progress: {upload.state.progress}%</Text>}\n * {upload.state.error && <Text>Error: {upload.state.error.message}</Text>}\n * {upload.canRetry && <Button title=\"Retry\" onPress={upload.retry} />}\n * <Button title=\"Abort\" onPress={upload.abort} disabled={!upload.isUploading} />\n * </View>\n * );\n * }\n * ```\n */\nexport function useUpload(options: UseUploadOptions = {}): UseUploadReturn {\n const { client, fileSystemProvider } = useUploadistaContext();\n const [state, setState] = useState<UploadState>(initialState);\n const managerRef = useRef<UploadManager | null>(null);\n const lastFileRef = useRef<FilePickResult | null>(null);\n\n // Create UploadManager instance\n useEffect(() => {\n // Create upload function that handles React Native file reading\n const uploadFn = async (input: unknown, opts: UploadistaUploadOptions) => {\n const file = input as FilePickResult;\n\n if (file.status === \"success\") {\n // Read file content from React Native file system\n const fileContent = await fileSystemProvider.readFile(file.data.uri);\n\n // Create a Blob from the file content using platform-aware utility\n const blob = createBlobFromBuffer(fileContent, {\n type: file.data.mimeType || \"application/octet-stream\",\n });\n\n // Upload the Blob\n return client.upload(blob, opts);\n }\n\n return Promise.resolve({ abort: () => {} });\n };\n\n managerRef.current = new UploadManager(\n uploadFn,\n {\n onStateChange: setState,\n onProgress: options.onProgress,\n onChunkComplete: options.onChunkComplete,\n onSuccess: options.onSuccess,\n onError: options.onError,\n onAbort: options.onAbort,\n },\n {\n metadata: options.metadata,\n uploadLengthDeferred: options.uploadLengthDeferred,\n uploadSize: options.uploadSize,\n onShouldRetry: options.onShouldRetry,\n },\n );\n\n return () => {\n managerRef.current?.cleanup();\n };\n }, [client, fileSystemProvider, options]);\n\n // Upload function - stores file reference for retry\n const upload = useCallback(async (file: FilePickResult) => {\n lastFileRef.current = file;\n await managerRef.current?.upload(file);\n }, []);\n\n // Abort function\n const abort = useCallback(() => {\n managerRef.current?.abort();\n }, []);\n\n // Reset function\n const reset = useCallback(() => {\n managerRef.current?.reset();\n lastFileRef.current = null;\n }, []);\n\n // Retry function\n const retry = useCallback(() => {\n if (lastFileRef.current && managerRef.current?.canRetry()) {\n managerRef.current.retry();\n }\n }, []);\n\n // Derive computed values from state\n const isUploading = state.status === \"uploading\";\n const canRetry = managerRef.current?.canRetry() ?? false;\n\n // Create metrics object that delegates to the upload client\n const metrics: UploadMetrics = {\n getInsights: () => client.getChunkingInsights(),\n exportMetrics: () => client.exportMetrics(),\n getNetworkMetrics: () => client.getNetworkMetrics(),\n getNetworkCondition: () => client.getNetworkCondition(),\n resetMetrics: () => client.resetMetrics(),\n };\n\n return {\n state,\n upload,\n abort,\n reset,\n retry,\n isUploading,\n canRetry,\n metrics,\n };\n}\n","import { useCallback } from \"react\";\nimport type { UseCameraUploadOptions } from \"../types\";\nimport { useUpload } from \"./use-upload\";\nimport { useUploadistaContext } from \"./use-uploadista-context\";\n\n/**\n * Hook for capturing photos and uploading them\n * Handles camera permissions and capture flow\n * @param options - Camera upload configuration\n * @returns Upload state and camera capture/upload function\n */\nexport function useCameraUpload(options?: UseCameraUploadOptions) {\n const { fileSystemProvider } = useUploadistaContext();\n const uploadHook = useUpload({\n metadata: options?.metadata,\n onSuccess: options?.onSuccess,\n onError: options?.onError,\n onProgress: options?.onProgress,\n });\n\n // Capture and upload photo\n const captureAndUpload = useCallback(async () => {\n try {\n // Capture photo with camera\n const photo = await fileSystemProvider.pickCamera(options?.cameraOptions);\n\n // Upload captured photo\n await uploadHook.upload(photo);\n } catch (error) {\n console.error(\"Camera capture error:\", error);\n }\n }, [fileSystemProvider, options?.cameraOptions, uploadHook]);\n\n return {\n ...uploadHook,\n captureAndUpload,\n };\n}\n","import { useCallback } from \"react\";\nimport type { UseFileUploadOptions } from \"../types\";\nimport { useUpload } from \"./use-upload\";\nimport { useUploadistaContext } from \"./use-uploadista-context\";\n\n/**\n * Hook for selecting and uploading generic files (documents, etc.)\n * @param options - File upload configuration\n * @returns Upload state and file picker/upload function\n */\nexport function useFileUpload(options?: UseFileUploadOptions) {\n const { fileSystemProvider } = useUploadistaContext();\n const uploadHook = useUpload({\n metadata: options?.metadata,\n onSuccess: options?.onSuccess,\n onError: options?.onError,\n onProgress: options?.onProgress,\n });\n\n // Pick and upload file\n const pickAndUpload = useCallback(async () => {\n try {\n // Pick file\n const file = await fileSystemProvider.pickDocument({\n allowedTypes: options?.allowedTypes,\n });\n\n // Upload file\n await uploadHook.upload(file);\n } catch (error) {\n console.error(\"File selection error:\", error);\n throw error;\n }\n }, [fileSystemProvider, options?.allowedTypes, uploadHook]);\n\n return {\n ...uploadHook,\n pickAndUpload,\n };\n}\n","import type { UploadistaEvent } from \"@uploadista/client-core\";\nimport {\n FlowManager,\n type FlowManagerCallbacks,\n type FlowUploadOptions,\n} from \"@uploadista/client-core\";\nimport { EventType, type FlowEvent } from \"@uploadista/core/flow\";\nimport { UploadEventType } from \"@uploadista/core/types\";\nimport type { ReactNode } from \"react\";\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n} from \"react\";\nimport { useUploadistaContext } from \"../hooks/use-uploadista-context\";\nimport type { ReactNativeUploadInput } from \"../types\";\n\n/**\n * Type guard to check if an event is a flow event\n */\nfunction isFlowEvent(event: UploadistaEvent): event is FlowEvent {\n const flowEvent = event as FlowEvent;\n return (\n flowEvent.eventType === EventType.FlowStart ||\n flowEvent.eventType === EventType.FlowEnd ||\n flowEvent.eventType === EventType.FlowError ||\n flowEvent.eventType === EventType.NodeStart ||\n flowEvent.eventType === EventType.NodeEnd ||\n flowEvent.eventType === EventType.NodePause ||\n flowEvent.eventType === EventType.NodeResume ||\n flowEvent.eventType === EventType.NodeError\n );\n}\n\n/**\n * Internal manager registry entry with ref counting\n */\ninterface ManagerEntry {\n manager: FlowManager<unknown>;\n refCount: number;\n flowId: string;\n}\n\n/**\n * Context value providing access to flow managers\n */\ninterface FlowManagerContextValue {\n /**\n * Get or create a flow manager for the given flow ID.\n * Increments ref count - must call releaseManager when done.\n *\n * @param flowId - Unique identifier for the flow\n * @param callbacks - Callbacks for state changes and lifecycle events\n * @param options - Flow configuration options\n * @returns FlowManager instance\n */\n getManager: (\n flowId: string,\n callbacks: FlowManagerCallbacks,\n options: FlowUploadOptions,\n ) => FlowManager<unknown>;\n\n /**\n * Release a flow manager reference.\n * Decrements ref count and cleans up when reaching zero.\n *\n * @param flowId - Unique identifier for the flow to release\n */\n releaseManager: (flowId: string) => void;\n}\n\nconst FlowManagerContext = createContext<FlowManagerContextValue | undefined>(\n undefined,\n);\n\n/**\n * Props for FlowManagerProvider\n */\ninterface FlowManagerProviderProps {\n children: ReactNode;\n}\n\n/**\n * Provider that manages FlowManager instances with ref counting and event routing.\n * Ensures managers persist across component re-renders and are only cleaned up\n * when all consuming components unmount.\n *\n * This provider should be nested inside UploadistaProvider to access the upload client\n * and event subscription system.\n *\n * @example\n * ```tsx\n * <UploadistaProvider baseUrl=\"https://api.example.com\" storageId=\"default\">\n * <FlowManagerProvider>\n * <App />\n * </FlowManagerProvider>\n * </UploadistaProvider>\n * ```\n */\nexport function FlowManagerProvider({ children }: FlowManagerProviderProps) {\n const { client, subscribeToEvents } = useUploadistaContext();\n const managersRef = useRef(new Map<string, ManagerEntry>());\n\n // Subscribe to all events and route to appropriate managers\n useEffect(() => {\n const unsubscribe = subscribeToEvents((event: UploadistaEvent) => {\n // Route flow events to all managers (they filter by jobId internally)\n if (isFlowEvent(event)) {\n for (const entry of managersRef.current.values()) {\n entry.manager.handleFlowEvent(event);\n }\n return;\n }\n\n // Route upload progress events to all managers\n if (\n \"type\" in event &&\n event.type === UploadEventType.UPLOAD_PROGRESS &&\n \"data\" in event\n ) {\n for (const entry of managersRef.current.values()) {\n entry.manager.handleUploadProgress(\n event.data.id,\n event.data.progress,\n event.data.total,\n );\n }\n }\n });\n\n return unsubscribe;\n }, [subscribeToEvents]);\n\n const getManager = useCallback(\n (\n flowId: string,\n callbacks: FlowManagerCallbacks,\n options: FlowUploadOptions,\n ): FlowManager<unknown> => {\n const existing = managersRef.current.get(flowId);\n\n if (existing) {\n // Increment ref count for existing manager\n existing.refCount++;\n return existing.manager;\n }\n\n // Create new manager using client from hook scope\n const manager = new FlowManager<ReactNativeUploadInput>(\n client.uploadWithFlow,\n callbacks,\n options,\n );\n\n managersRef.current.set(flowId, {\n manager,\n refCount: 1,\n flowId,\n });\n\n return manager;\n },\n [client],\n );\n\n const releaseManager = useCallback((flowId: string) => {\n const existing = managersRef.current.get(flowId);\n if (!existing) return;\n\n existing.refCount--;\n\n // Clean up when no more refs\n if (existing.refCount <= 0) {\n existing.manager.cleanup();\n managersRef.current.delete(flowId);\n }\n }, []);\n\n return (\n <FlowManagerContext.Provider value={{ getManager, releaseManager }}>\n {children}\n </FlowManagerContext.Provider>\n );\n}\n\n/**\n * Hook to access the FlowManager context.\n * Must be used within a FlowManagerProvider.\n *\n * @returns FlowManager context value with getManager and releaseManager functions\n * @throws Error if used outside of FlowManagerProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { getManager, releaseManager } = useFlowManagerContext();\n * // Use to create managers...\n * }\n * ```\n */\nexport function useFlowManagerContext(): FlowManagerContextValue {\n const context = useContext(FlowManagerContext);\n\n if (context === undefined) {\n throw new Error(\n \"useFlowManagerContext must be used within a FlowManagerProvider. \" +\n \"Make sure to wrap your component tree with <FlowManagerProvider>.\",\n );\n }\n\n return context;\n}\n","import type {\n FlowManager,\n FlowUploadState,\n FlowUploadStatus,\n} from \"@uploadista/client-core\";\nimport type { TypedOutput } from \"@uploadista/core/flow\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useFlowManagerContext } from \"../contexts/flow-manager-context\";\nimport type { FilePickResult, UseFlowUploadOptions } from \"../types\";\nimport { createBlobFromBuffer } from \"../types/platform-types\";\nimport { useUploadistaContext } from \"./use-uploadista-context\";\n\n// Re-export types from core for convenience\nexport type { FlowUploadState, FlowUploadStatus };\n\nconst initialState: FlowUploadState = {\n status: \"idle\",\n progress: 0,\n bytesUploaded: 0,\n totalBytes: null,\n error: null,\n jobId: null,\n flowStarted: false,\n currentNodeName: null,\n currentNodeType: null,\n flowOutputs: null,\n};\n\n/**\n * Hook for uploading files through a flow pipeline with full state management.\n * Provides upload progress tracking, flow execution monitoring, error handling, and abort functionality.\n *\n * Must be used within FlowManagerProvider (which must be within UploadistaProvider).\n * Flow events are automatically routed by the provider to the appropriate manager.\n *\n * @param options - Flow upload configuration\n * @returns Flow upload state and control methods\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const flowUpload = useFlowUpload({\n * flowId: 'image-processing-flow',\n * storageId: 'my-storage',\n * onSuccess: (result) => console.log('Flow complete:', result),\n * onError: (error) => console.error('Flow failed:', error),\n * onProgress: (progress) => console.log('Progress:', progress + '%'),\n * });\n *\n * const handlePickFile = async () => {\n * const file = await fileSystemProvider.pickDocument();\n * if (file) {\n * await flowUpload.upload(file);\n * }\n * };\n *\n * return (\n * <View>\n * <Button title=\"Pick File\" onPress={handlePickFile} />\n * {flowUpload.isUploading && <Text>Progress: {flowUpload.state.progress}%</Text>}\n * {flowUpload.state.jobId && <Text>Job ID: {flowUpload.state.jobId}</Text>}\n * {flowUpload.state.error && <Text>Error: {flowUpload.state.error.message}</Text>}\n * <Button title=\"Abort\" onPress={flowUpload.abort} disabled={!flowUpload.isActive} />\n * </View>\n * );\n * }\n * ```\n */\nexport function useFlowUpload(options: UseFlowUploadOptions) {\n const { getManager, releaseManager } = useFlowManagerContext();\n const { fileSystemProvider } = useUploadistaContext();\n const [state, setState] = useState<FlowUploadState>(initialState);\n const managerRef = useRef<FlowManager<unknown> | null>(null);\n const lastFileRef = useRef<FilePickResult | null>(null);\n\n // Store callbacks in refs so they can be updated without recreating the manager\n const callbacksRef = useRef(options);\n\n // Update refs on every render to capture latest callbacks\n useEffect(() => {\n callbacksRef.current = options;\n });\n\n // Get or create manager from context when component mounts\n // Manager lifecycle is now handled by FlowManagerProvider\n useEffect(() => {\n const flowId = options.flowId;\n\n // Create stable callback wrappers that call the latest callbacks via refs\n const stableCallbacks = {\n onStateChange: setState,\n onProgress: (\n _uploadId: string,\n bytesUploaded: number,\n totalBytes: number | null,\n ) => {\n if (callbacksRef.current.onProgress) {\n const progress = totalBytes\n ? Math.round((bytesUploaded / totalBytes) * 100)\n : 0;\n callbacksRef.current.onProgress(progress, bytesUploaded, totalBytes);\n }\n },\n onChunkComplete: (\n chunkSize: number,\n bytesAccepted: number,\n bytesTotal: number | null,\n ) => {\n callbacksRef.current.onChunkComplete?.(\n chunkSize,\n bytesAccepted,\n bytesTotal,\n );\n },\n onFlowComplete: (outputs: TypedOutput[]) => {\n callbacksRef.current.onFlowComplete?.(outputs);\n },\n onSuccess: (outputs: TypedOutput[]) => {\n callbacksRef.current.onSuccess?.(outputs);\n },\n onError: (error: Error) => {\n callbacksRef.current.onError?.(error);\n },\n onAbort: () => {\n // onAbort is not exposed in the public API\n },\n };\n\n // Get manager from context (creates if doesn't exist, increments ref count)\n managerRef.current = getManager(flowId, stableCallbacks, {\n flowConfig: {\n flowId: options.flowId,\n storageId: options.storageId,\n outputNodeId: options.outputNodeId,\n metadata: options.metadata as Record<string, string> | undefined,\n },\n onChunkComplete: options.onChunkComplete,\n onSuccess: options.onSuccess,\n onError: options.onError,\n });\n\n // Release manager when component unmounts or flowId changes\n return () => {\n releaseManager(flowId);\n managerRef.current = null;\n };\n }, [\n options.flowId,\n options.storageId,\n options.outputNodeId,\n getManager,\n releaseManager,\n ]);\n\n const upload = useCallback(\n async (file: FilePickResult) => {\n // Handle cancelled picker\n if (file.status === \"cancelled\") {\n return;\n }\n\n // Handle picker error\n if (file.status === \"error\") {\n options.onError?.(file.error);\n return;\n }\n\n lastFileRef.current = file;\n\n try {\n // Read file content\n const fileContent = await fileSystemProvider.readFile(file.data.uri);\n\n // Create a Blob from the file content using platform-aware utility\n // Handles differences between React Native and browser Blob APIs\n const blob = createBlobFromBuffer(fileContent, {\n type: file.data.mimeType || \"application/octet-stream\",\n });\n\n // Start the upload using the manager\n await managerRef.current?.upload(blob);\n } catch (error) {\n options.onError?.(error as Error);\n }\n },\n [fileSystemProvider, options],\n );\n\n const reset = useCallback(() => {\n managerRef.current?.reset();\n lastFileRef.current = null;\n }, []);\n\n const abort = useCallback(() => {\n managerRef.current?.abort();\n }, []);\n\n const retry = useCallback(() => {\n if (\n lastFileRef.current &&\n (state.status === \"error\" || state.status === \"aborted\")\n ) {\n upload(lastFileRef.current);\n }\n }, [upload, state.status]);\n\n // Derive computed values from state (reactive to state changes)\n const isActive =\n state.status === \"uploading\" || state.status === \"processing\";\n const canRetry =\n (state.status === \"error\" || state.status === \"aborted\") &&\n lastFileRef.current !== null;\n\n return {\n state,\n upload,\n abort,\n reset,\n retry,\n isActive,\n canRetry,\n };\n}\n","import type { UploadFile } from \"@uploadista/core/types\";\nimport { useCallback, useRef, useState } from \"react\";\nimport type { FilePickResult, UseMultiUploadOptions } from \"../types\";\nimport { useUploadistaContext } from \"./use-uploadista-context\";\n\nexport interface UploadItemState {\n id: string;\n file: Extract<FilePickResult, { status: \"success\" }>;\n status: \"idle\" | \"uploading\" | \"success\" | \"error\" | \"aborted\";\n progress: number;\n bytesUploaded: number;\n totalBytes: number;\n error: Error | null;\n result: UploadFile | null;\n}\n\nexport interface MultiUploadState {\n items: UploadItemState[];\n totalProgress: number;\n totalUploaded: number;\n totalBytes: number;\n activeCount: number;\n completedCount: number;\n failedCount: number;\n}\n\nconst initialState: MultiUploadState = {\n items: [],\n totalProgress: 0,\n totalUploaded: 0,\n totalBytes: 0,\n activeCount: 0,\n completedCount: 0,\n failedCount: 0,\n};\n\n/**\n * Hook for managing multiple concurrent file uploads with progress tracking.\n * Each file is uploaded independently using the core upload client.\n *\n * Must be used within an UploadistaProvider.\n *\n * @param options - Multi-upload configuration options\n * @returns Multi-upload state and control methods\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const multiUpload = useMultiUpload({\n * maxConcurrent: 3,\n * onSuccess: (result) => console.log('File uploaded:', result),\n * onError: (error) => console.error('Upload failed:', error),\n * });\n *\n * const handlePickFiles = async () => {\n * const files = await fileSystemProvider.pickImage({ allowMultiple: true });\n * multiUpload.addFiles(files);\n * await multiUpload.startUploads();\n * };\n *\n * return (\n * <View>\n * <Button title=\"Pick Files\" onPress={handlePickFiles} />\n * <Text>Progress: {multiUpload.state.totalProgress}%</Text>\n * <Text>Active: {multiUpload.state.activeCount}</Text>\n * <Text>Completed: {multiUpload.state.completedCount}/{multiUpload.state.items.length}</Text>\n * </View>\n * );\n * }\n * ```\n */\nexport function useMultiUpload(options: UseMultiUploadOptions = {}) {\n const { client } = useUploadistaContext();\n const [state, setState] = useState<MultiUploadState>(initialState);\n const abortControllersRef = useRef<Map<string, { abort: () => void }>>(\n new Map(),\n );\n const nextIdRef = useRef(0);\n // Use ref to track items synchronously\n const itemsRef = useRef<UploadItemState[]>([]);\n\n const generateId = useCallback(() => {\n return `upload-${Date.now()}-${nextIdRef.current++}`;\n }, []);\n\n const updateAggregateStats = useCallback((items: UploadItemState[]) => {\n const totalBytes = items.reduce((sum, item) => sum + item.totalBytes, 0);\n const totalUploaded = items.reduce(\n (sum, item) => sum + item.bytesUploaded,\n 0,\n );\n const totalProgress =\n totalBytes > 0 ? Math.round((totalUploaded / totalBytes) * 100) : 0;\n const activeCount = items.filter(\n (item) => item.status === \"uploading\",\n ).length;\n const completedCount = items.filter(\n (item) => item.status === \"success\",\n ).length;\n const failedCount = items.filter((item) => item.status === \"error\").length;\n\n // Update ref synchronously\n itemsRef.current = items;\n\n setState((prev) => ({\n ...prev,\n items,\n totalProgress,\n totalUploaded,\n totalBytes,\n activeCount,\n completedCount,\n failedCount,\n }));\n }, []);\n\n const addFiles = useCallback(\n (files: FilePickResult[]) => {\n // Filter out cancelled and error results, only keep successful picks\n const successfulFiles = files.filter(\n (file): file is Extract<FilePickResult, { status: \"success\" }> =>\n file.status === \"success\",\n );\n\n const newItems: UploadItemState[] = successfulFiles.map((file) => ({\n id: generateId(),\n file,\n status: \"idle\" as const,\n progress: 0,\n bytesUploaded: 0,\n totalBytes: file.data.size,\n error: null,\n result: null,\n }));\n\n // Update ref synchronously\n const updatedItems = [...itemsRef.current, ...newItems];\n itemsRef.current = updatedItems;\n\n setState((prev) => {\n const totalBytes = updatedItems.reduce(\n (sum, item) => sum + item.totalBytes,\n 0,\n );\n return {\n ...prev,\n items: updatedItems,\n totalBytes,\n };\n });\n\n return newItems.map((item) => item.id);\n },\n [generateId],\n );\n\n const uploadSingleItem = useCallback(\n async (item: UploadItemState) => {\n try {\n console.log(\"Uploading item:\", item.file.data.name);\n // Update status to uploading\n const updatedItems = itemsRef.current.map((i) =>\n i.id === item.id ? { ...i, status: \"uploading\" as const } : i,\n );\n updateAggregateStats(updatedItems);\n\n // Convert file URI to Blob using fetch (React Native compatible)\n // React Native's Blob doesn't support ArrayBuffer/Uint8Array constructor\n const response = await fetch(item.file.data.uri);\n const blob = await response.blob();\n\n // Override blob type if we have mimeType from picker\n const uploadInput = item.file.data.mimeType\n ? new Blob([blob], {\n type: item.file.data.mimeType,\n lastModified: Date.now(),\n })\n : blob;\n\n // Start upload using the client\n console.log(\"Uploading input:\", uploadInput);\n const uploadPromise = client.upload(uploadInput, {\n metadata: options.metadata,\n\n onProgress: (\n _uploadId: string,\n bytesUploaded: number,\n totalBytes: number | null,\n ) => {\n const progress = totalBytes\n ? Math.round((bytesUploaded / totalBytes) * 100)\n : 0;\n\n const updatedItems = itemsRef.current.map((i) =>\n i.id === item.id\n ? {\n ...i,\n progress,\n bytesUploaded,\n totalBytes: totalBytes || i.totalBytes,\n }\n : i,\n );\n updateAggregateStats(updatedItems);\n },\n\n onSuccess: (result: UploadFile) => {\n const updatedItems = itemsRef.current.map((i) =>\n i.id === item.id\n ? {\n ...i,\n status: \"success\" as const,\n progress: 100,\n result,\n bytesUploaded: result.size || i.totalBytes,\n }\n : i,\n );\n updateAggregateStats(updatedItems);\n\n options.onSuccess?.(result);\n abortControllersRef.current.delete(item.id);\n },\n\n onError: (error: Error) => {\n const updatedItems = itemsRef.current.map((i) =>\n i.id === item.id ? { ...i, status: \"error\" as const, error } : i,\n );\n updateAggregateStats(updatedItems);\n\n options.onError?.(error);\n abortControllersRef.current.delete(item.id);\n },\n });\n\n // Store abort controller\n const controller = await uploadPromise;\n abortControllersRef.current.set(item.id, controller);\n } catch (error) {\n console.error(\"Error uploading item:\", error);\n const updatedItems = itemsRef.current.map((i) =>\n i.id === item.id\n ? {\n ...i,\n status: \"error\" as const,\n error: error as Error,\n }\n : i,\n );\n updateAggregateStats(updatedItems);\n\n options.onError?.(error as Error);\n abortControllersRef.current.delete(item.id);\n }\n },\n [client, options, updateAggregateStats],\n );\n\n const startUploads = useCallback(\n async (itemIds?: string[]) => {\n const maxConcurrent = options.maxConcurrent || 3;\n\n // Get items from ref (synchronous access to latest items)\n const itemsToUpload = itemIds\n ? itemsRef.current.filter(\n (item) => itemIds.includes(item.id) && item.status === \"idle\",\n )\n : itemsRef.current.filter((item) => item.status === \"idle\");\n\n console.log(\"Items to upload:\", itemsToUpload.length, itemsToUpload);\n\n // Process items in batches\n for (let i = 0; i < itemsToUpload.length; i += maxConcurrent) {\n const batch = itemsToUpload.slice(i, i + maxConcurrent);\n await Promise.all(batch.map((item) => uploadSingleItem(item)));\n }\n },\n [options.maxConcurrent, uploadSingleItem],\n );\n\n const removeItem = useCallback(\n (id: string) => {\n const controller = abortControllersRef.current.get(id);\n if (controller) {\n controller.abort();\n abortControllersRef.current.delete(id);\n }\n\n const updatedItems = itemsRef.current.filter((item) => item.id !== id);\n updateAggregateStats(updatedItems);\n },\n [updateAggregateStats],\n );\n\n const abortItem = useCallback(\n (id: string) => {\n const controller = abortControllersRef.current.get(id);\n if (controller) {\n controller.abort();\n abortControllersRef.current.delete(id);\n }\n\n const updatedItems = itemsRef.current.map((item) =>\n item.id === id ? { ...item, status: \"aborted\" as const } : item,\n );\n updateAggregateStats(updatedItems);\n },\n [updateAggregateStats],\n );\n\n const clear = useCallback(() => {\n // Abort all active uploads\n abortControllersRef.current.forEach((controller) => {\n controller.abort();\n });\n abortControllersRef.current.clear();\n\n // Clear ref\n itemsRef.current = [];\n\n setState(initialState);\n }, []);\n\n const retryItem = useCallback(\n async (id: string) => {\n const item = itemsRef.current.find((i) => i.id === id);\n if (item && (item.status === \"error\" || item.status === \"aborted\")) {\n // Reset item status to idle\n const updatedItems = itemsRef.current.map((i) =>\n i.id === id\n ? {\n ...i,\n status: \"idle\" as const,\n progress: 0,\n bytesUploaded: 0,\n error: null,\n }\n : i,\n );\n updateAggregateStats(updatedItems);\n\n // Upload it (get the reset item from the updated items)\n const resetItem = itemsRef.current.find((i) => i.id === id);\n if (resetItem) {\n await uploadSingleItem(resetItem);\n }\n }\n },\n [uploadSingleItem, updateAggregateStats],\n );\n\n return {\n state,\n addFiles,\n startUploads,\n removeItem,\n abortItem,\n retryItem,\n clear,\n };\n}\n","import { useCallback } from \"react\";\nimport type { FilePickResult, UseGalleryUploadOptions } from \"../types\";\nimport { useMultiUpload } from \"./use-multi-upload\";\nimport { useUploadistaContext } from \"./use-uploadista-context\";\n\n/**\n * Hook for selecting and uploading photos/videos from gallery\n * Handles batch selection and concurrent uploads\n * @param options - Gallery upload configuration\n * @returns Upload state and gallery selection/upload function\n */\nexport function useGalleryUpload(options?: UseGalleryUploadOptions) {\n const { fileSystemProvider } = useUploadistaContext();\n const uploadHook = useMultiUpload({\n maxConcurrent: 3,\n metadata: options?.metadata,\n onSuccess: options?.onSuccess,\n onError: options?.onError,\n });\n\n // Select and upload media from gallery\n const selectAndUpload = useCallback(async () => {\n let result: FilePickResult;\n\n // Select appropriate media type\n if (options?.mediaType === \"video\") {\n result = await fileSystemProvider.pickVideo({\n allowMultiple: options?.allowMultiple ?? true,\n });\n } else if (options?.mediaType === \"photo\") {\n result = await fileSystemProvider.pickImage({\n allowMultiple: options?.allowMultiple ?? true,\n });\n } else {\n // For 'mixed' or default, use pickImage first (can be extended to support both)\n result = await fileSystemProvider.pickImage({\n allowMultiple: options?.allowMultiple ?? true,\n });\n }\n\n // Handle cancelled picker\n if (result.status === \"cancelled\") {\n return [];\n }\n\n // Handle picker error\n if (result.status === \"error\") {\n console.error(\"Gallery selection error:\", result.error);\n options?.onError?.(result.error);\n return [];\n }\n\n // Success - add file and start upload\n const itemIds = uploadHook.addFiles([result]);\n await uploadHook.startUploads(itemIds);\n\n return itemIds;\n }, [\n fileSystemProvider,\n options?.allowMultiple,\n options?.mediaType,\n options?.onError,\n uploadHook,\n ]);\n\n return {\n ...uploadHook,\n selectAndUpload,\n };\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport type { UploadMetrics } from \"../types\";\n\n/**\n * Hook for tracking upload performance metrics\n * @returns Metrics object and methods to track uploads\n */\nexport function useUploadMetrics() {\n const startTimeRef = useRef<number | null>(null);\n const startBytesRef = useRef<number>(0);\n const peakSpeedRef = useRef<number>(0);\n\n const [metrics, setMetrics] = useState<UploadMetrics>({\n totalBytes: 0,\n durationMs: 0,\n avgSpeed: 0,\n peakSpeed: 0,\n retries: 0,\n });\n\n // Start tracking\n const start = useCallback(() => {\n startTimeRef.current = Date.now();\n startBytesRef.current = 0;\n peakSpeedRef.current = 0;\n }, []);\n\n // Update metrics based on current progress\n const update = useCallback(\n (uploadedBytes: number, _totalBytes: number, currentRetries = 0) => {\n if (!startTimeRef.current) {\n return;\n }\n\n const now = Date.now();\n const durationMs = now - startTimeRef.current;\n const speed = durationMs > 0 ? (uploadedBytes / durationMs) * 1000 : 0;\n\n if (speed > peakSpeedRef.current) {\n peakSpeedRef.current = speed;\n }\n\n setMetrics({\n totalBytes: uploadedBytes,\n durationMs,\n avgSpeed: durationMs > 0 ? (uploadedBytes / durationMs) * 1000 : 0,\n peakSpeed: peakSpeedRef.current,\n retries: currentRetries,\n });\n },\n [],\n );\n\n // End tracking and return final metrics\n const end = useCallback(() => {\n const finalMetrics = metrics;\n startTimeRef.current = null;\n return finalMetrics;\n }, [metrics]);\n\n // Reset metrics\n const reset = useCallback(() => {\n startTimeRef.current = null;\n startBytesRef.current = 0;\n peakSpeedRef.current = 0;\n setMetrics({\n totalBytes: 0,\n durationMs: 0,\n avgSpeed: 0,\n peakSpeed: 0,\n retries: 0,\n });\n }, []);\n\n return {\n metrics,\n start,\n update,\n end,\n reset,\n };\n}\n","/**\n * File utility functions for React Native uploads\n */\n\n/**\n * Format file size to human readable string\n * @param bytes - Size in bytes\n * @returns Formatted size string (e.g., \"1.5 MB\")\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return \"0 Bytes\";\n\n const k = 1024;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${Math.round((bytes / k ** i) * 100) / 100} ${sizes[i]}`;\n}\n\n/**\n * Get MIME type from file name\n * @param fileName - File name with extension\n * @returns MIME type or 'application/octet-stream' as fallback\n */\nexport function getMimeTypeFromFileName(fileName: string): string {\n const mimeTypes: Record<string, string> = {\n // Images\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".gif\": \"image/gif\",\n \".bmp\": \"image/bmp\",\n \".webp\": \"image/webp\",\n \".svg\": \"image/svg+xml\",\n\n // Videos\n \".mp4\": \"video/mp4\",\n \".avi\": \"video/x-msvideo\",\n \".mov\": \"video/quicktime\",\n \".wmv\": \"video/x-ms-wmv\",\n \".flv\": \"video/x-flv\",\n \".mkv\": \"video/x-matroska\",\n \".webm\": \"video/webm\",\n\n // Audio\n \".mp3\": \"audio/mpeg\",\n \".wav\": \"audio/wav\",\n \".aac\": \"audio/aac\",\n \".flac\": \"audio/flac\",\n \".m4a\": \"audio/mp4\",\n\n // Documents\n \".pdf\": \"application/pdf\",\n \".doc\": \"application/msword\",\n \".docx\":\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \".xls\": \"application/vnd.ms-excel\",\n \".xlsx\":\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \".ppt\": \"application/vnd.ms-powerpoint\",\n \".pptx\":\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \".txt\": \"text/plain\",\n \".csv\": \"text/csv\",\n \".json\": \"application/json\",\n \".xml\": \"application/xml\",\n \".zip\": \"application/zip\",\n };\n\n const ext = fileName.toLowerCase().slice(fileName.lastIndexOf(\".\"));\n return mimeTypes[ext] || \"application/octet-stream\";\n}\n\n/**\n * Check if file type is allowed\n * @param fileName - File name to check\n * @param allowedTypes - Array of allowed MIME types (e.g., ['image/jpeg', 'image/png'])\n * @returns True if file type is allowed\n */\nexport function isFileTypeAllowed(\n fileName: string,\n allowedTypes: string[],\n): boolean {\n if (!allowedTypes || allowedTypes.length === 0) {\n return true;\n }\n\n const mimeType = getMimeTypeFromFileName(fileName);\n return allowedTypes.some((allowed) => {\n if (allowed.endsWith(\"/*\")) {\n // Handle wildcard patterns like 'image/*'\n const [type] = allowed.split(\"/\");\n return mimeType.startsWith(`${type}/`);\n }\n return allowed === mimeType;\n });\n}\n\n/**\n * Check if file size is within limits\n * @param fileSize - File size in bytes\n * @param maxSize - Maximum allowed size in bytes (optional)\n * @param minSize - Minimum allowed size in bytes (optional)\n * @returns True if file size is within limits\n */\nexport function isFileSizeValid(\n fileSize: number,\n maxSize?: number,\n minSize?: number,\n): boolean {\n if (maxSize !== undefined && fileSize > maxSize) {\n return false;\n }\n\n if (minSize !== undefined && fileSize < minSize) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Get file extension\n * @param fileName - File name\n * @returns File extension without dot (e.g., 'pdf' for 'document.pdf')\n */\nexport function getFileExtension(fileName: string): string {\n const lastDot = fileName.lastIndexOf(\".\");\n if (lastDot === -1) return \"\";\n return fileName.slice(lastDot + 1).toLowerCase();\n}\n\n/**\n * Get file name without extension\n * @param fileName - File name\n * @returns File name without extension\n */\nexport function getFileNameWithoutExtension(fileName: string): string {\n const lastDot = fileName.lastIndexOf(\".\");\n if (lastDot === -1) return fileName;\n return fileName.slice(0, lastDot);\n}\n\n/**\n * Check if file is an image\n * @param fileName - File name\n * @returns True if file is an image\n */\nexport function isImageFile(fileName: string): boolean {\n const imageExtensions = [\n \".jpg\",\n \".jpeg\",\n \".png\",\n \".gif\",\n \".bmp\",\n \".webp\",\n \".svg\",\n ];\n const ext = fileName.toLowerCase().slice(fileName.lastIndexOf(\".\"));\n return imageExtensions.includes(ext);\n}\n\n/**\n * Check if file is a video\n * @param fileName - File name\n * @returns True if file is a video\n */\nexport function isVideoFile(fileName: string): boolean {\n const videoExtensions = [\n \".mp4\",\n \".avi\",\n \".mov\",\n \".wmv\",\n \".flv\",\n \".mkv\",\n \".webm\",\n ];\n const ext = fileName.toLowerCase().slice(fileName.lastIndexOf(\".\"));\n return videoExtensions.includes(ext);\n}\n\n/**\n * Check if file is a document\n * @param fileName - File name\n * @returns True if file is a document\n */\nexport function isDocumentFile(fileName: string): boolean {\n const docExtensions = [\n \".pdf\",\n \".doc\",\n \".docx\",\n \".xls\",\n \".xlsx\",\n \".ppt\",\n \".pptx\",\n \".txt\",\n \".csv\",\n ];\n const ext = fileName.toLowerCase().slice(fileName.lastIndexOf(\".\"));\n return docExtensions.includes(ext);\n}\n","/**\n * Permission utility functions for React Native uploads\n * Handles camera, gallery, and file access permissions\n */\n\n/**\n * Permission types\n */\nexport enum PermissionType {\n CAMERA = \"CAMERA\",\n PHOTO_LIBRARY = \"PHOTO_LIBRARY\",\n WRITE_STORAGE = \"WRITE_STORAGE\",\n READ_STORAGE = \"READ_STORAGE\",\n}\n\n/**\n * Permission status\n */\nexport enum PermissionStatus {\n GRANTED = \"granted\",\n DENIED = \"denied\",\n NOT_DETERMINED = \"not_determined\",\n RESTRICTED = \"restricted\",\n}\n\n/**\n * Request camera permission\n * This is primarily used to check if we should attempt camera operations\n * Actual permission requests are handled by the file system providers\n *\n * @returns Promise resolving to true if permission is granted or already granted\n */\nexport async function requestCameraPermission(): Promise<boolean> {\n try {\n // Permission requests are handled by the file system provider implementations\n // This function serves as a placeholder for app-level permission handling\n console.log(\n \"Camera permission requested (handled by file system provider)\",\n );\n return true;\n } catch (error) {\n console.error(\"Failed to request camera permission:\", error);\n return false;\n }\n}\n\n/**\n * Request photo library permission\n * @returns Promise resolving to true if permission is granted\n */\nexport async function requestPhotoLibraryPermission(): Promise<boolean> {\n try {\n console.log(\n \"Photo library permission requested (handled by file system provider)\",\n );\n return true;\n } catch (error) {\n console.error(\"Failed to request photo library permission:\", error);\n return false;\n }\n}\n\n/**\n * Request storage read permission\n * @returns Promise resolving to true if permission is granted\n */\nexport async function requestStorageReadPermission(): Promise<boolean> {\n try {\n console.log(\n \"Storage read permission requested (handled by file system provider)\",\n );\n return true;\n } catch (error) {\n console.error(\"Failed to request storage read permission:\", error);\n return false;\n }\n}\n\n/**\n * Request storage write permission\n * @returns Promise resolving to true if permission is granted\n */\nexport async function requestStorageWritePermission(): Promise<boolean> {\n try {\n console.log(\n \"Storage write permission requested (handled by file system provider)\",\n );\n return true;\n } catch (error) {\n console.error(\"Failed to request storage write permission:\", error);\n return false;\n }\n}\n\n/**\n * Request multiple permissions at once\n * @param permissions - Array of permission types to request\n * @returns Promise resolving to true if all permissions are granted\n */\nexport async function requestPermissions(\n permissions: PermissionType[],\n): Promise<boolean> {\n try {\n const results = await Promise.all(\n permissions.map(async (permission) => {\n switch (permission) {\n case PermissionType.CAMERA:\n return requestCameraPermission();\n case PermissionType.PHOTO_LIBRARY:\n return requestPhotoLibraryPermission();\n case PermissionType.READ_STORAGE:\n return requestStorageReadPermission();\n case PermissionType.WRITE_STORAGE:\n return requestStorageWritePermission();\n default:\n return false;\n }\n }),\n );\n\n return results.every((result) => result);\n } catch (error) {\n console.error(\"Failed to request permissions:\", error);\n return false;\n }\n}\n\n/**\n * Check if all required permissions are granted\n * @param permissions - Array of permission types to check\n * @returns Promise resolving to true if all permissions are granted\n */\nexport async function hasPermissions(\n _permissions: PermissionType[],\n): Promise<boolean> {\n try {\n // In React Native, permission checking is typically handled by the platform\n // This is a placeholder that assumes permissions will be handled by the file system provider\n return true;\n } catch (error) {\n console.error(\"Failed to check permissions:\", error);\n return false;\n }\n}\n\n/**\n * Get permission status\n * @param permission - Permission type to check\n * @returns Promise resolving to permission status\n */\nexport async function getPermissionStatus(\n _permission: PermissionType,\n): Promise<PermissionStatus> {\n try {\n // This is a placeholder implementation\n // Real implementation would use platform-specific APIs\n return PermissionStatus.GRANTED;\n } catch (error) {\n console.error(\"Failed to get permission status:\", error);\n return PermissionStatus.DENIED;\n }\n}\n\n/**\n * Open app settings to request permissions\n * Guides user to app settings where they can manually enable permissions\n */\nexport function openAppSettings(): void {\n try {\n // This would typically use react-native-app-settings or similar\n console.log(\n \"Opening app settings (requires react-native-app-settings or platform implementation)\",\n );\n } catch (error) {\n console.error(\"Failed to open app settings:\", error);\n }\n}\n","/**\n * URI utility functions for React Native file handling\n */\n\n/**\n * Extract file name from URI\n * @param uri - File URI\n * @returns File name extracted from URI\n */\nexport function getFileNameFromUri(uri: string): string {\n try {\n // Handle different URI formats\n if (uri.startsWith(\"file://\")) {\n // File URI format\n const path = uri.replace(\"file://\", \"\");\n return path.split(\"/\").pop() || \"file\";\n }\n\n if (uri.startsWith(\"content://\")) {\n // Content URI format (Android)\n const parts = uri.split(\"/\");\n return parts[parts.length - 1] || \"file\";\n }\n\n // Assume it's a path or other format\n const parts = uri.split(\"/\");\n return parts[parts.length - 1] || \"file\";\n } catch {\n return \"file\";\n }\n}\n\n/**\n * Convert file path to file URI\n * @param filePath - File path\n * @returns File URI\n */\nexport function pathToUri(filePath: string): string {\n if (filePath.startsWith(\"file://\")) {\n return filePath;\n }\n\n if (filePath.startsWith(\"content://\")) {\n return filePath;\n }\n\n // Convert to file URI\n return `file://${filePath}`;\n}\n\n/**\n * Convert file URI to file path\n * @param uri - File URI\n * @returns File path\n */\nexport function uriToPath(uri: string): string {\n if (uri.startsWith(\"file://\")) {\n return uri.replace(\"file://\", \"\");\n }\n\n if (uri.startsWith(\"content://\")) {\n // Content URIs cannot be converted to paths directly\n return uri;\n }\n\n return uri;\n}\n\n/**\n * Get directory from URI\n * @param uri - File URI\n * @returns Directory path\n */\nexport function getDirectoryFromUri(uri: string): string {\n try {\n const path = uriToPath(uri);\n const parts = path.split(\"/\");\n parts.pop(); // Remove file name\n return parts.join(\"/\");\n } catch {\n return \"\";\n }\n}\n\n/**\n * Check if URI is a content URI (Android specific)\n * @param uri - URI to check\n * @returns True if URI is a content URI\n */\nexport function isContentUri(uri: string): boolean {\n return uri.startsWith(\"content://\");\n}\n\n/**\n * Check if URI is a file URI\n * @param uri - URI to check\n * @returns True if URI is a file URI\n */\nexport function isFileUri(uri: string): boolean {\n return uri.startsWith(\"file://\");\n}\n\n/**\n * Normalize URI for cross-platform compatibility\n * @param uri - URI to normalize\n * @returns Normalized URI\n */\nexport function normalizeUri(uri: string): string {\n // Remove duplicate slashes (but keep protocol slashes)\n return uri.replace(/([^:]\\/)\\/+/g, \"$1\");\n}\n\n/**\n * Get MIME type hint from URI\n * @param uri - File URI\n * @returns MIME type hint based on file extension\n */\nexport function getMimeTypeFromUri(uri: string): string {\n const fileName = getFileNameFromUri(uri);\n\n const mimeTypes: Record<string, string> = {\n // Images\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".gif\": \"image/gif\",\n \".bmp\": \"image/bmp\",\n \".webp\": \"image/webp\",\n\n // Videos\n \".mp4\": \"video/mp4\",\n \".mov\": \"video/quicktime\",\n \".avi\": \"video/x-msvideo\",\n\n // Audio\n \".mp3\": \"audio/mpeg\",\n \".wav\": \"audio/wav\",\n \".aac\": \"audio/aac\",\n\n // Documents\n \".pdf\": \"application/pdf\",\n \".txt\": \"text/plain\",\n \".json\": \"application/json\",\n };\n\n const ext = fileName.toLowerCase().slice(fileName.lastIndexOf(\".\"));\n return mimeTypes[ext] || \"application/octet-stream\";\n}\n","import { ActivityIndicator, StyleSheet, Text, View } from \"react-native\";\nimport type { UploadState } from \"../hooks/use-upload\";\nimport { formatFileSize } from \"../utils\";\n\nexport interface UploadProgressProps {\n /** Upload state information */\n state: UploadState;\n /** Optional custom label */\n label?: string;\n}\n\n/**\n * Component to display upload progress with percentage, size, and speed\n */\nexport function UploadProgress({ state, label }: UploadProgressProps) {\n const getStatusColor = () => {\n switch (state.status) {\n case \"uploading\":\n return \"#007AFF\";\n case \"success\":\n return \"#34C759\";\n case \"error\":\n case \"aborted\":\n return \"#FF3B30\";\n default:\n return \"#999999\";\n }\n };\n\n const renderContent = () => {\n switch (state.status) {\n case \"idle\":\n return (\n <View style={styles.container}>\n <Text style={styles.label}>{label || \"Ready to upload\"}</Text>\n </View>\n );\n\n case \"uploading\":\n return (\n <View style={styles.container}>\n <View style={styles.headerRow}>\n <Text style={styles.label}>{label || \"Uploading\"}</Text>\n <Text style={styles.percentage}>{state.progress}%</Text>\n </View>\n\n {/* Progress bar */}\n <View style={styles.progressBarContainer}>\n <View\n style={[\n styles.progressBar,\n {\n width: `${state.progress}%`,\n backgroundColor: getStatusColor(),\n },\n ]}\n />\n </View>\n\n {/* Details row */}\n <View style={styles.detailsRow}>\n <Text style={styles.detail}>\n {formatFileSize(state.bytesUploaded)} /{\" \"}\n {formatFileSize(state.totalBytes || 0)}\n </Text>\n </View>\n </View>\n );\n\n case \"success\":\n return (\n <View style={styles.container}>\n <View style={styles.headerRow}>\n <Text style={[styles.label, { color: getStatusColor() }]}>\n {label || \"Upload complete\"}\n </Text>\n <Text style={[styles.percentage, { color: getStatusColor() }]}>\n ✓\n </Text>\n </View>\n <Text style={[styles.detail, { color: getStatusColor() }]}>\n {formatFileSize(state.totalBytes || 0)}\n </Text>\n </View>\n );\n\n case \"error\":\n return (\n <View style={styles.container}>\n <View style={styles.headerRow}>\n <Text style={[styles.label, { color: getStatusColor() }]}>\n {label || \"Upload failed\"}\n </Text>\n <Text style={[styles.percentage, { color: getStatusColor() }]}>\n ✕\n </Text>\n </View>\n {state.error && (\n <Text style={[styles.detail, { color: getStatusColor() }]}>\n {state.error.message}\n </Text>\n )}\n </View>\n );\n\n case \"aborted\":\n return (\n <View style={styles.container}>\n <Text style={[styles.label, { color: getStatusColor() }]}>\n {label || \"Upload cancelled\"}\n </Text>\n </View>\n );\n\n default:\n return null;\n }\n };\n\n return (\n <View\n style={[\n styles.wrapper,\n {\n borderLeftColor: getStatusColor(),\n },\n ]}\n >\n {state.status === \"uploading\" && (\n <ActivityIndicator\n size=\"small\"\n color={getStatusColor()}\n style={styles.spinner}\n />\n )}\n {renderContent()}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n wrapper: {\n flexDirection: \"row\",\n alignItems: \"flex-start\",\n paddingVertical: 8,\n paddingHorizontal: 12,\n borderLeftWidth: 4,\n backgroundColor: \"#f5f5f5\",\n borderRadius: 4,\n gap: 8,\n },\n spinner: {\n marginTop: 4,\n },\n container: {\n flex: 1,\n gap: 4,\n },\n headerRow: {\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n },\n label: {\n fontSize: 14,\n fontWeight: \"600\",\n color: \"#333333\",\n flex: 1,\n },\n percentage: {\n fontSize: 14,\n fontWeight: \"600\",\n color: \"#007AFF\",\n minWidth: 36,\n textAlign: \"right\",\n },\n progressBarContainer: {\n height: 4,\n backgroundColor: \"#e0e0e0\",\n borderRadius: 2,\n overflow: \"hidden\",\n },\n progressBar: {\n height: \"100%\",\n borderRadius: 2,\n },\n detailsRow: {\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n },\n detail: {\n fontSize: 12,\n color: \"#666666\",\n },\n});\n","import { type ReactNode, useEffect } from \"react\";\nimport {\n ActivityIndicator,\n Pressable,\n StyleSheet,\n Text,\n View,\n} from \"react-native\";\nimport { useCameraUpload } from \"../hooks\";\nimport type { UseCameraUploadOptions } from \"../types\";\nimport { UploadProgress } from \"./UploadProgress\";\n\nexport interface CameraUploadButtonProps {\n /** Options for camera upload */\n options?: UseCameraUploadOptions;\n /** Button label text */\n label?: string;\n /** Custom button content */\n children?: ReactNode;\n /** Callback when upload completes successfully */\n onSuccess?: (result: unknown) => void;\n /** Callback when upload fails */\n onError?: (error: Error) => void;\n /** Callback when upload is cancelled */\n onCancel?: () => void;\n /** Whether to show progress inline */\n showProgress?: boolean;\n}\n\n/**\n * Button component for camera capture and upload\n * Triggers camera on press and handles upload with progress display\n */\nexport function CameraUploadButton({\n options,\n label = \"Take Photo\",\n children,\n onSuccess,\n onError,\n onCancel,\n showProgress = true,\n}: CameraUploadButtonProps) {\n const { state, captureAndUpload } = useCameraUpload(options);\n\n const handlePress = async () => {\n try {\n await captureAndUpload();\n } catch (error) {\n if (error instanceof Error) {\n if (\n error.message.includes(\"cancelled\") ||\n error.message.includes(\"aborted\")\n ) {\n onCancel?.();\n } else {\n onError?.(error);\n }\n }\n }\n };\n\n const isLoading = state.status === \"uploading\";\n const isDisabled = isLoading || state.status === \"aborted\";\n\n useEffect(() => {\n if (state.status === \"success\" && state.result) {\n onSuccess?.(state.result);\n }\n }, [state.status, state.result, onSuccess]);\n\n useEffect(() => {\n if (state.status === \"error\" && state.error) {\n onError?.(state.error);\n }\n }, [state.status, state.error, onError]);\n\n return (\n <View style={styles.container}>\n <Pressable\n style={[styles.button, isDisabled && styles.buttonDisabled]}\n onPress={handlePress}\n disabled={isDisabled}\n >\n {isLoading && (\n <ActivityIndicator\n size=\"small\"\n color=\"#FFFFFF\"\n style={styles.spinner}\n />\n )}\n <Text style={styles.buttonText}>{children || label}</Text>\n </Pressable>\n {showProgress && state.status !== \"idle\" && (\n <View style={styles.progressContainer}>\n <UploadProgress state={state} label=\"Camera upload\" />\n </View>\n )}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n gap: 8,\n },\n button: {\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"center\",\n paddingVertical: 12,\n paddingHorizontal: 16,\n backgroundColor: \"#007AFF\",\n borderRadius: 8,\n gap: 8,\n },\n buttonDisabled: {\n opacity: 0.6,\n },\n buttonText: {\n fontSize: 16,\n fontWeight: \"600\",\n color: \"#FFFFFF\",\n },\n spinner: {\n marginRight: 4,\n },\n progressContainer: {\n marginTop: 4,\n },\n});\n","import { type ReactNode, useEffect } from \"react\";\nimport {\n ActivityIndicator,\n Pressable,\n StyleSheet,\n Text,\n View,\n} from \"react-native\";\nimport { useFileUpload } from \"../hooks\";\nimport type { UseFileUploadOptions } from \"../types\";\nimport { UploadProgress } from \"./UploadProgress\";\n\nexport interface FileUploadButtonProps {\n /** Options for file upload */\n options?: UseFileUploadOptions;\n /** Button label text */\n label?: string;\n /** Custom button content */\n children?: ReactNode;\n /** Callback when upload completes successfully */\n onSuccess?: (result: unknown) => void;\n /** Callback when upload fails */\n onError?: (error: Error) => void;\n /** Callback when upload is cancelled */\n onCancel?: () => void;\n /** Whether to show progress inline */\n showProgress?: boolean;\n}\n\n/**\n * Button component for document/file selection and upload\n * Generic file picker with progress display\n */\nexport function FileUploadButton({\n options,\n label = \"Choose File\",\n children,\n onSuccess,\n onError,\n onCancel,\n showProgress = true,\n}: FileUploadButtonProps) {\n const { state, pickAndUpload } = useFileUpload(options);\n\n const handlePress = async () => {\n try {\n await pickAndUpload();\n } catch (error) {\n if (error instanceof Error) {\n if (\n error.message.includes(\"cancelled\") ||\n error.message.includes(\"aborted\")\n ) {\n onCancel?.();\n } else {\n onError?.(error);\n }\n }\n }\n };\n\n const isLoading = state.status === \"uploading\";\n const isDisabled = isLoading || state.status === \"aborted\";\n\n useEffect(() => {\n if (state.status === \"success\" && state.result) {\n onSuccess?.(state.result);\n }\n }, [state.status, state.result, onSuccess]);\n\n useEffect(() => {\n if (state.status === \"error\" && state.error) {\n onError?.(state.error);\n }\n }, [state.status, state.error, onError]);\n\n return (\n <View style={styles.container}>\n <Pressable\n style={[styles.button, isDisabled && styles.buttonDisabled]}\n onPress={handlePress}\n disabled={isDisabled}\n >\n {isLoading && (\n <ActivityIndicator\n size=\"small\"\n color=\"#FFFFFF\"\n style={styles.spinner}\n />\n )}\n <Text style={styles.buttonText}>{children || label}</Text>\n </Pressable>\n {showProgress && state.status !== \"idle\" && (\n <View style={styles.progressContainer}>\n <UploadProgress state={state} label=\"File upload\" />\n </View>\n )}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n gap: 8,\n },\n button: {\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"center\",\n paddingVertical: 12,\n paddingHorizontal: 16,\n backgroundColor: \"#FF9500\",\n borderRadius: 8,\n gap: 8,\n },\n buttonDisabled: {\n opacity: 0.6,\n },\n buttonText: {\n fontSize: 16,\n fontWeight: \"600\",\n color: \"#FFFFFF\",\n },\n spinner: {\n marginRight: 4,\n },\n progressContainer: {\n marginTop: 4,\n },\n});\n","import React, { type ReactNode } from \"react\";\nimport {\n ActivityIndicator,\n FlatList,\n Pressable,\n StyleSheet,\n Text,\n View,\n} from \"react-native\";\nimport { useGalleryUpload } from \"../hooks\";\nimport type { UseGalleryUploadOptions } from \"../types\";\nimport { UploadProgress } from \"./UploadProgress\";\n\nexport interface GalleryUploadButtonProps {\n /** Options for gallery upload */\n options?: UseGalleryUploadOptions;\n /** Button label text */\n label?: string;\n /** Custom button content */\n children?: ReactNode;\n /** Callback when all uploads complete successfully */\n onSuccess?: (results: unknown[]) => void;\n /** Callback when any upload fails */\n onError?: (error: Error) => void;\n /** Callback when upload is cancelled */\n onCancel?: () => void;\n /** Whether to show individual progress for each file */\n showProgress?: boolean;\n}\n\n/**\n * Button component for gallery selection and batch upload\n * Triggers gallery picker on press and handles concurrent uploads\n */\nexport function GalleryUploadButton({\n options,\n label = \"Select from Gallery\",\n children,\n onSuccess,\n onError,\n onCancel,\n showProgress = true,\n}: GalleryUploadButtonProps) {\n const { state, selectAndUpload } = useGalleryUpload(options);\n\n const handlePress = async () => {\n try {\n await selectAndUpload();\n } catch (error) {\n if (error instanceof Error) {\n if (\n error.message.includes(\"cancelled\") ||\n error.message.includes(\"aborted\")\n ) {\n onCancel?.();\n } else {\n onError?.(error);\n }\n }\n }\n };\n\n const isLoading = state.items.some((item) => item.status === \"uploading\");\n const hasItems = state.items.length > 0;\n const allComplete =\n hasItems &&\n state.items.every(\n (item) => item.status !== \"uploading\" && item.status !== \"idle\",\n );\n\n React.useEffect(() => {\n if (allComplete) {\n const results = state.items\n .filter((item) => item.status === \"success\")\n .map((item) => item.result);\n if (results.length > 0) {\n onSuccess?.(results);\n }\n }\n }, [allComplete, state.items, onSuccess]);\n\n React.useEffect(() => {\n const errors = state.items.filter((item) => item.status === \"error\");\n const firstError = errors[0]?.error;\n if (firstError) {\n onError?.(firstError);\n }\n }, [state.items, onError]);\n\n const renderItem = ({ item }: { item: (typeof state.items)[0] }) => (\n <View key={item.id} style={styles.itemContainer}>\n <UploadProgress\n state={{\n status: item.status,\n progress: item.progress,\n bytesUploaded: item.bytesUploaded,\n totalBytes: item.totalBytes,\n error: item.error,\n result: item.result,\n }}\n label={item.file.data.name}\n />\n </View>\n );\n\n return (\n <View style={styles.container}>\n <Pressable\n style={[styles.button, isLoading && styles.buttonDisabled]}\n onPress={handlePress}\n disabled={isLoading}\n >\n {isLoading && (\n <ActivityIndicator\n size=\"small\"\n color=\"#FFFFFF\"\n style={styles.spinner}\n />\n )}\n <Text style={styles.buttonText}>\n {children || label}\n {hasItems && ` (${state.items.length})`}\n </Text>\n </Pressable>\n\n {hasItems && (\n <View style={styles.statsContainer}>\n <Text style={styles.statsText}>\n Progress: {state.items.filter((i) => i.status === \"success\").length}\n /{state.items.length} uploaded\n </Text>\n <Text style={styles.statsText}>Overall: {state.totalProgress}%</Text>\n </View>\n )}\n\n {showProgress && hasItems && (\n <FlatList\n scrollEnabled={false}\n data={state.items}\n renderItem={renderItem}\n keyExtractor={(item) => item.id}\n style={styles.listContainer}\n contentContainerStyle={styles.listContent}\n ItemSeparatorComponent={() => <View style={styles.separator} />}\n />\n )}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n gap: 8,\n },\n button: {\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"center\",\n paddingVertical: 12,\n paddingHorizontal: 16,\n backgroundColor: \"#34C759\",\n borderRadius: 8,\n gap: 8,\n },\n buttonDisabled: {\n opacity: 0.6,\n },\n buttonText: {\n fontSize: 16,\n fontWeight: \"600\",\n color: \"#FFFFFF\",\n },\n spinner: {\n marginRight: 4,\n },\n statsContainer: {\n paddingVertical: 8,\n paddingHorizontal: 12,\n backgroundColor: \"#f5f5f5\",\n borderRadius: 4,\n gap: 4,\n },\n statsText: {\n fontSize: 12,\n color: \"#666666\",\n },\n listContainer: {\n maxHeight: 400,\n },\n listContent: {\n gap: 8,\n },\n itemContainer: {\n paddingHorizontal: 0,\n },\n separator: {\n height: 4,\n },\n});\n","import { FlatList, Pressable, StyleSheet, Text, View } from \"react-native\";\nimport type { UploadItem } from \"../types\";\nimport { UploadProgress } from \"./UploadProgress\";\n\nexport interface UploadListProps {\n /** List of upload items to display */\n items: UploadItem[];\n /** Callback when remove item is pressed */\n onRemove?: (id: string) => void;\n /** Callback when item is pressed */\n onItemPress?: (item: UploadItem) => void;\n /** Whether to show remove button */\n showRemoveButton?: boolean;\n}\n\n/**\n * Component to display a list of upload items with individual progress\n * Shows status indicators and allows removal of items\n */\nexport function UploadList({\n items,\n onRemove,\n onItemPress,\n showRemoveButton = true,\n}: UploadListProps) {\n const renderItem = ({ item }: { item: UploadItem }) => (\n <Pressable\n style={[\n styles.itemContainer,\n { borderLeftColor: getStatusColor(item.progress.state) },\n ]}\n onPress={() => onItemPress?.(item)}\n >\n <View style={styles.itemContent}>\n {item.file.status === \"success\" && (\n <View style={styles.itemHeader}>\n <Text style={styles.fileName} numberOfLines={1}>\n {item.file.data.name}\n </Text>\n <Text style={styles.fileSize}>\n {getFileSizeDisplay(item.file.data.size)}\n </Text>\n </View>\n )}\n {item.file.status === \"error\" && (\n <Text style={styles.errorText}>{item.progress.error?.message}</Text>\n )}\n <View style={styles.progressWrapper}>\n <UploadProgress\n state={{\n status:\n item.progress.state === \"pending\"\n ? \"idle\"\n : item.progress.state === \"cancelled\"\n ? \"aborted\"\n : item.progress.state,\n progress: item.progress.progress,\n bytesUploaded: item.progress.uploadedBytes,\n totalBytes: item.progress.totalBytes,\n error: item.progress.error || null,\n result: item.result ?? null,\n }}\n />\n </View>\n </View>\n {showRemoveButton &&\n item.progress.state !== \"uploading\" &&\n item.progress.state !== \"pending\" && (\n <Pressable\n style={styles.removeButton}\n onPress={() => onRemove?.(item.id)}\n hitSlop={{ top: 8, right: 8, bottom: 8, left: 8 }}\n >\n <Text style={styles.removeButtonText}>✕</Text>\n </Pressable>\n )}\n </Pressable>\n );\n\n if (items.length === 0) {\n return (\n <View style={styles.emptyContainer}>\n <Text style={styles.emptyText}>No uploads</Text>\n </View>\n );\n }\n\n return (\n <View style={styles.container}>\n <View style={styles.headerRow}>\n <Text style={styles.headerText}>Uploads ({items.length})</Text>\n <Text style={styles.headerSubtext}>\n {items.filter((i) => i.progress.state === \"success\").length} complete\n </Text>\n </View>\n <FlatList\n scrollEnabled={false}\n data={items}\n renderItem={renderItem}\n keyExtractor={(item) => item.id}\n ItemSeparatorComponent={() => <View style={styles.separator} />}\n contentContainerStyle={styles.listContent}\n />\n </View>\n );\n}\n\n// Helper functions\nfunction getStatusColor(state: string): string {\n switch (state) {\n case \"success\":\n return \"#34C759\";\n case \"error\":\n case \"cancelled\":\n return \"#FF3B30\";\n case \"uploading\":\n case \"pending\":\n return \"#007AFF\";\n default:\n return \"#999999\";\n }\n}\n\nfunction getFileSizeDisplay(bytes: number): string {\n if (bytes === 0) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${Math.round((bytes / k ** i) * 10) / 10} ${sizes[i]}`;\n}\n\nconst styles = StyleSheet.create({\n container: {\n gap: 8,\n },\n headerRow: {\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n paddingHorizontal: 12,\n paddingVertical: 8,\n backgroundColor: \"#f9f9f9\",\n borderRadius: 4,\n },\n headerText: {\n fontSize: 16,\n fontWeight: \"600\",\n color: \"#333333\",\n },\n errorText: {\n fontSize: 14,\n color: \"#FF3B30\",\n },\n headerSubtext: {\n fontSize: 14,\n color: \"#666666\",\n },\n listContent: {\n gap: 8,\n },\n itemContainer: {\n flexDirection: \"row\",\n alignItems: \"center\",\n paddingVertical: 8,\n paddingHorizontal: 12,\n borderLeftWidth: 4,\n backgroundColor: \"#f5f5f5\",\n borderRadius: 4,\n gap: 8,\n },\n itemContent: {\n flex: 1,\n gap: 6,\n },\n itemHeader: {\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n },\n fileName: {\n fontSize: 14,\n fontWeight: \"500\",\n color: \"#333333\",\n flex: 1,\n },\n fileSize: {\n fontSize: 12,\n color: \"#999999\",\n marginLeft: 8,\n },\n progressWrapper: {\n marginTop: 2,\n },\n removeButton: {\n width: 32,\n height: 32,\n justifyContent: \"center\",\n alignItems: \"center\",\n borderRadius: 16,\n backgroundColor: \"#FFE5E5\",\n },\n removeButtonText: {\n fontSize: 16,\n fontWeight: \"600\",\n color: \"#FF3B30\",\n },\n separator: {\n height: 4,\n },\n emptyContainer: {\n paddingVertical: 24,\n paddingHorizontal: 12,\n backgroundColor: \"#f5f5f5\",\n borderRadius: 4,\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n emptyText: {\n fontSize: 14,\n color: \"#999999\",\n fontStyle: \"italic\",\n },\n});\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uploadista/react-native-core",
|
|
3
|
-
"version": "0.0.15
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Core React Native client for Uploadista",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"uuid": "^13.0.0",
|
|
17
17
|
"js-base64": "^3.7.7",
|
|
18
|
-
"@uploadista/core": "0.0.15
|
|
19
|
-
"@uploadista/
|
|
18
|
+
"@uploadista/client-core": "0.0.15",
|
|
19
|
+
"@uploadista/core": "0.0.15"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"react": ">=16.8.0",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/react": ">=18.0.0",
|
|
33
33
|
"tsdown": "0.16.4",
|
|
34
|
-
"@uploadista/typescript-config": "0.0.15
|
|
34
|
+
"@uploadista/typescript-config": "0.0.15"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "tsdown",
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
FlowManager,
|
|
4
4
|
type FlowManagerCallbacks,
|
|
5
5
|
type FlowUploadOptions,
|
|
6
|
-
type FlowUploadState,
|
|
7
6
|
} from "@uploadista/client-core";
|
|
8
7
|
import { EventType, type FlowEvent } from "@uploadista/core/flow";
|
|
9
8
|
import { UploadEventType } from "@uploadista/core/types";
|
|
@@ -16,6 +15,7 @@ import {
|
|
|
16
15
|
useRef,
|
|
17
16
|
} from "react";
|
|
18
17
|
import { useUploadistaContext } from "../hooks/use-uploadista-context";
|
|
18
|
+
import type { ReactNativeUploadInput } from "../types";
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Type guard to check if an event is a flow event
|
|
@@ -37,8 +37,8 @@ function isFlowEvent(event: UploadistaEvent): event is FlowEvent {
|
|
|
37
37
|
/**
|
|
38
38
|
* Internal manager registry entry with ref counting
|
|
39
39
|
*/
|
|
40
|
-
interface ManagerEntry
|
|
41
|
-
manager: FlowManager<unknown
|
|
40
|
+
interface ManagerEntry {
|
|
41
|
+
manager: FlowManager<unknown>;
|
|
42
42
|
refCount: number;
|
|
43
43
|
flowId: string;
|
|
44
44
|
}
|
|
@@ -56,11 +56,11 @@ interface FlowManagerContextValue {
|
|
|
56
56
|
* @param options - Flow configuration options
|
|
57
57
|
* @returns FlowManager instance
|
|
58
58
|
*/
|
|
59
|
-
getManager:
|
|
59
|
+
getManager: (
|
|
60
60
|
flowId: string,
|
|
61
|
-
callbacks: FlowManagerCallbacks
|
|
62
|
-
options: FlowUploadOptions
|
|
63
|
-
) => FlowManager<unknown
|
|
61
|
+
callbacks: FlowManagerCallbacks,
|
|
62
|
+
options: FlowUploadOptions,
|
|
63
|
+
) => FlowManager<unknown>;
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* Release a flow manager reference.
|
|
@@ -101,9 +101,7 @@ interface FlowManagerProviderProps {
|
|
|
101
101
|
*/
|
|
102
102
|
export function FlowManagerProvider({ children }: FlowManagerProviderProps) {
|
|
103
103
|
const { client, subscribeToEvents } = useUploadistaContext();
|
|
104
|
-
const managersRef = useRef(
|
|
105
|
-
new Map<string, ManagerEntry<unknown>>(),
|
|
106
|
-
);
|
|
104
|
+
const managersRef = useRef(new Map<string, ManagerEntry>());
|
|
107
105
|
|
|
108
106
|
// Subscribe to all events and route to appropriate managers
|
|
109
107
|
useEffect(() => {
|
|
@@ -122,17 +120,11 @@ export function FlowManagerProvider({ children }: FlowManagerProviderProps) {
|
|
|
122
120
|
event.type === UploadEventType.UPLOAD_PROGRESS &&
|
|
123
121
|
"data" in event
|
|
124
122
|
) {
|
|
125
|
-
const uploadEvent = event as {
|
|
126
|
-
type: UploadEventType;
|
|
127
|
-
uploadId: string;
|
|
128
|
-
data: { progress: number; total: number | null };
|
|
129
|
-
};
|
|
130
|
-
|
|
131
123
|
for (const entry of managersRef.current.values()) {
|
|
132
124
|
entry.manager.handleUploadProgress(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
125
|
+
event.data.id,
|
|
126
|
+
event.data.progress,
|
|
127
|
+
event.data.total,
|
|
136
128
|
);
|
|
137
129
|
}
|
|
138
130
|
}
|
|
@@ -142,36 +134,28 @@ export function FlowManagerProvider({ children }: FlowManagerProviderProps) {
|
|
|
142
134
|
}, [subscribeToEvents]);
|
|
143
135
|
|
|
144
136
|
const getManager = useCallback(
|
|
145
|
-
|
|
137
|
+
(
|
|
146
138
|
flowId: string,
|
|
147
|
-
callbacks: FlowManagerCallbacks
|
|
148
|
-
options: FlowUploadOptions
|
|
149
|
-
): FlowManager<unknown
|
|
139
|
+
callbacks: FlowManagerCallbacks,
|
|
140
|
+
options: FlowUploadOptions,
|
|
141
|
+
): FlowManager<unknown> => {
|
|
150
142
|
const existing = managersRef.current.get(flowId);
|
|
151
143
|
|
|
152
144
|
if (existing) {
|
|
153
145
|
// Increment ref count for existing manager
|
|
154
146
|
existing.refCount++;
|
|
155
|
-
return existing.manager
|
|
147
|
+
return existing.manager;
|
|
156
148
|
}
|
|
157
149
|
|
|
158
150
|
// Create new manager using client from hook scope
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
flowConfig: FlowUploadOptions<TOutput>["flowConfig"],
|
|
162
|
-
internalOptions: unknown,
|
|
163
|
-
) => {
|
|
164
|
-
return client.uploadWithFlow(input, flowConfig, internalOptions);
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const manager = new FlowManager<unknown, TOutput>(
|
|
168
|
-
flowUploadFn,
|
|
151
|
+
const manager = new FlowManager<ReactNativeUploadInput>(
|
|
152
|
+
client.uploadWithFlow,
|
|
169
153
|
callbacks,
|
|
170
154
|
options,
|
|
171
155
|
);
|
|
172
156
|
|
|
173
157
|
managersRef.current.set(flowId, {
|
|
174
|
-
manager
|
|
158
|
+
manager,
|
|
175
159
|
refCount: 1,
|
|
176
160
|
flowId,
|
|
177
161
|
});
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type {
|
|
2
|
+
FlowManager,
|
|
3
|
+
FlowUploadState,
|
|
4
|
+
FlowUploadStatus,
|
|
5
5
|
} from "@uploadista/client-core";
|
|
6
6
|
import type { TypedOutput } from "@uploadista/core/flow";
|
|
7
|
-
import type { UploadFile } from "@uploadista/core/types";
|
|
8
7
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
9
8
|
import { useFlowManagerContext } from "../contexts/flow-manager-context";
|
|
10
9
|
import type { FilePickResult, UseFlowUploadOptions } from "../types";
|
|
@@ -20,7 +19,6 @@ const initialState: FlowUploadState = {
|
|
|
20
19
|
bytesUploaded: 0,
|
|
21
20
|
totalBytes: null,
|
|
22
21
|
error: null,
|
|
23
|
-
result: null,
|
|
24
22
|
jobId: null,
|
|
25
23
|
flowStarted: false,
|
|
26
24
|
currentNodeName: null,
|
|
@@ -72,7 +70,7 @@ export function useFlowUpload(options: UseFlowUploadOptions) {
|
|
|
72
70
|
const { getManager, releaseManager } = useFlowManagerContext();
|
|
73
71
|
const { fileSystemProvider } = useUploadistaContext();
|
|
74
72
|
const [state, setState] = useState<FlowUploadState>(initialState);
|
|
75
|
-
const managerRef = useRef<FlowManager<unknown
|
|
73
|
+
const managerRef = useRef<FlowManager<unknown> | null>(null);
|
|
76
74
|
const lastFileRef = useRef<FilePickResult | null>(null);
|
|
77
75
|
|
|
78
76
|
// Store callbacks in refs so they can be updated without recreating the manager
|
|
@@ -91,7 +89,11 @@ export function useFlowUpload(options: UseFlowUploadOptions) {
|
|
|
91
89
|
// Create stable callback wrappers that call the latest callbacks via refs
|
|
92
90
|
const stableCallbacks = {
|
|
93
91
|
onStateChange: setState,
|
|
94
|
-
onProgress: (
|
|
92
|
+
onProgress: (
|
|
93
|
+
_uploadId: string,
|
|
94
|
+
bytesUploaded: number,
|
|
95
|
+
totalBytes: number | null,
|
|
96
|
+
) => {
|
|
95
97
|
if (callbacksRef.current.onProgress) {
|
|
96
98
|
const progress = totalBytes
|
|
97
99
|
? Math.round((bytesUploaded / totalBytes) * 100)
|
|
@@ -99,46 +101,56 @@ export function useFlowUpload(options: UseFlowUploadOptions) {
|
|
|
99
101
|
callbacksRef.current.onProgress(progress, bytesUploaded, totalBytes);
|
|
100
102
|
}
|
|
101
103
|
},
|
|
102
|
-
onChunkComplete: (
|
|
103
|
-
|
|
104
|
+
onChunkComplete: (
|
|
105
|
+
chunkSize: number,
|
|
106
|
+
bytesAccepted: number,
|
|
107
|
+
bytesTotal: number | null,
|
|
108
|
+
) => {
|
|
109
|
+
callbacksRef.current.onChunkComplete?.(
|
|
110
|
+
chunkSize,
|
|
111
|
+
bytesAccepted,
|
|
112
|
+
bytesTotal,
|
|
113
|
+
);
|
|
104
114
|
},
|
|
105
115
|
onFlowComplete: (outputs: TypedOutput[]) => {
|
|
106
116
|
callbacksRef.current.onFlowComplete?.(outputs);
|
|
107
117
|
},
|
|
108
|
-
onSuccess: (
|
|
109
|
-
callbacksRef.current.onSuccess?.(
|
|
118
|
+
onSuccess: (outputs: TypedOutput[]) => {
|
|
119
|
+
callbacksRef.current.onSuccess?.(outputs);
|
|
110
120
|
},
|
|
111
121
|
onError: (error: Error) => {
|
|
112
122
|
callbacksRef.current.onError?.(error);
|
|
113
123
|
},
|
|
114
124
|
onAbort: () => {
|
|
115
|
-
|
|
125
|
+
// onAbort is not exposed in the public API
|
|
116
126
|
},
|
|
117
127
|
};
|
|
118
128
|
|
|
119
129
|
// Get manager from context (creates if doesn't exist, increments ref count)
|
|
120
|
-
managerRef.current = getManager(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
storageId: options.storageId,
|
|
127
|
-
outputNodeId: options.outputNodeId,
|
|
128
|
-
metadata: options.metadata as Record<string, string> | undefined,
|
|
129
|
-
},
|
|
130
|
-
onChunkComplete: options.onChunkComplete,
|
|
131
|
-
onSuccess: options.onSuccess,
|
|
132
|
-
onError: options.onError,
|
|
130
|
+
managerRef.current = getManager(flowId, stableCallbacks, {
|
|
131
|
+
flowConfig: {
|
|
132
|
+
flowId: options.flowId,
|
|
133
|
+
storageId: options.storageId,
|
|
134
|
+
outputNodeId: options.outputNodeId,
|
|
135
|
+
metadata: options.metadata as Record<string, string> | undefined,
|
|
133
136
|
},
|
|
134
|
-
|
|
137
|
+
onChunkComplete: options.onChunkComplete,
|
|
138
|
+
onSuccess: options.onSuccess,
|
|
139
|
+
onError: options.onError,
|
|
140
|
+
});
|
|
135
141
|
|
|
136
142
|
// Release manager when component unmounts or flowId changes
|
|
137
143
|
return () => {
|
|
138
144
|
releaseManager(flowId);
|
|
139
145
|
managerRef.current = null;
|
|
140
146
|
};
|
|
141
|
-
}, [
|
|
147
|
+
}, [
|
|
148
|
+
options.flowId,
|
|
149
|
+
options.storageId,
|
|
150
|
+
options.outputNodeId,
|
|
151
|
+
getManager,
|
|
152
|
+
releaseManager,
|
|
153
|
+
]);
|
|
142
154
|
|
|
143
155
|
const upload = useCallback(
|
|
144
156
|
async (file: FilePickResult) => {
|
|
@@ -171,7 +171,10 @@ export function useMultiUpload(options: UseMultiUploadOptions = {}) {
|
|
|
171
171
|
|
|
172
172
|
// Override blob type if we have mimeType from picker
|
|
173
173
|
const uploadInput = item.file.data.mimeType
|
|
174
|
-
? new Blob([blob], {
|
|
174
|
+
? new Blob([blob], {
|
|
175
|
+
type: item.file.data.mimeType,
|
|
176
|
+
lastModified: Date.now(),
|
|
177
|
+
})
|
|
175
178
|
: blob;
|
|
176
179
|
|
|
177
180
|
// Start upload using the client
|
package/src/types/types.ts
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
* Core types for React Native Uploadista client
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import type { TypedOutput } from "@uploadista/core/flow";
|
|
6
|
+
import type { UploadFile } from "@uploadista/core/types";
|
|
7
|
+
|
|
5
8
|
/**
|
|
6
9
|
* Options for file picker operations
|
|
7
10
|
*/
|
|
@@ -187,7 +190,7 @@ export interface UploadItem {
|
|
|
187
190
|
/** Upload progress */
|
|
188
191
|
progress: UploadProgress;
|
|
189
192
|
/** Result from server if successful */
|
|
190
|
-
result?:
|
|
193
|
+
result?: UploadFile;
|
|
191
194
|
}
|
|
192
195
|
|
|
193
196
|
/**
|
|
@@ -264,8 +267,10 @@ export interface UseFlowUploadOptions {
|
|
|
264
267
|
outputNodeId?: string;
|
|
265
268
|
/** Metadata to pass to flow */
|
|
266
269
|
metadata?: Record<string, unknown>;
|
|
267
|
-
/** Called when upload succeeds */
|
|
268
|
-
onSuccess?: (
|
|
270
|
+
/** Called when upload succeeds (receives typed outputs from all output nodes) */
|
|
271
|
+
onSuccess?: (outputs: TypedOutput[]) => void;
|
|
272
|
+
/** Called when the flow completes successfully (receives full flow outputs) */
|
|
273
|
+
onFlowComplete?: (outputs: TypedOutput[]) => void;
|
|
269
274
|
/** Called when upload fails */
|
|
270
275
|
onError?: (error: Error) => void;
|
|
271
276
|
/** Called when upload progress updates */
|
|
@@ -298,7 +303,7 @@ export interface UseCameraUploadOptions {
|
|
|
298
303
|
onError?: (error: Error) => void;
|
|
299
304
|
/** Called when upload progress updates */
|
|
300
305
|
onProgress?: (
|
|
301
|
-
|
|
306
|
+
uploadId: string,
|
|
302
307
|
bytesUploaded: number,
|
|
303
308
|
totalBytes: number | null,
|
|
304
309
|
) => void;
|
|
@@ -344,7 +349,7 @@ export interface UseFileUploadOptions {
|
|
|
344
349
|
onError?: (error: Error) => void;
|
|
345
350
|
/** Called when upload progress updates */
|
|
346
351
|
onProgress?: (
|
|
347
|
-
|
|
352
|
+
uploadId: string,
|
|
348
353
|
bytesUploaded: number,
|
|
349
354
|
totalBytes: number | null,
|
|
350
355
|
) => void;
|