diff options
-rw-r--r-- | app.config.js | 16 | ||||
-rw-r--r-- | src/App.native.tsx | 2 | ||||
-rw-r--r-- | src/lib/hooks/useOTAUpdates.ts | 70 | ||||
-rw-r--r-- | src/lib/hooks/useOTAUpdates.web.ts | 1 | ||||
-rw-r--r-- | src/lib/statsig/gates.ts | 1 | ||||
-rw-r--r-- | src/view/screens/Home.tsx | 3 |
6 files changed, 51 insertions, 42 deletions
diff --git a/app.config.js b/app.config.js index 9036d5e33..40feed40c 100644 --- a/app.config.js +++ b/app.config.js @@ -42,8 +42,14 @@ module.exports = function (config) { const IS_DEV = process.env.EXPO_PUBLIC_ENV === 'development' const IS_TESTFLIGHT = process.env.EXPO_PUBLIC_ENV === 'testflight' + const IS_PRODUCTION = process.env.EXPO_PUBLIC_ENV === 'production' - const UPDATES_CHANNEL = IS_TESTFLIGHT ? 'testflight' : 'production' + const UPDATES_CHANNEL = IS_TESTFLIGHT + ? 'testflight' + : IS_PRODUCTION + ? 'production' + : undefined + const UPDATES_ENABLED = !!UPDATES_CHANNEL return { expo: { @@ -126,14 +132,12 @@ module.exports = function (config) { }, updates: { url: 'https://updates.bsky.app/manifest', - // TODO Eventually we want to enable this for all environments, but for now it will only be used for - // TestFlight builds - enabled: IS_TESTFLIGHT, + enabled: UPDATES_ENABLED, fallbackToCacheTimeout: 30000, - codeSigningCertificate: IS_TESTFLIGHT + codeSigningCertificate: UPDATES_ENABLED ? './code-signing/certificate.pem' : undefined, - codeSigningMetadata: IS_TESTFLIGHT + codeSigningMetadata: UPDATES_ENABLED ? { keyid: 'main', alg: 'rsa-v1_5-sha256', diff --git a/src/App.native.tsx b/src/App.native.tsx index 9abe4a559..ede587c89 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -19,7 +19,6 @@ import {init as initPersistedState} from '#/state/persisted' import * as persisted from '#/state/persisted' import {Provider as LabelDefsProvider} from '#/state/preferences/label-defs' import {useIntentHandler} from 'lib/hooks/useIntentHandler' -import {useOTAUpdates} from 'lib/hooks/useOTAUpdates' import {useNotificationsListener} from 'lib/notifications/notifications' import {QueryProvider} from 'lib/react-query' import {s} from 'lib/styles' @@ -58,7 +57,6 @@ function InnerApp() { const {_} = useLingui() useIntentHandler() - useOTAUpdates() // init useEffect(() => { diff --git a/src/lib/hooks/useOTAUpdates.ts b/src/lib/hooks/useOTAUpdates.ts index 51fd18aa0..70905c137 100644 --- a/src/lib/hooks/useOTAUpdates.ts +++ b/src/lib/hooks/useOTAUpdates.ts @@ -12,6 +12,7 @@ import { import {logger} from '#/logger' import {IS_TESTFLIGHT} from 'lib/app-info' +import {useGate} from 'lib/statsig/statsig' import {isIOS} from 'platform/detection' const MINIMUM_MINIMIZE_TIME = 15 * 60e3 @@ -30,6 +31,9 @@ async function setExtraParams() { } export function useOTAUpdates() { + const shouldReceiveUpdates = + useGate('receive_updates') && isEnabled && !__DEV__ + const appState = React.useRef<AppStateStatus>('active') const lastMinimize = React.useRef(0) const ranInitialCheck = React.useRef(false) @@ -51,61 +55,59 @@ export function useOTAUpdates() { logger.debug('No update available.') } } catch (e) { - logger.warn('OTA Update Error', {error: `${e}`}) + logger.error('OTA Update Error', {error: `${e}`}) } }, 10e3) }, []) - const onIsTestFlight = React.useCallback(() => { - setTimeout(async () => { - try { - await setExtraParams() - - const res = await checkForUpdateAsync() - if (res.isAvailable) { - await fetchUpdateAsync() - - Alert.alert( - 'Update Available', - 'A new version of the app is available. Relaunch now?', - [ - { - text: 'No', - style: 'cancel', - }, - { - text: 'Relaunch', - style: 'default', - onPress: async () => { - await reloadAsync() - }, + const onIsTestFlight = React.useCallback(async () => { + try { + await setExtraParams() + + const res = await checkForUpdateAsync() + if (res.isAvailable) { + await fetchUpdateAsync() + + Alert.alert( + 'Update Available', + 'A new version of the app is available. Relaunch now?', + [ + { + text: 'No', + style: 'cancel', + }, + { + text: 'Relaunch', + style: 'default', + onPress: async () => { + await reloadAsync() }, - ], - ) - } - } catch (e: any) { - // No need to handle + }, + ], + ) } - }, 3e3) + } catch (e: any) { + logger.error('Internal OTA Update Error', {error: `${e}`}) + } }, []) React.useEffect(() => { + // We use this setTimeout to allow Statsig to initialize before we check for an update // For Testflight users, we can prompt the user to update immediately whenever there's an available update. This // is suspect however with the Apple App Store guidelines, so we don't want to prompt production users to update // immediately. if (IS_TESTFLIGHT) { onIsTestFlight() return - } else if (!isEnabled || __DEV__ || ranInitialCheck.current) { - // Development client shouldn't check for updates at all, so we skip that here. + } else if (!shouldReceiveUpdates || ranInitialCheck.current) { return } setCheckTimeout() ranInitialCheck.current = true - }, [onIsTestFlight, setCheckTimeout]) + }, [onIsTestFlight, setCheckTimeout, shouldReceiveUpdates]) - // After the app has been minimized for 30 minutes, we want to either A. install an update if one has become available + // After the app has been minimized for 15 minutes, we want to either A. install an update if one has become available // or B check for an update again. React.useEffect(() => { if (!isEnabled) return diff --git a/src/lib/hooks/useOTAUpdates.web.ts b/src/lib/hooks/useOTAUpdates.web.ts new file mode 100644 index 000000000..1baf4894e --- /dev/null +++ b/src/lib/hooks/useOTAUpdates.web.ts @@ -0,0 +1 @@ +export function useOTAUpdates() {} diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index acf0b2aff..81f3f19d5 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -5,6 +5,7 @@ export type Gate = | 'disable_poll_on_discover' | 'new_profile_scroll_component' | 'new_search' + | 'receive_updates' | 'show_follow_back_label' | 'start_session_with_following' | 'use_new_suggestions_endpoint' diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 7a2a88265..b55053af0 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -14,6 +14,7 @@ import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types' import {useSession} from '#/state/session' import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed' +import {useOTAUpdates} from 'lib/hooks/useOTAUpdates' import {HomeTabNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' import {FeedPage} from 'view/com/feeds/FeedPage' import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager' @@ -51,6 +52,8 @@ function HomeScreenReady({ preferences: UsePreferencesQueryResponse pinnedFeedInfos: FeedSourceInfo[] }) { + useOTAUpdates() + const allFeeds = React.useMemo(() => { const feeds: FeedDescriptor[] = [] feeds.push('home') |