UNPKG

@applicaster/zapp-react-native-bridge

Version:

Applicaster Zapp React Native modules

178 lines (144 loc) 4.45 kB
import { NativeModules } from "react-native"; import * as R from "ramda"; import md5 from "md5"; import { reducePromises } from "@applicaster/zapp-react-native-utils/arrayUtils"; import { bridgeLogger } from "../logger"; const { OfflineAssetsBridge } = NativeModules as { OfflineAssetsBridge: OfflineAssetsBridgeI; }; const defaultOptions = { abortOnFail: false }; export function isAssetCacheEnabled(): boolean { return typeof OfflineAssetsBridge !== "undefined"; } const reduceArrayResponse = R.when( Array.isArray, R.reduce((results, fileResult) => { return R.mergeLeft( results, R.zipObj(R.keys(fileResult), R.values(fileResult)) ); }, {}) ); export async function storeFiles( files: FilesList, options: StoreFileOptions = defaultOptions ): Promise<StoredFilesResult> { const result = await OfflineAssetsBridge?.storeFiles?.(files, options); return reduceArrayResponse(result); } export function deleteFolders(folders: string[]): Promise<boolean> { return reducePromises<string>( (current: string, _) => OfflineAssetsBridge?.delete?.(current), Promise.resolve(), folders ); } export function getNativeRootPath(): Promise<string> { return OfflineAssetsBridge?.getFilesDirectory?.(); } function isImageAsset(value) { if (typeof value !== "string") { return false; } return /\.(jpg|png|jpeg)$/.test(value.split("?")[0]); } function getNativeFileName(nativeRootPath, url, path) { const fileNameWithoutQuery = url.split("?")[0]; const extension = R.compose(R.last, R.split("."))(fileNameWithoutQuery); return `${nativeRootPath}/${path}/${md5(url)}.${extension}`; } function propOrIndex(value) { const number = Number(value); return Number.isNaN(number) ? value : number; } export const fileIsSaved = R.curry((savedFilesResult, { file }) => { if (typeof savedFilesResult === "undefined") return true; return ( savedFilesResult?.[file] || savedFilesResult?.[file?.replace("file://", "")] || savedFilesResult?.[`file://${file}`] ); }); export const remapAssetPath = ( assets: FilesList, source: Record<string, any>, localFiles: Record<string, string>, tag: string = "unknown" ): Record<string, any> => { const startTime = performance.now(); const result = assets.reduce( (obj, { path, url }) => { const localFile = localFiles[url]; if (!localFile) { bridgeLogger.warning({ message: `remapAssetPath: Could not replace asset with local file for url: ${url}`, data: { path, obj }, }); return obj; } let target = obj; // Traverse the path to the appropriate nested object for (let i = 0; i < path.length - 1; i++) { target = target[path[i]] = target[path[i]]; } if (!target) { bridgeLogger.error({ message: `remapAssetPath: Could not find path ${path.join(".")}`, data: obj, }); } else { // Set the final value target[path[path.length - 1]] = localFile; } return obj; }, JSON.parse(JSON.stringify(source)) ); bridgeLogger.log({ message: `remapAssetPath: remapping took ${ performance.now() - startTime } ms, tag: ${tag}`, }); return result; }; export function collectAssets(object, nativeRootPath, source, path = []) { if (!object) { const message = `collectAssets: object is null or undefined for source: ${source}`; bridgeLogger.error({ message, data: { object, path } }); throw new Error(message); } return Object.entries(object).reduce((assets, [prop, value]) => { const propPath = [...path, propOrIndex(prop)]; if (R.is(Object, value) && !R.isEmpty(value)) { assets.push(...collectAssets(value, nativeRootPath, source, propPath)); } else if (isImageAsset(value)) { assets.push({ url: value, path: propPath, file: getNativeFileName(nativeRootPath, value, source), source, }); } return assets; }, []); } export const getFolders = R.compose( R.uniq, // @ts-ignore R.map(R.compose(R.join("/"), R.init, R.split("/"), R.prop("file"))) ); if (process.env.NODE_ENV === "test") { module.exports = { isImageAsset, getNativeFileName, propOrIndex, remapAssetPath, collectAssets, getFolders, deleteFolders, getNativeRootPath, storeFiles, reduceArrayResponse, fileIsSaved, }; }