BluestoneApps MCP Remote Server
by lallen30
import { useState, useCallback, useEffect } from 'react';
import UpdateService from '../services/UpdateService';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { compareVersions } from 'compare-versions';
interface VersionInfo {
latestVersion: string;
minimumVersion: string;
releaseNotes?: string;
}
/**
* Custom hook for managing app update state and logic
* @param checkOnMount Whether to check for updates when the component mounts
* @param updateCheckInterval Time interval between update checks (in milliseconds)
* @returns Object containing update state and functions
*/
const useAppUpdate = (checkOnMount = true, updateCheckInterval = 60 * 60 * 1000) => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [versionInfo, setVersionInfo] = useState<VersionInfo | null>(null);
const [isAppStoreVersion, setIsAppStoreVersion] = useState(false);
const [storedAppVersion, setStoredAppVersion] = useState<string | null>(null);
const [updateModalVisible, setUpdateModalVisible] = useState(false);
const [lastUpdateCheck, setLastUpdateCheck] = useState<number | null>(null);
// Time interval between update checks (in milliseconds)
// Default: 1 hour - adjust this value based on how frequently you want to check for updates
// Lower values will check more frequently but may impact performance
const UPDATE_CHECK_INTERVAL = updateCheckInterval;
// Check if the app is from the App Store
const checkAppSource = useCallback(async () => {
try {
const isFromAppStore = await UpdateService.isAppStoreVersion();
setIsAppStoreVersion(isFromAppStore);
return isFromAppStore;
} catch (err) {
console.error('Error checking app source');
return false;
}
}, []);
// Get the stored app version
const getStoredAppVersion = useCallback(async () => {
try {
const version = await AsyncStorage.getItem('app_version');
setStoredAppVersion(version);
return version;
} catch (err) {
console.error('Error getting stored app version');
return null;
}
}, []);
// Update the stored app version
const updateStoredAppVersion = useCallback(async (version: string) => {
try {
await AsyncStorage.setItem('app_version', version);
setStoredAppVersion(version);
} catch (err) {
console.error('Error updating stored app version');
}
}, []);
/**
* Load the last update check time from storage
*/
const loadLastUpdateCheckTime = useCallback(async () => {
try {
const storedTime = await AsyncStorage.getItem('lastUpdateCheckTime');
if (storedTime) {
setLastUpdateCheck(parseInt(storedTime, 10));
}
} catch (error) {
console.error('Error loading last update check time');
}
}, []);
/**
* Save the current time as the last update check time
*/
const saveLastUpdateCheckTime = async () => {
try {
const currentTime = Date.now();
await AsyncStorage.setItem('lastUpdateCheckTime', currentTime.toString());
setLastUpdateCheck(currentTime);
} catch (error) {
console.error('Error saving last update check time');
}
};
/**
* Check if enough time has passed since the last update check
*/
const shouldCheckForUpdates = () => {
if (!lastUpdateCheck) {
return true;
}
const currentTime = Date.now();
const timeSinceLastCheck = currentTime - lastUpdateCheck;
const shouldCheck = timeSinceLastCheck > UPDATE_CHECK_INTERVAL;
return shouldCheck;
};
/**
* Check if app version needs update
* @param silent If true, performs the check silently in the background without showing loading indicators
* @param force If true, checks for updates regardless of when the last check occurred
*
* Usage examples:
* - checkAppVersion(true, true): Silent check that bypasses time interval (ideal for HomeScreen focus)
* - checkAppVersion(false, false): Regular check with loading indicator that respects time interval
* - checkAppVersion(true, false): Silent check that respects time interval (good for background checks)
* @returns Promise that resolves when the check is complete
*/
const checkAppVersion = useCallback(async (silent = false, force = false): Promise<void> => {
try {
// Skip check if it was done recently, unless force is true
if (!force && !shouldCheckForUpdates()) {
return;
}
if (!silent) {
setLoading(true);
}
// Get the current app version
const currentVersion = await UpdateService.getCurrentVersion();
if (!currentVersion) {
console.error('Failed to get current app version');
return;
}
// Get the latest version info from the server
const versionInfo = await UpdateService.checkForUpdates();
if (!versionInfo) {
console.error('Failed to get latest version info');
return;
}
// Update the last check time
await saveLastUpdateCheckTime();
// Set version info state
setVersionInfo(versionInfo);
// Check if update is needed
const shouldShowModal = UpdateService.shouldShowUpdateModal(
currentVersion,
versionInfo.latestVersion,
versionInfo.minimumVersion
);
// Only show modal if update is needed
if (shouldShowModal) {
setUpdateModalVisible(true);
} else {
setUpdateModalVisible(false);
}
} catch (error) {
console.error('Error checking app version');
// Don't show modal on error
setUpdateModalVisible(false);
} finally {
if (!silent) {
setLoading(false);
}
}
}, [shouldCheckForUpdates, saveLastUpdateCheckTime]);
/**
* Close the update modal
*/
const closeUpdateModal = useCallback(async () => {
try {
// If there's version info and it's not a required update, mark it as skipped
if (versionInfo) {
const currentVersion = await UpdateService.getCurrentVersion();
const isRequired = UpdateService.isUpdateRequired(currentVersion, versionInfo.minimumVersion);
if (!isRequired) {
await UpdateService.skipVersion(versionInfo.latestVersion);
}
}
setUpdateModalVisible(false);
} catch (error) {
console.error('Error closing update modal');
}
}, [versionInfo]);
/**
* Navigate to the app store to update the app
*/
const goToUpdate = useCallback(async () => {
try {
await UpdateService.openAppStore();
} catch (error) {
console.error('Error navigating to app store');
setError(`Failed to open app store: ${error instanceof Error ? error.message : String(error)}`);
}
}, []);
// Check for updates on mount if checkOnMount is true
useEffect(() => {
// Always load the last update check time
loadLastUpdateCheckTime();
// Only check for updates if checkOnMount is true
if (checkOnMount) {
// Use silent mode for initial check to avoid disrupting the user experience
checkAppVersion(true, false);
}
}, [checkOnMount, loadLastUpdateCheckTime]);
// Check if the app is from the App Store on mount
useEffect(() => {
checkAppSource();
}, [checkAppSource]);
// Get the stored app version on mount
useEffect(() => {
getStoredAppVersion();
}, [getStoredAppVersion]);
return {
versionInfo,
isAppStoreVersion,
storedAppVersion,
loading,
error,
updateModalVisible,
setUpdateModalVisible,
checkAppVersion,
checkForUpdates: useCallback((silent = false, force = false): Promise<void> => {
// Directly call checkAppVersion with the provided parameters
return checkAppVersion(silent, force);
}, [checkAppVersion]),
closeUpdateModal,
goToUpdate,
};
};
export default useAppUpdate;