@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 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 react_jsx_runtime1 from "react/jsx-runtime";
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?: unknown;
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?: (result: unknown) => void;
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?: (progress: number, bytesUploaded: number, totalBytes: number | null) => void;
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?: (progress: number, bytesUploaded: number, totalBytes: number | null) => void;
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): react_jsx_runtime1.JSX.Element;
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): react_jsx_runtime1.JSX.Element;
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): react_jsx_runtime1.JSX.Element;
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): react_jsx_runtime1.JSX.Element;
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): react_jsx_runtime1.JSX.Element;
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: <TOutput = unknown>(flowId: string, callbacks: FlowManagerCallbacks<TOutput>, options: FlowUploadOptions<TOutput>) => FlowManager<unknown, TOutput>;
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): react_jsx_runtime1.JSX.Element;
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<_uploadista_core0.FlowJob>;
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<UploadFile>;
732
+ state: FlowUploadState;
733
733
  upload: (file: FilePickResult) => Promise<void>;
734
734
  abort: () => void;
735
735
  reset: () => void;
@@ -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
@@ -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-beta.4",
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-beta.4",
19
- "@uploadista/client-core": "0.0.15-beta.4"
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-beta.4"
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<TOutput> {
41
- manager: FlowManager<unknown, TOutput>;
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: <TOutput = unknown>(
59
+ getManager: (
60
60
  flowId: string,
61
- callbacks: FlowManagerCallbacks<TOutput>,
62
- options: FlowUploadOptions<TOutput>,
63
- ) => FlowManager<unknown, TOutput>;
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
- uploadEvent.uploadId,
134
- uploadEvent.data.progress,
135
- uploadEvent.data.total,
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
- <TOutput,>(
137
+ (
146
138
  flowId: string,
147
- callbacks: FlowManagerCallbacks<TOutput>,
148
- options: FlowUploadOptions<TOutput>,
149
- ): FlowManager<unknown, TOutput> => {
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 as FlowManager<unknown, TOutput>;
147
+ return existing.manager;
156
148
  }
157
149
 
158
150
  // Create new manager using client from hook scope
159
- const flowUploadFn = (
160
- input: unknown,
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: manager as FlowManager<unknown, unknown>,
158
+ manager,
175
159
  refCount: 1,
176
160
  flowId,
177
161
  });
@@ -1,10 +1,9 @@
1
- import {
2
- type FlowManager,
3
- type FlowUploadState,
4
- type FlowUploadStatus,
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, UploadFile> | null>(null);
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: (_uploadId: string, bytesUploaded: number, totalBytes: number | null) => {
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: (chunkSize: number, bytesAccepted: number, bytesTotal: number | null) => {
103
- callbacksRef.current.onChunkComplete?.(chunkSize, bytesAccepted, bytesTotal);
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: (result: UploadFile) => {
109
- callbacksRef.current.onSuccess?.(result);
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
- callbacksRef.current.onAbort?.();
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
- flowId,
122
- stableCallbacks,
123
- {
124
- flowConfig: {
125
- flowId: options.flowId,
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
- }, [options.flowId, options.storageId, options.outputNodeId, getManager, releaseManager]);
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], { type: item.file.data.mimeType })
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
@@ -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?: unknown;
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?: (result: unknown) => void;
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
- progress: number,
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
- progress: number,
352
+ uploadId: string,
348
353
  bytesUploaded: number,
349
354
  totalBytes: number | null,
350
355
  ) => void;