diff options
author | Ansh <anshnanda10@gmail.com> | 2023-06-02 13:27:59 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-02 13:27:59 -0700 |
commit | ba4bb46c3fc3d670e565c69bdf71dbb4510b51f0 (patch) | |
tree | cb48529f6fcf6553d0b8697aeb66d405d4124166 /src | |
parent | ad4eaf5ed2e35233ecc7b29ddcafc52c2001dcd1 (diff) | |
download | voidsky-ba4bb46c3fc3d670e565c69bdf71dbb4510b51f0.tar.zst |
[APP-107] OTA updates (#587)
* add 1000ms fallbackToCacheTimeout * add listener via useOTAUpdate hook and show modal if update is available * finish expo-updates setup * setup useOTAUpdate hook * add 1000ms fallbackToCacheTimeout * add listener via useOTAUpdate hook and show modal if update is available * finish expo-updates setup * setup useOTAUpdate hook * add OTA updates * Update build.md * temporarily disable ota updates * refactor useOTAUpdate code
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/app-info.ts | 3 | ||||
-rw-r--r-- | src/lib/hooks/useOTAUpdate.ts | 74 | ||||
-rw-r--r-- | src/view/screens/Settings.tsx | 2 | ||||
-rw-r--r-- | src/view/shell/index.tsx | 2 |
4 files changed, 80 insertions, 1 deletions
diff --git a/src/lib/app-info.ts b/src/lib/app-info.ts index a365e7e9f..3f026d3fe 100644 --- a/src/lib/app-info.ts +++ b/src/lib/app-info.ts @@ -1,2 +1,5 @@ import VersionNumber from 'react-native-version-number' +import * as Updates from 'expo-updates' +export const updateChannel = Updates.channel + export const appVersion = `${VersionNumber.appVersion} (${VersionNumber.buildVersion})` diff --git a/src/lib/hooks/useOTAUpdate.ts b/src/lib/hooks/useOTAUpdate.ts new file mode 100644 index 000000000..ae6035223 --- /dev/null +++ b/src/lib/hooks/useOTAUpdate.ts @@ -0,0 +1,74 @@ +import * as Updates from 'expo-updates' +import {useCallback, useEffect} from 'react' +import {AppState} from 'react-native' +import {useStores} from 'state/index' + +export function useOTAUpdate() { + const store = useStores() + + // HELPER FUNCTIONS + const showUpdatePopup = useCallback(() => { + store.shell.openModal({ + name: 'confirm', + title: 'Update Available', + message: + 'A new version of the app is available. Please update to continue using the app.', + onPressConfirm: async () => { + Updates.reloadAsync().catch(err => { + throw err + }) + }, + }) + }, [store.shell]) + const checkForUpdate = useCallback(async () => { + store.log.debug('useOTAUpdate: Checking for update...') + try { + // Check if new OTA update is available + const update = await Updates.checkForUpdateAsync() + // If updates aren't available stop the function execution + if (!update.isAvailable) { + return + } + // Otherwise fetch the update in the background, so even if the user rejects switching to latest version it will be done automatically on next relaunch. + await Updates.fetchUpdateAsync() + // show a popup modal + showUpdatePopup() + } catch (e) { + console.error('useOTAUpdate: Error while checking for update', e) + store.log.error('useOTAUpdate: Error while checking for update', e) + } + }, [showUpdatePopup, store.log]) + const updateEventListener = useCallback( + (event: Updates.UpdateEvent) => { + store.log.debug('useOTAUpdate: Listening for update...') + if (event.type === Updates.UpdateEventType.ERROR) { + throw new Error(event.message) + } else if (event.type === Updates.UpdateEventType.NO_UPDATE_AVAILABLE) { + // Handle no update available + // do nothing + } else if (event.type === Updates.UpdateEventType.UPDATE_AVAILABLE) { + // Handle update available + // open modal, ask for user confirmation, and reload the app + showUpdatePopup() + } + }, + [showUpdatePopup, store.log], + ) + + useEffect(() => { + // ADD EVENT LISTENERS + const updateEventSubscription = Updates.addListener(updateEventListener) + const appStateSubscription = AppState.addEventListener('change', state => { + if (state === 'active' && !__DEV__) { + checkForUpdate() + } + }) + + // REMOVE EVENT LISTENERS (CLEANUP) + return () => { + updateEventSubscription.remove() + appStateSubscription.remove() + } + }, []) // eslint-disable-line react-hooks/exhaustive-deps + // disable exhaustive deps because we don't want to run this effect again +} diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx index 798893855..52be35a48 100644 --- a/src/view/screens/Settings.tsx +++ b/src/view/screens/Settings.tsx @@ -445,7 +445,7 @@ export const SettingsScreen = withAuthRequired( </Link> ) : null} <Text type="sm" style={[styles.buildInfo, pal.textLight]}> - Build version {AppInfo.appVersion} + Build version {AppInfo.appVersion} {AppInfo.updateChannel} </Text> <View style={s.footerSpacer} /> </ScrollView> diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx index a6066b25f..36f7442dc 100644 --- a/src/view/shell/index.tsx +++ b/src/view/shell/index.tsx @@ -18,9 +18,11 @@ import {RoutesContainer, TabsNavigator} from '../../Navigation' import {isStateAtTabRoot} from 'lib/routes/helpers' import {isAndroid} from 'platform/detection' import {SafeAreaProvider} from 'react-native-safe-area-context' +import {useOTAUpdate} from 'lib/hooks/useOTAUpdate' const ShellInner = observer(() => { const store = useStores() + useOTAUpdate() // this hook polls for OTA updates every few seconds const winDim = useWindowDimensions() const safeAreaInsets = useSafeAreaInsets() const containerPadding = React.useMemo( |