/** * Derived directly from NetInfoState, but we don't want to force you to use * that package if you don't want. So we take just the subset of fields we * actually use. */ export interface DownloadQueueNetInfoState { /** * NetInfo's isConnected. They insist on accepting the null. */ isConnected: boolean | null; /** * This should ideally be "unknown" | "none" | "wifi" | "cellular" | * "bluetooth" | "ethernet" | "wimax" | "vpn" | "other" | "mixed", to match * NetInfoStateType. However, by locking into that right now, this library * will break (Typescript-wise) if NetInfo adds new types of connections. So * we compromise and just accept "string". You should only use valid values, * though, if you want reasonable behavior from this library. */ type: string; } export declare type DownloadQueueNetInfoUnsubscribe = () => void; /** * A strict subset (in types) of NetInfo's addEventListener. Unless you * implement your own network detection, you should probably just pass * NetInfo.addEventListener. */ export declare type DownloadQueueAddEventListener = (listener: (state: DownloadQueueNetInfoState) => void) => DownloadQueueNetInfoUnsubscribe; export interface DownloadQueueStatus { url: string; path: string; complete: boolean; } export interface DownloadQueueHandlers { onBegin?: (url: string, totalBytes: number) => void; onProgress?: (url: string, fractionWritten: number, bytesWritten: number, totalBytes: number) => void; onDone?: (url: string, localPath: string) => void; /** * This is async because `removeUrl` (and also `setQueue`, when it needs to * remove some urls) will block until you return from this, giving you the * opportunity in your app to remove any dependencies on the local file before * it's deleted. */ onWillRemove?: (url: string) => Promise; onError?: (url: string, error: any) => void; } /** * Optional settings to pass to DownloadQueue.init() */ export interface DownloadQueueOptions { /** * By default, AsyncStorage keys and RNFS filenames are prefixed with * "DownloadQueue/main". If you want to use something other than "main", pass * it here. This is commonly used to manage different queues for different * users (e.g. you can use userId as the domain). */ domain?: string; /** * Callbacks for events related to ongoing downloads */ handlers?: DownloadQueueHandlers; /** * If you'd like DownloadQueue to pause downloads when the device is offline, * pass this. Usually easiest to literally pass `NetInfo.addEventListener`. */ netInfoAddEventListener?: DownloadQueueAddEventListener; /** * Callback that gets the current network state. If you pass * `netInfoAddEventListener`, you must pass this as well. The easiest thing * is usually to pass `NetInfo.fetch`. */ netInfoFetchState?: () => Promise; /** * The NetInfoStateType values for which downloads will be allowed. Only works * if you also pass `netInfoAddEventListener`. If `activeNetworkTypes` is * undefined or [], downloads will happen on all connection types. A common * practice is to pass ["wifi", "ethernet"] if you want to help users avoid * cell data charges. As of @react-native-community/netinfo@9.3.7, valid * values are "unknown" | "none" | "wifi" | "cellular" | "bluetooth" | * "ethernet" | "wimax" | "vpn" | "other" | "mixed". */ activeNetworkTypes?: string[]; /** * Whether to start the queue in an active state where downloads will be * started. If false, no downloads will begin until you call resumeAll(). */ startActive?: boolean; /** * Callback used to get a pathname from a URL. By default, files are saved * without any particular extension. But if you need the server extension to * be preserved (e.g. you pass the file to a media player that uses the * extension to determine its data format), pass a function here that returns * a path given a URL (e.g. for `https://foo.com/baz/moo.mp3?q=song`, returns * `baz/moo.mp3`). The easiest way to implement this, if you already have * a React Native polyfill for URL, is: * * function urlToPath(url) { * const parsed = new URL(url); * return parsed.pathname; * } * * If you don't have a polyfill, you can use something like * https://www.npmjs.com/package/react-native-url-polyfill */ urlToPath?: (url: string) => string; } /** * A queue for downloading files in the background. You should call init() * before using any other methods. A suggested practice is to have one queue * per userId, using that userId as the queue's `domain`, if you want downloads * several users to occur concurrently and not interfere with each other. */ export default class DownloadQueue { private domain; private specs; private tasks; private inited; private kvfs; private handlers?; private active; private urlToPath?; private erroredIds; private errorTimer; private netInfoUnsubscriber?; private netInfoFetchState?; private activeNetworkTypes; private wouldAutoPause; private isPausedByUser; /** * Gets everything started (e.g. reconstitutes state from storage and * reconciles it with downloads that might have completed in the background, * subscribes to events, etc). You must call this first. * * @param options (optional) Configuration for the queue * @param options.handlers (optional) Callbacks for events * @param options.domain (optional) By default, AsyncStorage keys and RNFS * filenames are prefixed with "DownloadQueue/main". If you want to use * something other than "main", pass it here. This is commonly used to * manage different queues for different users (e.g. you can use userId * as the domain). * @param options.startActive (optional) Whether to start the queue in an * active state where downloads will be started. If false, no downloads will * begin until you call resumeAll(). * @param options.netInfoAddEventListener (optional) If you'd like * DownloadQueue to pause downloads when the device is offline, pass this. * Usually easiest to literally pass `NetInfo.addEventListener`. * @param options.netInfoFetchState (optional )Callback that gets the current * network state. If you pass `netInfoAddEventListener`, you must pass this as * well. The easiest thing is usually to pass `NetInfo.fetch`. * @param options.activeNetworkTypes (optional) The NetInfoStateType values * for which downloads will be allowed. Only works if you also pass * `netInfoAddEventListener`. If `activeNetworkTypes` is undefined or [], * downloads will happen on all connection types. A common practice is to pass * ["wifi", "ethernet"] if you want to help users avoid cell data charges. As * of @react-native-community/netinfo@9.3.7, valid values are "unknown" | * "none" | "wifi" | "cellular" | "bluetooth" | "ethernet" | "wimax" | "vpn" | * "other" | "mixed". */ init({ domain, handlers, netInfoAddEventListener, netInfoFetchState, activeNetworkTypes, startActive, urlToPath, }?: DownloadQueueOptions): Promise; /** * Terminates all pending downloads and stops all activity, including * processing lazy-deletes. You can re-init() if you'd like -- but in most * cases where you plan to re-init, pause() might be what you really meant. */ terminate(): void; /** * Downloads a url to the local documents directory. Safe to call if it's * already been added before. If it's been lazy-deleted, it'll be revived. * * @param url Remote url to download */ addUrl(url: string): Promise; /** * Removes a url record and any associated file that's been downloaded. Can * optionally be a lazy delete. * * @param url Url to remove, including the downloaded file associated with it * @param deleteTime (optional) The timestamp beyond which the file associated * with the url should be deleted, or zero if it should be deleted the next * time DownloadQueue is initialized. The record of the url, in the meantime, * won't be acknowledged via DownloadQueue's API. */ removeUrl(url: string, deleteTime?: number): Promise; private removeUrlInternal; /** * Sets the sum total of urls to keep in the queue. If previously-added urls * don't show up here, they'll be removed. New urls will be added. * * @param deleteTime (optional) The timestamp beyond which files associated * with removed urls should be deleted, or zero if they should be deleted the * next time DownloadQueue is initialized. The record of those urls, in the * meantime, won't be acknowledged via DownloadQueue's API. */ setQueue(urls: string[], deleteTime?: number): Promise; /** * Returns the status of all urls in the queue, excluding urls marked for * deletion. * * @returns urls, paths to local files, and whether the file has been * completely downloaded. If `!complete`, the file may be only partially * downloaded. */ getQueueStatus(): Promise; /** * Returns the status of a single url in the queue, excluding urls marked for * deletion. */ getStatus(url: string): Promise; /** * Pauses all active downloads. Most used to implement wifi-only downloads, * by pausing when NetInfo reports a non-wifi connection. */ pauseAll(): void; private pauseAllInternal; /** * Resumes all active downloads that were previously paused. If you init() * with startActive === false, you'll want to call this at some point or else * downloads will never happen. Also, downloads will only proceed if the * network connection type passes the `activeNetworkTypes` filter (which by * default allows all connection types). */ resumeAll(): void; private resumeAllInternal; /** * Gets a remote or local url, preferring to the local path when possible. If * the local file hasn't yet been downloaded, returns the remote url. Also * returns the remote url if the record is being lazy-deleted. * @param url The remote URL to check for local availability * @returns A local file path if the URL has already been downloaded, else url */ getAvailableUrl(url: string): Promise; private removeTask; private start; /** * Sets the types of networks which you want downloads to occur on. * @param types The network types to allow downloads on. These should come * from `NetInfo.NetInfoStateType`, e.g. `["wifi", "cellular"]`. If you pass * an empty array, downloads will happen under all network connection types. */ setActiveNetworkTypes(types: string[]): Promise; private addTask; private doDone; private ensureErrorTimerOn; private retryErroredTasks; private scheduleDeletions; /** * Doesn't handle createTime === 0 cases, which are deleted during init() * @param basisTimestamp The timestamp to use as the basis for deletion */ private deleteExpiredSpecs; private onNetInfoChanged; private reviveTask; /** * Makes sure, if a spec thinks it's finished, that the file which backs it * actually exists. If that file doesn't exist, we set finished === false. * @returns true if spec is finished and the file exists */ private reconcileFinishStateWithFile; private isTaskDownloading; private extensionFromUri; private getDirFilenames; private getDomainedBasePath; private pathFromId; private keyFromId; /** * Should be run on every public method */ private verifyInitialized; }