import AsyncStorage from '@react-native-async-storage/async-storage' import { Linking, Platform } from 'react-native' import { getConfig } from '../core/config' import { apiRequest } from '../core/http' export interface AppUpdateInfo { needsUpdate: boolean versionName?: string versionCode?: number downloadUrl?: string changeLog?: string forceUpdate?: boolean appStoreUrl?: string marketUrl?: string } export interface RnUpdateInfo { needsUpdate: boolean latestVersion: string downloadUrl: string md5: string minCommonVersion: string note: string packageName?: string packageMatched?: boolean } export interface CachedRnBundle { moduleId: string version: string md5: string downloadedAt: string source: string } function getBundleCacheKey(moduleId: string) { return `@xuqm_sdk_rn_bundle:${moduleId}` } function normalizeDownloadUrl(rawUrl?: string) { if (!rawUrl) return rawUrl // Compatible with legacy server config where update base URL already includes /api/v1/updates. if (rawUrl.includes('/api/v1/updates/api/v1/rn/files/')) { return rawUrl.replace('/api/v1/updates/api/v1/rn/files/', '/api/v1/rn/files/') } if (rawUrl.includes('/files/apk/')) { try { const url = new URL(rawUrl) if (url.pathname.startsWith('/files/apk/')) { return `${url.origin}/api/v1/updates${url.pathname}${url.search}` } } catch { return rawUrl } } return rawUrl } export const UpdateSDK = { async checkAppUpdate(currentVersionCode: number): Promise { const config = getConfig() const result = await apiRequest('/api/v1/updates/app/check', { params: { appId: config.appKey, platform: Platform.OS === 'android' ? 'ANDROID' : 'IOS', currentVersionCode: String(currentVersionCode), }, }) return { ...result, downloadUrl: normalizeDownloadUrl(result.downloadUrl), } }, async openAppStore(appStoreUrl?: string, marketUrl?: string): Promise { const url = Platform.OS === 'ios' ? appStoreUrl : marketUrl if (url) await Linking.openURL(url) }, async checkRnUpdate(moduleId: string, currentVersion: string, packageName?: string): Promise { const config = getConfig() const result = await apiRequest('/api/v1/rn/update/check', { params: { appId: config.appKey, moduleId, platform: Platform.OS === 'android' ? 'ANDROID' : 'IOS', currentVersion, ...(packageName ? { packageName } : {}), }, }) if (packageName && result.packageMatched === false) { return { ...result, needsUpdate: false, } } return { ...result, downloadUrl: normalizeDownloadUrl(result.downloadUrl) ?? result.downloadUrl, } }, async downloadRnBundle(downloadUrl: string): Promise { const response = await fetch(downloadUrl) if (!response.ok) { throw new Error(`Failed to download bundle: ${response.status}`) } return response.text() }, async cacheRnBundle(moduleId: string, version: string, md5: string, source: string): Promise { const payload: CachedRnBundle = { moduleId, version, md5, source, downloadedAt: new Date().toISOString(), } await AsyncStorage.setItem(getBundleCacheKey(moduleId), JSON.stringify(payload)) return payload }, async getCachedRnBundle(moduleId: string): Promise { const raw = await AsyncStorage.getItem(getBundleCacheKey(moduleId)) return raw ? JSON.parse(raw) as CachedRnBundle : null }, }