UNPKG

react-native-code-sync

Version:

React-native plugin for the code-sync functionality on live via OTA (Over-the-air)

193 lines (179 loc) 7.83 kB
import { useMemo } from 'react'; import { Platform } from 'react-native'; import ReactNativeBlobUtil from 'react-native-blob-util'; import { unzip } from 'react-native-zip-archive' import RNFS from 'react-native-fs'; import { Utility } from '../utils' import OtaApiConstant from './OtaApiConstant.json'; const OTA_ROOT_PATH = RNFS.DocumentDirectoryPath export const useCheckVersion = () => { const bundleFileName = useMemo(() => { return Platform.select({ android: 'index.android.bundle', ios: 'main.jsbundle' }) }, []) const installFail = (message) => Utility.log("Update failed:", message); const downloadBundleFile = async (uri, downloadProgress) => { if (!uri) return ''; const filename = uri.split('/').pop(); const downloadPath = `${OTA_ROOT_PATH}/${filename}`; try { const downloadResult = await RNFS.downloadFile({ fromUrl: uri, toFile: downloadPath, begin: res => { }, progress: res => { const progressPercent = (res.bytesWritten / res.contentLength) * 100; downloadProgress?.(progressPercent); } }).promise; return downloadResult.statusCode === 200 ? downloadPath : ''; } catch (error) { Utility.log("Download error:", error); return ''; } }; const deleteOldBundleIfNeeded = async () => { const filePath = `${OTA_ROOT_PATH}/${bundleFileName}`; const fileExists = await Utility.checkFileExists(filePath); if (fileExists) { try { await ReactNativeBlobUtil.fs.unlink(filePath); return true; } catch (error) { Utility.log("Error deleting old bundle:", error); } } return false; }; const extractZipFile = async (zipFilePath = '') => { if (!zipFilePath) return false; try { const extractedPath = `${OTA_ROOT_PATH}/${Date.now()}`; await unzip(zipFilePath, extractedPath); const files = await ReactNativeBlobUtil.fs.ls(extractedPath); if (files?.length) { const androidDirectoryName = 'output' if (!files?.includes(androidDirectoryName)) return false const outputfiles = `${extractedPath}/${androidDirectoryName}` const outputDirectoryFiles = await ReactNativeBlobUtil.fs.ls(outputfiles); if (outputDirectoryFiles.includes(bundleFileName)) { await deleteOldBundleIfNeeded(); return await moveBundleFile(outputfiles, [extractedPath, zipFilePath], outputDirectoryFiles); } } } catch (error) { Utility.log("Unzip error:", error); } return false } const moveBundleFile = async (extractedPath, filesToDelete = [], bundleFiles = []) => { try { for (const element of bundleFiles) { const completetargetPath = `${OTA_ROOT_PATH}/${element}` const completeExtractedPath = `${extractedPath}/${element}` if (await Utility.checkFileExists(completetargetPath)) { await ReactNativeBlobUtil.fs.unlink(completetargetPath) } if (await Utility.checkFileExists(completeExtractedPath)) { await ReactNativeBlobUtil.fs.mv(completeExtractedPath, completetargetPath); } } await Promise.all(filesToDelete.map(path => ReactNativeBlobUtil.fs.unlink(path))); return true; } catch (error) { Utility.log("Move file error:", error); } return false; }; const setupBundlePath = async (path, curentbundleVersion, onUpdateComplete, onUpdateFailed) => { try { if (!path || !(await extractZipFile(path))) { onUpdateFailed?.(); return; } const otaVersionInfo = { ota_version_code: curentbundleVersion?.toString(), ota_app_version: Utility.getAppVersion()?.toString() } Utility.log("otaVersionInfo===>", otaVersionInfo) Utility.setAsyncStorage({ key: OtaApiConstant.LOCAL_KEYS.OTA_INFO, value: JSON.stringify(otaVersionInfo) }) onUpdateComplete?.(); } catch (err) { onUpdateFailed?.() } } const startUpdate = async (url, curentbundleVersion, downloadProgress, onUpdateComplete, onUpdateFailed) => { try { if (!url || !curentbundleVersion) return installFail("Invalid URL or version"); // if (!(await Utility.requestStoragePermission())) { // Utility.showToast('Storage permission not provided'); // onUpdateFailed?.() // return installFail("Storage permission denied"); // } const downloadedPath = await downloadBundleFile(url, downloadProgress) if (downloadedPath) await setupBundlePath(downloadedPath, curentbundleVersion, onUpdateComplete, onUpdateFailed); else { onUpdateFailed?.() } } catch (error) { Utility.log("Update error:", error); onUpdateFailed?.() } } const checkForUpgradedAppVersionAvailable = async () => { if (Utility.checkForDevMode()) return const otaInfo = await Utility.getAsyncStorage({ key: OtaApiConstant.LOCAL_KEYS.OTA_INFO }) let previousAppVersion = null if (otaInfo) { const parsedOTAInfo = JSON.parse(otaInfo) previousAppVersion = parsedOTAInfo.ota_app_version } const installedAppVersion = Utility.getAppVersion() const convertedInstalledAppVersion = Number(installedAppVersion?.toString().replace(/\./g, '')) const convertedPreviousAppversion = Number(previousAppVersion?.toString().replace(/\./g, '')) if (convertedInstalledAppVersion > convertedPreviousAppversion) { await deleteOldBundleIfNeeded() const otaVersionInfo = { ota_version_code: '0', ota_app_version: installedAppVersion?.toString() } Utility.setAsyncStorage({ key: OtaApiConstant.LOCAL_KEYS.OTA_INFO, value: JSON.stringify(otaVersionInfo) }) Utility.reloadApp() return } } const onCheckVersion = async ({ bundleUrl = '', curentbundleVersion = '', serverAppVersion = '', onStartUpdate = () => { }, downloadProgress = () => { }, onUpdateComplete = () => { }, onUpdateFailed = () => { }, onUpdateNotAvailable = () => { } }) => { if (Utility.checkForDevMode()) return onUpdateNotAvailable?.() const otaInfo = await Utility.getAsyncStorage({ key: OtaApiConstant.LOCAL_KEYS.OTA_INFO }) let previousVersion = null, previousAppVersion = null, installedAppVersion = Utility.getAppVersion() if (otaInfo) { const parsedOTAInfo = JSON.parse(otaInfo) previousVersion = parsedOTAInfo.ota_version_code previousAppVersion = parsedOTAInfo.ota_app_version } if (curentbundleVersion > previousVersion && serverAppVersion == installedAppVersion) { onStartUpdate?.(true); startUpdate( bundleUrl, curentbundleVersion, downloadProgress, onUpdateComplete, onUpdateFailed ) } else onUpdateNotAvailable?.() }; return { onCheckVersion, checkForUpgradedAppVersionAvailable }; }