@markvivanco/app-version-checker
Version:
React App version checking and update prompts for React, React Native, and web applications
1,087 lines (1,077 loc) • 33.1 kB
JavaScript
import React, { createContext, useState, useMemo, useEffect, useCallback, useContext, useRef } from 'react';
// src/core/types.ts
var DEFAULT_CHECK_INTERVALS = {
MIN_CHECK_INTERVAL: 60 * 60 * 1e3,
// 1 hour minimum between checks
REMIND_LATER_DURATION: 24 * 60 * 60 * 1e3
// 24 hours for "remind me later"
};
// src/core/version-compare.ts
function compareVersions(v1, v2) {
const parts1 = v1.split(".").map((num) => parseInt(num, 10));
const parts2 = v2.split(".").map((num) => parseInt(num, 10));
const maxLength = Math.max(parts1.length, parts2.length);
while (parts1.length < maxLength) parts1.push(0);
while (parts2.length < maxLength) parts2.push(0);
for (let i = 0; i < maxLength; i++) {
if (parts1[i] < parts2[i]) return -1;
if (parts1[i] > parts2[i]) return 1;
}
return 0;
}
function isUpdateAvailable(currentVersion, latestVersion) {
return compareVersions(currentVersion, latestVersion) < 0;
}
function parseVersion(version) {
const parts = version.split(".").map((num) => parseInt(num, 10));
return {
major: parts[0] || 0,
minor: parts[1] || 0,
patch: parts[2] || 0,
build: parts[3]
// Optional build number
};
}
function formatVersion(major, minor, patch, build) {
const parts = [major, minor, patch];
if (build !== void 0) {
parts.push(build);
}
return parts.join(".");
}
function getMajorVersion(version) {
return parseVersion(version).major;
}
function getMinorVersion(version) {
return parseVersion(version).minor;
}
function getPatchVersion(version) {
return parseVersion(version).patch;
}
function isValidVersion(version) {
const regex = /^\d+\.\d+\.\d+(\.\d+)?$/;
return regex.test(version);
}
function getVersionDiff(v1, v2) {
const comparison = compareVersions(v1, v2);
if (comparison === 0) {
return { type: "none", fromVersion: v1, toVersion: v2 };
}
const parsed1 = parseVersion(v1);
const parsed2 = parseVersion(v2);
let type = "build";
if (parsed1.major !== parsed2.major) {
type = "major";
} else if (parsed1.minor !== parsed2.minor) {
type = "minor";
} else if (parsed1.patch !== parsed2.patch) {
type = "patch";
}
return {
type,
fromVersion: comparison < 0 ? v1 : v2,
toVersion: comparison < 0 ? v2 : v1
};
}
// src/core/version-formatter.ts
function formatVersionWithBuild(platformVersion, buildNumber) {
if (buildNumber !== void 0 && buildNumber !== null && buildNumber !== "") {
return `${platformVersion}.${buildNumber}`;
}
return platformVersion;
}
function extractBuildNumber(version) {
const parts = version.split(".");
if (parts.length > 3) {
return parts[3];
}
return void 0;
}
function extractBaseVersion(version) {
const parts = version.split(".");
return parts.slice(0, 3).join(".");
}
function normalizeVersion(version, padToLength = 3) {
const parts = version.split(".");
const numericParts = parts.map((part) => parseInt(part, 10)).filter((num) => !isNaN(num));
while (numericParts.length < padToLength) {
numericParts.push(0);
}
return numericParts.join(".");
}
function formatDisplayVersion(version, includePrefix = true) {
const normalized = normalizeVersion(version, 3);
return includePrefix ? `v${normalized}` : normalized;
}
function sortVersions(versions, descending = true) {
const sorted = [...versions].sort((a, b) => {
const aParts = a.split(".").map((num) => parseInt(num, 10));
const bParts = b.split(".").map((num) => parseInt(num, 10));
const maxLength = Math.max(aParts.length, bParts.length);
for (let i = 0; i < maxLength; i++) {
const aPart = aParts[i] || 0;
const bPart = bParts[i] || 0;
if (aPart !== bPart) {
return descending ? bPart - aPart : aPart - bPart;
}
}
return 0;
});
return sorted;
}
function getLatestVersion(versions) {
if (!versions || versions.length === 0) {
return null;
}
const sorted = sortVersions(versions, true);
return sorted[0];
}
// src/core/stores.ts
function getIosStoreUrl(appStoreId, customUrl) {
if (customUrl) {
return customUrl;
}
if (!appStoreId) {
return null;
}
return `https://apps.apple.com/app/id${appStoreId}`;
}
function getAndroidStoreUrl(packageName, customUrl) {
if (customUrl) {
return customUrl;
}
if (!packageName) {
return null;
}
return `https://play.google.com/store/apps/details?id=${packageName}`;
}
function getStoreUrl(platform, config) {
switch (platform) {
case "ios":
return getIosStoreUrl(config.iosAppStoreId, config.iosStoreUrl);
case "android":
return getAndroidStoreUrl(config.androidPackageName, config.androidStoreUrl);
case "web":
return null;
default:
return null;
}
}
function isValidIosAppStoreId(appStoreId) {
return /^\d{9,10}$/.test(appStoreId);
}
function isValidAndroidPackageName(packageName) {
return /^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)+$/.test(packageName);
}
function extractAppIdFromUrl(url, platform) {
if (platform === "ios") {
const match = url.match(/\/id(\d+)/);
return match ? match[1] : null;
}
if (platform === "android") {
const match = url.match(/[?&]id=([^&]+)/);
return match ? match[1] : null;
}
return null;
}
function getStoreName(platform) {
switch (platform) {
case "ios":
return "App Store";
case "android":
return "Google Play Store";
case "web":
return "Web";
default:
return "Unknown";
}
}
function getStoreBadgeUrl(platform, _locale = "en-US") {
switch (platform) {
case "ios":
return `https://developer.apple.com/app-store/marketing/guidelines/images/badge-download-on-the-app-store.svg`;
case "android":
return `https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png`;
default:
return null;
}
}
// src/core/version-checker.ts
var VersionChecker = class {
constructor(dataProvider, storageProvider, options = {}) {
this.initialized = false;
this.dataProvider = dataProvider;
this.storageProvider = storageProvider;
this.options = {
minCheckInterval: options.minCheckInterval ?? DEFAULT_CHECK_INTERVALS.MIN_CHECK_INTERVAL,
remindLaterDuration: options.remindLaterDuration ?? DEFAULT_CHECK_INTERVALS.REMIND_LATER_DURATION,
skipWebPlatform: options.skipWebPlatform ?? true,
getPlatform: options.getPlatform ?? (() => this.detectPlatform())
};
}
/**
* Initialize the version checker
*/
async initialize() {
if (this.initialized) {
return;
}
if (this.dataProvider.initialize) {
await this.dataProvider.initialize();
}
if (this.storageProvider.initialize) {
await this.storageProvider.initialize();
}
this.initialized = true;
}
/**
* Detect the current platform
*/
detectPlatform() {
if (this.dataProvider.getCurrentPlatform) {
return this.dataProvider.getCurrentPlatform();
}
if (typeof window !== "undefined") {
const userAgent = window.navigator?.userAgent || "";
if (/android/i.test(userAgent)) {
return "android";
}
if (/iPad|iPhone|iPod/.test(userAgent)) {
return "ios";
}
return "web";
}
return "web";
}
/**
* Get the current platform
*/
getPlatform() {
return this.options.getPlatform();
}
/**
* Get version information
*/
async getVersionInfo() {
const platform = this.getPlatform();
const currentVersion = await this.dataProvider.getCurrentVersion();
const latestVersion = await this.dataProvider.getLatestVersion(platform);
const appStoreConfig = await this.dataProvider.getAppStoreConfig();
const updateAvailable = latestVersion ? isUpdateAvailable(currentVersion, latestVersion) : false;
const storeUrl = getStoreUrl(platform, appStoreConfig);
return {
currentVersion,
latestVersion,
updateAvailable,
storeUrl,
platform
};
}
/**
* Check if an update is available
*/
async isUpdateAvailable() {
const platform = this.getPlatform();
if (platform === "web" && this.options.skipWebPlatform) {
return false;
}
const versionInfo = await this.getVersionInfo();
return versionInfo.updateAvailable;
}
/**
* Check if we should show the update prompt
*/
async shouldShowUpdatePrompt() {
const platform = this.getPlatform();
if (platform === "web" && this.options.skipWebPlatform) {
const versionInfo = await this.getVersionInfo();
return {
shouldShowPrompt: false,
versionInfo,
skipReason: "web_platform"
};
}
try {
const versionInfo = await this.getVersionInfo();
if (!versionInfo.updateAvailable) {
return {
shouldShowPrompt: false,
versionInfo,
skipReason: "no_update"
};
}
const remindLaterTime = await this.storageProvider.getRemindLaterTime();
if (remindLaterTime && Date.now() < remindLaterTime) {
return {
shouldShowPrompt: false,
versionInfo,
skipReason: "remind_later"
};
}
const lastCheckTime = await this.storageProvider.getLastCheckTime();
if (lastCheckTime && Date.now() - lastCheckTime < this.options.minCheckInterval) {
return {
shouldShowPrompt: false,
versionInfo,
skipReason: "too_soon"
};
}
if (this.storageProvider.getLastShownVersion) {
const lastShownVersion = await this.storageProvider.getLastShownVersion();
if (lastShownVersion === versionInfo.latestVersion) {
if (this.dataProvider.isUpdateMandatory) {
const isMandatory = await this.dataProvider.isUpdateMandatory(
versionInfo.currentVersion,
versionInfo.latestVersion
);
if (!isMandatory) {
return {
shouldShowPrompt: false,
versionInfo,
skipReason: "remind_later"
};
}
} else {
return {
shouldShowPrompt: false,
versionInfo,
skipReason: "remind_later"
};
}
}
}
await this.storageProvider.setLastCheckTime(Date.now());
if (this.storageProvider.setLastShownVersion && versionInfo.latestVersion) {
await this.storageProvider.setLastShownVersion(versionInfo.latestVersion);
}
return {
shouldShowPrompt: true,
versionInfo
};
} catch (error) {
console.error("Error checking for updates:", error);
const versionInfo = await this.getVersionInfo();
return {
shouldShowPrompt: false,
versionInfo,
skipReason: "error"
};
}
}
/**
* Set "remind me later" for the update prompt
*/
async setRemindMeLater() {
const remindTime = Date.now() + this.options.remindLaterDuration;
await this.storageProvider.setRemindLaterTime(remindTime);
if (this.storageProvider.incrementDismissCount) {
await this.storageProvider.incrementDismissCount();
}
}
/**
* Clear the "remind me later" setting
*/
async clearRemindMeLater() {
await this.storageProvider.clearRemindLaterTime();
}
/**
* Check if update is mandatory
*/
async isUpdateMandatory() {
if (!this.dataProvider.isUpdateMandatory) {
return false;
}
const versionInfo = await this.getVersionInfo();
if (!versionInfo.latestVersion) {
return false;
}
return await this.dataProvider.isUpdateMandatory(
versionInfo.currentVersion,
versionInfo.latestVersion
);
}
/**
* Get changelog for the latest version
*/
async getChangeLog() {
if (!this.dataProvider.getChangeLog) {
return null;
}
const versionInfo = await this.getVersionInfo();
if (!versionInfo.latestVersion) {
return null;
}
return await this.dataProvider.getChangeLog(versionInfo.latestVersion);
}
/**
* Reset all version check data (useful for testing)
*/
async resetVersionCheckData() {
await this.storageProvider.clearRemindLaterTime();
await this.storageProvider.setLastCheckTime(0);
if (this.storageProvider.clearAll) {
await this.storageProvider.clearAll();
}
}
/**
* Get formatted version string
*/
async getFormattedVersion() {
if (this.dataProvider.getFormattedVersion) {
return await this.dataProvider.getFormattedVersion();
}
return await this.dataProvider.getCurrentVersion();
}
/**
* Dispose of resources
*/
async dispose() {
if (this.dataProvider.dispose) {
await this.dataProvider.dispose();
}
if (this.storageProvider.dispose) {
await this.storageProvider.dispose();
}
this.initialized = false;
}
};
// src/providers/data-provider.interface.ts
var BaseVersionDataProvider = class {
getCurrentPlatform() {
if (typeof window !== "undefined" && !("ReactNativeWebView" in window)) {
return "web";
}
return "web";
}
async initialize() {
}
async dispose() {
}
};
// src/providers/storage-provider.interface.ts
var BaseStorageProvider = class {
async getDismissCount() {
return 0;
}
async incrementDismissCount() {
}
async initialize() {
}
async dispose() {
}
};
var InMemoryStorageProvider = class extends BaseStorageProvider {
constructor() {
super(...arguments);
this.storage = /* @__PURE__ */ new Map();
}
async getLastCheckTime() {
return this.storage.get("lastCheckTime") || null;
}
async setLastCheckTime(timestamp) {
this.storage.set("lastCheckTime", timestamp);
}
async getRemindLaterTime() {
return this.storage.get("remindLaterTime") || null;
}
async setRemindLaterTime(timestamp) {
this.storage.set("remindLaterTime", timestamp);
}
async clearRemindLaterTime() {
this.storage.delete("remindLaterTime");
}
async getDismissCount() {
return this.storage.get("dismissCount") || 0;
}
async incrementDismissCount() {
const current = await this.getDismissCount();
this.storage.set("dismissCount", current + 1);
}
async getLastShownVersion() {
return this.storage.get("lastShownVersion") || null;
}
async setLastShownVersion(version) {
this.storage.set("lastShownVersion", version);
}
async clearAll() {
this.storage.clear();
}
async getAllPreferences() {
const preferences = {};
this.storage.forEach((value, key) => {
preferences[key] = value;
});
return preferences;
}
};
// src/adapters/stores/local-storage-provider.ts
var LocalStorageProvider = class extends BaseStorageProvider {
constructor(prefix = "app_version_check_") {
super();
this.prefix = prefix;
}
getKey(key) {
return `${this.prefix}${key}`;
}
async getLastCheckTime() {
try {
const value = localStorage.getItem(this.getKey("lastCheckTime"));
return value ? parseInt(value, 10) : null;
} catch (error) {
console.error("Error reading last check time:", error);
return null;
}
}
async setLastCheckTime(timestamp) {
try {
localStorage.setItem(this.getKey("lastCheckTime"), timestamp.toString());
} catch (error) {
console.error("Error setting last check time:", error);
}
}
async getRemindLaterTime() {
try {
const value = localStorage.getItem(this.getKey("remindLaterTime"));
return value ? parseInt(value, 10) : null;
} catch (error) {
console.error("Error reading remind later time:", error);
return null;
}
}
async setRemindLaterTime(timestamp) {
try {
localStorage.setItem(this.getKey("remindLaterTime"), timestamp.toString());
} catch (error) {
console.error("Error setting remind later time:", error);
}
}
async clearRemindLaterTime() {
try {
localStorage.removeItem(this.getKey("remindLaterTime"));
} catch (error) {
console.error("Error clearing remind later time:", error);
}
}
async getDismissCount() {
try {
const value = localStorage.getItem(this.getKey("dismissCount"));
return value ? parseInt(value, 10) : 0;
} catch (error) {
console.error("Error reading dismiss count:", error);
return 0;
}
}
async incrementDismissCount() {
try {
const current = await this.getDismissCount();
localStorage.setItem(this.getKey("dismissCount"), (current + 1).toString());
} catch (error) {
console.error("Error incrementing dismiss count:", error);
}
}
async getLastShownVersion() {
try {
return localStorage.getItem(this.getKey("lastShownVersion"));
} catch (error) {
console.error("Error reading last shown version:", error);
return null;
}
}
async setLastShownVersion(version) {
try {
localStorage.setItem(this.getKey("lastShownVersion"), version);
} catch (error) {
console.error("Error setting last shown version:", error);
}
}
async getAutoUpdateEnabled() {
try {
const value = localStorage.getItem(this.getKey("autoUpdateEnabled"));
return value === "true";
} catch (error) {
console.error("Error reading auto update preference:", error);
return false;
}
}
async setAutoUpdateEnabled(enabled) {
try {
localStorage.setItem(this.getKey("autoUpdateEnabled"), enabled.toString());
} catch (error) {
console.error("Error setting auto update preference:", error);
}
}
async getAllPreferences() {
const preferences = {};
const keys = [
"lastCheckTime",
"remindLaterTime",
"dismissCount",
"lastShownVersion",
"autoUpdateEnabled"
];
for (const key of keys) {
try {
const value = localStorage.getItem(this.getKey(key));
if (value !== null) {
preferences[key] = value;
}
} catch (error) {
console.error(`Error reading ${key}:`, error);
}
}
return preferences;
}
async clearAll() {
const keysToRemove = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && key.startsWith(this.prefix)) {
keysToRemove.push(key);
}
}
for (const key of keysToRemove) {
try {
localStorage.removeItem(key);
} catch (error) {
console.error(`Error removing ${key}:`, error);
}
}
}
};
// src/adapters/stores/async-storage-provider.ts
var AsyncStorageProvider = class extends BaseStorageProvider {
constructor(storage, prefix = "app_version_check_") {
super();
this.storage = storage;
this.prefix = prefix;
}
getKey(key) {
return `${this.prefix}${key}`;
}
async getLastCheckTime() {
try {
const value = await this.storage.getItem(this.getKey("lastCheckTime"));
return value ? parseInt(value, 10) : null;
} catch (error) {
console.error("Error reading last check time:", error);
return null;
}
}
async setLastCheckTime(timestamp) {
try {
await this.storage.setItem(this.getKey("lastCheckTime"), timestamp.toString());
} catch (error) {
console.error("Error setting last check time:", error);
}
}
async getRemindLaterTime() {
try {
const value = await this.storage.getItem(this.getKey("remindLaterTime"));
return value ? parseInt(value, 10) : null;
} catch (error) {
console.error("Error reading remind later time:", error);
return null;
}
}
async setRemindLaterTime(timestamp) {
try {
await this.storage.setItem(this.getKey("remindLaterTime"), timestamp.toString());
} catch (error) {
console.error("Error setting remind later time:", error);
}
}
async clearRemindLaterTime() {
try {
await this.storage.removeItem(this.getKey("remindLaterTime"));
} catch (error) {
console.error("Error clearing remind later time:", error);
}
}
async getDismissCount() {
try {
const value = await this.storage.getItem(this.getKey("dismissCount"));
return value ? parseInt(value, 10) : 0;
} catch (error) {
console.error("Error reading dismiss count:", error);
return 0;
}
}
async incrementDismissCount() {
try {
const current = await this.getDismissCount();
await this.storage.setItem(this.getKey("dismissCount"), (current + 1).toString());
} catch (error) {
console.error("Error incrementing dismiss count:", error);
}
}
async getLastShownVersion() {
try {
return await this.storage.getItem(this.getKey("lastShownVersion"));
} catch (error) {
console.error("Error reading last shown version:", error);
return null;
}
}
async setLastShownVersion(version) {
try {
await this.storage.setItem(this.getKey("lastShownVersion"), version);
} catch (error) {
console.error("Error setting last shown version:", error);
}
}
async getAutoUpdateEnabled() {
try {
const value = await this.storage.getItem(this.getKey("autoUpdateEnabled"));
return value === "true";
} catch (error) {
console.error("Error reading auto update preference:", error);
return false;
}
}
async setAutoUpdateEnabled(enabled) {
try {
await this.storage.setItem(this.getKey("autoUpdateEnabled"), enabled.toString());
} catch (error) {
console.error("Error setting auto update preference:", error);
}
}
async getAllPreferences() {
const preferences = {};
const keys = [
"lastCheckTime",
"remindLaterTime",
"dismissCount",
"lastShownVersion",
"autoUpdateEnabled"
];
for (const key of keys) {
try {
const value = await this.storage.getItem(this.getKey(key));
if (value !== null) {
preferences[key] = value;
}
} catch (error) {
console.error(`Error reading ${key}:`, error);
}
}
return preferences;
}
async clearAll() {
if (this.storage.getAllKeys && this.storage.multiRemove) {
try {
const allKeys = await this.storage.getAllKeys();
const keysToRemove = allKeys.filter((key) => key.startsWith(this.prefix));
if (keysToRemove.length > 0) {
await this.storage.multiRemove(keysToRemove);
}
} catch (error) {
console.error("Error clearing all preferences:", error);
}
} else {
const keys = [
"lastCheckTime",
"remindLaterTime",
"dismissCount",
"lastShownVersion",
"autoUpdateEnabled"
];
for (const key of keys) {
try {
await this.storage.removeItem(this.getKey(key));
} catch (error) {
console.error(`Error removing ${key}:`, error);
}
}
}
}
};
var VersionCheckContext = createContext(void 0);
var VersionCheckProvider = ({
children,
dataProvider,
storageProvider,
options = {},
checkOnMount = true,
checkOnForeground = false,
onOpenStore,
onShowUpdateDialog,
onHideUpdateDialog,
renderDialog = true,
dialogComponent: DialogComponent
}) => {
const [versionInfo, setVersionInfo] = useState(null);
const [showUpdateDialog, setShowUpdateDialog] = useState(false);
const [isChecking, setIsChecking] = useState(false);
const [error, setError] = useState(null);
const [currentVersion, setCurrentVersion] = useState(null);
const [formattedVersion, setFormattedVersion] = useState(null);
const versionChecker = useMemo(
() => new VersionChecker(dataProvider, storageProvider, options),
[dataProvider, storageProvider, options]
);
useEffect(() => {
versionChecker.initialize().catch(console.error);
return () => {
versionChecker.dispose().catch(console.error);
};
}, [versionChecker]);
useEffect(() => {
const loadVersions = async () => {
try {
const current = await dataProvider.getCurrentVersion();
setCurrentVersion(current);
const formatted = dataProvider.getFormattedVersion ? await dataProvider.getFormattedVersion() : current;
setFormattedVersion(formatted);
} catch (err) {
console.error("Error loading versions:", err);
}
};
loadVersions();
}, [dataProvider]);
const checkForUpdates = useCallback(async () => {
if (isChecking) return;
setIsChecking(true);
setError(null);
try {
const result = await versionChecker.shouldShowUpdatePrompt();
const info = result.versionInfo;
setVersionInfo(info);
if (result.shouldShowPrompt) {
setShowUpdateDialog(true);
onShowUpdateDialog?.(info);
}
} catch (err) {
const error2 = err instanceof Error ? err : new Error(String(err));
setError(error2);
console.error("Error checking for updates:", error2);
} finally {
setIsChecking(false);
}
}, [versionChecker, isChecking, onShowUpdateDialog]);
const handleUpdateNow = useCallback(async () => {
setShowUpdateDialog(false);
onHideUpdateDialog?.();
if (versionInfo?.storeUrl) {
if (onOpenStore) {
await onOpenStore(versionInfo.storeUrl);
} else {
if (typeof window !== "undefined" && window.open) {
window.open(versionInfo.storeUrl, "_blank");
}
}
}
}, [versionInfo, onOpenStore, onHideUpdateDialog]);
const handleRemindLater = useCallback(async () => {
setShowUpdateDialog(false);
onHideUpdateDialog?.();
await versionChecker.setRemindMeLater();
}, [versionChecker, onHideUpdateDialog]);
const resetVersionCheck = useCallback(async () => {
await versionChecker.resetVersionCheckData();
setVersionInfo(null);
setShowUpdateDialog(false);
setError(null);
}, [versionChecker]);
const getChangeLog = useCallback(async () => {
return await versionChecker.getChangeLog();
}, [versionChecker]);
const isUpdateMandatory = useCallback(async () => {
return await versionChecker.isUpdateMandatory();
}, [versionChecker]);
useEffect(() => {
if (checkOnMount) {
checkForUpdates();
}
}, [checkOnMount]);
useEffect(() => {
if (!checkOnForeground) return;
}, [checkOnForeground, checkForUpdates]);
const contextValue = useMemo(
() => ({
versionInfo,
isUpdateAvailable: versionInfo?.updateAvailable || false,
currentVersion,
formattedVersion,
showUpdateDialog,
isChecking,
error,
checkForUpdates,
handleUpdateNow,
handleRemindLater,
resetVersionCheck,
getChangeLog,
isUpdateMandatory
}),
[
versionInfo,
currentVersion,
formattedVersion,
showUpdateDialog,
isChecking,
error,
checkForUpdates,
handleUpdateNow,
handleRemindLater,
resetVersionCheck,
getChangeLog,
isUpdateMandatory
]
);
return /* @__PURE__ */ React.createElement(VersionCheckContext.Provider, { value: contextValue }, children, renderDialog && DialogComponent && versionInfo && /* @__PURE__ */ React.createElement(
DialogComponent,
{
visible: showUpdateDialog,
versionInfo,
onUpdateNow: handleUpdateNow,
onRemindLater: handleRemindLater
}
));
};
var useVersionCheck = () => {
const context = useContext(VersionCheckContext);
if (context === void 0) {
throw new Error("useVersionCheck must be used within a VersionCheckProvider");
}
return context;
};
var useAppStateVersionCheck = (appStateModule, enabled = true) => {
const { checkForUpdates } = useVersionCheck();
const [appState, setAppState] = useState("active");
useEffect(() => {
if (!enabled || !appStateModule) return;
const currentState = appStateModule.currentState || "active";
setAppState(currentState);
const handleAppStateChange = (nextAppState) => {
if (appState.match(/inactive|background/) && nextAppState === "active") {
checkForUpdates();
}
setAppState(nextAppState);
};
const subscription = appStateModule.addEventListener("change", handleAppStateChange);
return () => {
subscription?.remove?.();
};
}, [appState, checkForUpdates, enabled, appStateModule]);
return appState;
};
var usePeriodicVersionCheck = (intervalMs = 60 * 60 * 1e3, enabled = true) => {
const { checkForUpdates } = useVersionCheck();
const intervalRef = useRef(null);
useEffect(() => {
if (!enabled) {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
return;
}
intervalRef.current = setInterval(() => {
checkForUpdates();
}, intervalMs);
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
};
}, [checkForUpdates, intervalMs, enabled]);
};
var useVisibilityVersionCheck = (enabled = true) => {
const { checkForUpdates } = useVersionCheck();
useEffect(() => {
if (!enabled || typeof document === "undefined") return;
const handleVisibilityChange = () => {
if (!document.hidden) {
checkForUpdates();
}
};
document.addEventListener("visibilitychange", handleVisibilityChange);
return () => {
document.removeEventListener("visibilitychange", handleVisibilityChange);
};
}, [checkForUpdates, enabled]);
};
var useStandaloneVersionChecker = (dataProvider, storageProvider, options) => {
const [versionInfo, setVersionInfo] = useState(null);
const [isChecking, setIsChecking] = useState(false);
const [error, setError] = useState(null);
const [showUpdatePrompt, setShowUpdatePrompt] = useState(false);
const versionCheckerRef = useRef(null);
useEffect(() => {
const checker = new VersionChecker(dataProvider, storageProvider);
versionCheckerRef.current = checker;
checker.initialize().catch(console.error);
return () => {
checker.dispose().catch(console.error);
};
}, [dataProvider, storageProvider]);
const checkForUpdates = useCallback(async () => {
if (!versionCheckerRef.current || isChecking) return;
setIsChecking(true);
setError(null);
try {
const result = await versionCheckerRef.current.shouldShowUpdatePrompt();
setVersionInfo(result.versionInfo);
setShowUpdatePrompt(result.shouldShowPrompt);
} catch (err) {
const error2 = err instanceof Error ? err : new Error(String(err));
setError(error2);
} finally {
setIsChecking(false);
}
}, [isChecking]);
const setRemindMeLater = useCallback(async () => {
if (!versionCheckerRef.current) return;
await versionCheckerRef.current.setRemindMeLater();
setShowUpdatePrompt(false);
}, []);
useEffect(() => {
if (options?.checkOnMount !== false) {
checkForUpdates();
}
}, []);
useEffect(() => {
if (!options?.checkOnFocus) return;
const handleFocus = () => checkForUpdates();
window.addEventListener("focus", handleFocus);
return () => window.removeEventListener("focus", handleFocus);
}, [checkForUpdates, options?.checkOnFocus]);
useEffect(() => {
if (!options?.checkInterval) return;
const interval = setInterval(checkForUpdates, options.checkInterval);
return () => clearInterval(interval);
}, [checkForUpdates, options?.checkInterval]);
return {
versionInfo,
isChecking,
error,
showUpdatePrompt,
checkForUpdates,
setRemindMeLater,
isUpdateAvailable: versionInfo?.updateAvailable || false
};
};
var useVersionInfo = () => {
const { versionInfo, currentVersion, formattedVersion } = useVersionCheck();
return {
current: currentVersion,
latest: versionInfo?.latestVersion,
formatted: formattedVersion,
updateAvailable: versionInfo?.updateAvailable || false,
platform: versionInfo?.platform,
storeUrl: versionInfo?.storeUrl
};
};
var useUpdateStatus = () => {
const { isUpdateAvailable: isUpdateAvailable2, isChecking, error, showUpdateDialog } = useVersionCheck();
return {
isUpdateAvailable: isUpdateAvailable2,
isChecking,
hasError: !!error,
error,
isDialogVisible: showUpdateDialog
};
};
export { AsyncStorageProvider, BaseStorageProvider, BaseVersionDataProvider, DEFAULT_CHECK_INTERVALS, InMemoryStorageProvider, LocalStorageProvider, VersionCheckProvider, VersionChecker, compareVersions, extractAppIdFromUrl, extractBaseVersion, extractBuildNumber, formatDisplayVersion, formatVersion, formatVersionWithBuild, getAndroidStoreUrl, getIosStoreUrl, getLatestVersion, getMajorVersion, getMinorVersion, getPatchVersion, getStoreBadgeUrl, getStoreName, getStoreUrl, getVersionDiff, isUpdateAvailable, isValidAndroidPackageName, isValidIosAppStoreId, isValidVersion, normalizeVersion, parseVersion, sortVersions, useAppStateVersionCheck, usePeriodicVersionCheck, useStandaloneVersionChecker, useUpdateStatus, useVersionCheck, useVersionInfo, useVisibilityVersionCheck };
//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map