about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/App.native.tsx2
-rw-r--r--src/components/ReportDialog/SelectLabelerView.tsx8
-rw-r--r--src/components/ReportDialog/SelectReportOptionView.tsx13
-rw-r--r--src/components/moderation/LabelPreference.tsx13
-rw-r--r--src/lib/app-info.ts10
-rw-r--r--src/lib/hooks/useOTAUpdates.ts142
-rw-r--r--src/screens/Moderation/index.tsx60
-rw-r--r--src/screens/Profile/Sections/Labels.tsx27
-rw-r--r--src/view/screens/Settings/index.tsx86
9 files changed, 250 insertions, 111 deletions
diff --git a/src/App.native.tsx b/src/App.native.tsx
index d6e726a59..2c880f217 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -19,6 +19,7 @@ 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 * as notifications from 'lib/notifications/notifications'
 import {
   asyncStoragePersister,
@@ -60,6 +61,7 @@ function InnerApp() {
   const theme = useColorModeTheme()
   const {_} = useLingui()
   useIntentHandler()
+  useOTAUpdates()
 
   // init
   useEffect(() => {
diff --git a/src/components/ReportDialog/SelectLabelerView.tsx b/src/components/ReportDialog/SelectLabelerView.tsx
index 383d1b95f..dd07cafa3 100644
--- a/src/components/ReportDialog/SelectLabelerView.tsx
+++ b/src/components/ReportDialog/SelectLabelerView.tsx
@@ -1,18 +1,16 @@
 import React from 'react'
 import {View} from 'react-native'
+import {AppBskyLabelerDefs} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import {AppBskyLabelerDefs} from '@atproto/api'
 
 export {useDialogControl as useReportDialogControl} from '#/components/Dialog'
 import {getLabelingServiceTitle} from '#/lib/moderation'
-
-import {atoms as a, useTheme, useBreakpoints} from '#/alf'
-import {Text} from '#/components/Typography'
+import {atoms as a, useBreakpoints, useTheme} from '#/alf'
 import {Button, useButtonContext} from '#/components/Button'
 import {Divider} from '#/components/Divider'
 import * as LabelingServiceCard from '#/components/LabelingServiceCard'
-
+import {Text} from '#/components/Typography'
 import {ReportDialogProps} from './types'
 
 export function SelectLabelerView({
diff --git a/src/components/ReportDialog/SelectReportOptionView.tsx b/src/components/ReportDialog/SelectReportOptionView.tsx
index 54844cfda..c67698348 100644
--- a/src/components/ReportDialog/SelectReportOptionView.tsx
+++ b/src/components/ReportDialog/SelectReportOptionView.tsx
@@ -1,16 +1,15 @@
 import React from 'react'
 import {View} from 'react-native'
+import {AppBskyLabelerDefs} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import {AppBskyLabelerDefs} from '@atproto/api'
 
-import {useReportOptions, ReportOption} from '#/lib/moderation/useReportOptions'
-import {DMCA_LINK} from '#/components/ReportDialog/const'
+import {ReportOption, useReportOptions} from '#/lib/moderation/useReportOptions'
 import {Link} from '#/components/Link'
+import {DMCA_LINK} from '#/components/ReportDialog/const'
 export {useDialogControl as useReportDialogControl} from '#/components/Dialog'
 
-import {atoms as a, useTheme, useBreakpoints} from '#/alf'
-import {Text} from '#/components/Typography'
+import {atoms as a, useBreakpoints, useTheme} from '#/alf'
 import {
   Button,
   ButtonIcon,
@@ -19,11 +18,11 @@ import {
 } from '#/components/Button'
 import {Divider} from '#/components/Divider'
 import {
-  ChevronRight_Stroke2_Corner0_Rounded as ChevronRight,
   ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft,
+  ChevronRight_Stroke2_Corner0_Rounded as ChevronRight,
 } from '#/components/icons/Chevron'
 import {SquareArrowTopRight_Stroke2_Corner0_Rounded as SquareArrowTopRight} from '#/components/icons/SquareArrowTopRight'
-
+import {Text} from '#/components/Typography'
 import {ReportDialogProps} from './types'
 
 export function SelectReportOptionView({
diff --git a/src/components/moderation/LabelPreference.tsx b/src/components/moderation/LabelPreference.tsx
index 7d4bd9c32..028bd1a39 100644
--- a/src/components/moderation/LabelPreference.tsx
+++ b/src/components/moderation/LabelPreference.tsx
@@ -1,22 +1,21 @@
 import React from 'react'
 import {View} from 'react-native'
 import {InterpretedLabelValueDefinition, LabelPreference} from '@atproto/api'
-import {useLingui} from '@lingui/react'
 import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
 
 import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings'
+import {useLabelBehaviorDescription} from '#/lib/moderation/useLabelBehaviorDescription'
+import {getLabelStrings} from '#/lib/moderation/useLabelInfo'
 import {
   usePreferencesQuery,
   usePreferencesSetContentLabelMutation,
 } from '#/state/queries/preferences'
-import {useLabelBehaviorDescription} from '#/lib/moderation/useLabelBehaviorDescription'
-import {getLabelStrings} from '#/lib/moderation/useLabelInfo'
-
-import {useTheme, atoms as a, useBreakpoints} from '#/alf'
-import {Text} from '#/components/Typography'
+import {atoms as a, useBreakpoints, useTheme} from '#/alf'
+import * as ToggleButton from '#/components/forms/ToggleButton'
 import {InlineLink} from '#/components/Link'
+import {Text} from '#/components/Typography'
 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '../icons/CircleInfo'
-import * as ToggleButton from '#/components/forms/ToggleButton'
 
 export function Outer({children}: React.PropsWithChildren<{}>) {
   return (
diff --git a/src/lib/app-info.ts b/src/lib/app-info.ts
index 3f026d3fe..3071e031b 100644
--- a/src/lib/app-info.ts
+++ b/src/lib/app-info.ts
@@ -1,5 +1,9 @@
 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})`
+export const IS_DEV = process.env.EXPO_PUBLIC_ENV === 'development'
+export const IS_TESTFLIGHT = process.env.EXPO_PUBLIC_ENV === 'testflight'
+
+const UPDATES_CHANNEL = IS_TESTFLIGHT ? 'testflight' : 'production'
+export const appVersion = `${VersionNumber.appVersion} (${
+  VersionNumber.buildVersion
+}, ${IS_DEV ? 'development' : UPDATES_CHANNEL})`
diff --git a/src/lib/hooks/useOTAUpdates.ts b/src/lib/hooks/useOTAUpdates.ts
new file mode 100644
index 000000000..181f0b2c6
--- /dev/null
+++ b/src/lib/hooks/useOTAUpdates.ts
@@ -0,0 +1,142 @@
+import React from 'react'
+import {Alert, AppState, AppStateStatus} from 'react-native'
+import app from 'react-native-version-number'
+import {
+  checkForUpdateAsync,
+  fetchUpdateAsync,
+  isEnabled,
+  reloadAsync,
+  setExtraParamAsync,
+  useUpdates,
+} from 'expo-updates'
+
+import {logger} from '#/logger'
+import {IS_TESTFLIGHT} from 'lib/app-info'
+import {isIOS} from 'platform/detection'
+
+const MINIMUM_MINIMIZE_TIME = 15 * 60e3
+
+async function setExtraParams() {
+  await setExtraParamAsync(
+    isIOS ? 'ios-build-number' : 'android-build-number',
+    // Hilariously, `buildVersion` is not actually a string on Android even though the TS type says it is.
+    // This just ensures it gets passed as a string
+    `${app.buildVersion}`,
+  )
+  await setExtraParamAsync(
+    'channel',
+    IS_TESTFLIGHT ? 'testflight' : 'production',
+  )
+}
+
+export function useOTAUpdates() {
+  const appState = React.useRef<AppStateStatus>('active')
+  const lastMinimize = React.useRef(0)
+  const ranInitialCheck = React.useRef(false)
+  const timeout = React.useRef<NodeJS.Timeout>()
+  const {isUpdatePending} = useUpdates()
+
+  const setCheckTimeout = React.useCallback(() => {
+    timeout.current = setTimeout(async () => {
+      try {
+        await setExtraParams()
+
+        logger.debug('Checking for update...')
+        const res = await checkForUpdateAsync()
+
+        if (res.isAvailable) {
+          logger.debug('Attempting to fetch update...')
+          await fetchUpdateAsync()
+        } else {
+          logger.debug('No update available.')
+        }
+      } catch (e) {
+        logger.warn('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()
+                },
+              },
+            ],
+          )
+        }
+      } catch (e: any) {
+        // No need to handle
+      }
+    }, 3e3)
+  }, [])
+
+  React.useEffect(() => {
+    // 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.
+      return
+    }
+
+    setCheckTimeout()
+    ranInitialCheck.current = true
+  }, [onIsTestFlight, setCheckTimeout])
+
+  // After the app has been minimized for 30 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
+
+    const subscription = AppState.addEventListener(
+      'change',
+      async nextAppState => {
+        if (
+          appState.current.match(/inactive|background/) &&
+          nextAppState === 'active'
+        ) {
+          // If it's been 15 minutes since the last "minimize", we should feel comfortable updating the client since
+          // chances are that there isn't anything important going on in the current session.
+          if (lastMinimize.current <= Date.now() - MINIMUM_MINIMIZE_TIME) {
+            if (isUpdatePending) {
+              await reloadAsync()
+            } else {
+              setCheckTimeout()
+            }
+          }
+        } else {
+          lastMinimize.current = Date.now()
+        }
+
+        appState.current = nextAppState
+      },
+    )
+
+    return () => {
+      clearTimeout(timeout.current)
+      subscription.remove()
+    }
+  }, [isUpdatePending, setCheckTimeout])
+}
diff --git a/src/screens/Moderation/index.tsx b/src/screens/Moderation/index.tsx
index 7d991cc71..9d51a6197 100644
--- a/src/screens/Moderation/index.tsx
+++ b/src/screens/Moderation/index.tsx
@@ -1,51 +1,49 @@
 import React from 'react'
 import {View} from 'react-native'
-import {useFocusEffect} from '@react-navigation/native'
+import {useSafeAreaFrame} from 'react-native-safe-area-context'
 import {ComAtprotoLabelDefs} from '@atproto/api'
-import {Trans, msg} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
 import {LABELS} from '@atproto/api'
-import {useSafeAreaFrame} from 'react-native-safe-area-context'
-
-import {NativeStackScreenProps, CommonNavigatorParams} from '#/lib/routes/types'
-import {CenteredView} from '#/view/com/util/Views'
-import {ViewHeader} from '#/view/com/util/ViewHeader'
-import {useAnalytics} from 'lib/analytics/analytics'
-import {useSetMinimalShellMode} from '#/state/shell'
-import {useSession} from '#/state/session'
-import {
-  useProfileQuery,
-  useProfileUpdateMutation,
-} from '#/state/queries/profile'
-import {ScrollView} from '#/view/com/util/Views'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {useFocusEffect} from '@react-navigation/native'
 
+import {getLabelingServiceTitle} from '#/lib/moderation'
+import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
+import {logger} from '#/logger'
 import {
-  UsePreferencesQueryResponse,
   useMyLabelersQuery,
   usePreferencesQuery,
+  UsePreferencesQueryResponse,
   usePreferencesSetAdultContentMutation,
 } from '#/state/queries/preferences'
-
-import {getLabelingServiceTitle} from '#/lib/moderation'
-import {logger} from '#/logger'
-import {useTheme, atoms as a, useBreakpoints, ViewStyleProp} from '#/alf'
+import {
+  useProfileQuery,
+  useProfileUpdateMutation,
+} from '#/state/queries/profile'
+import {useSession} from '#/state/session'
+import {useSetMinimalShellMode} from '#/state/shell'
+import {useAnalytics} from 'lib/analytics/analytics'
+import {ViewHeader} from '#/view/com/util/ViewHeader'
+import {CenteredView} from '#/view/com/util/Views'
+import {ScrollView} from '#/view/com/util/Views'
+import {atoms as a, useBreakpoints, useTheme, ViewStyleProp} from '#/alf'
+import {Button, ButtonText} from '#/components/Button'
+import * as Dialog from '#/components/Dialog'
+import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings'
+import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
 import {Divider} from '#/components/Divider'
+import * as Toggle from '#/components/forms/Toggle'
+import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
 import {CircleBanSign_Stroke2_Corner0_Rounded as CircleBanSign} from '#/components/icons/CircleBanSign'
+import {Props as SVGIconProps} from '#/components/icons/common'
+import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filter'
 import {Group3_Stroke2_Corner0_Rounded as Group} from '#/components/icons/Group'
 import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Person'
-import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
-import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filter'
-import {Text} from '#/components/Typography'
-import * as Toggle from '#/components/forms/Toggle'
+import * as LabelingService from '#/components/LabelingServiceCard'
 import {InlineLink, Link} from '#/components/Link'
-import {Button, ButtonText} from '#/components/Button'
 import {Loader} from '#/components/Loader'
-import * as LabelingService from '#/components/LabelingServiceCard'
 import {GlobalLabelPreference} from '#/components/moderation/LabelPreference'
-import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
-import {Props as SVGIconProps} from '#/components/icons/common'
-import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings'
-import * as Dialog from '#/components/Dialog'
+import {Text} from '#/components/Typography'
 
 function ErrorState({error}: {error: string}) {
   const t = useTheme()
diff --git a/src/screens/Profile/Sections/Labels.tsx b/src/screens/Profile/Sections/Labels.tsx
index 2b2b99594..5ba8f00a5 100644
--- a/src/screens/Profile/Sections/Labels.tsx
+++ b/src/screens/Profile/Sections/Labels.tsx
@@ -1,30 +1,29 @@
 import React from 'react'
 import {View} from 'react-native'
+import {useSafeAreaFrame} from 'react-native-safe-area-context'
 import {
   AppBskyLabelerDefs,
-  ModerationOpts,
-  interpretLabelValueDefinitions,
   InterpretedLabelValueDefinition,
+  interpretLabelValueDefinitions,
+  ModerationOpts,
 } from '@atproto/api'
-import {Trans, msg} from '@lingui/macro'
+import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import {useSafeAreaFrame} from 'react-native-safe-area-context'
 
-import {useScrollHandlers} from '#/lib/ScrollContext'
 import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
 import {isLabelerSubscribed, lookupLabelValueDefinition} from '#/lib/moderation'
-import {ListRef} from '#/view/com/util/List'
-import {SectionRef} from './types'
+import {useScrollHandlers} from '#/lib/ScrollContext'
 import {isNative} from '#/platform/detection'
-
-import {useTheme, atoms as a} from '#/alf'
-import {Text} from '#/components/Typography'
-import {Loader} from '#/components/Loader'
-import {Divider} from '#/components/Divider'
+import {ListRef} from '#/view/com/util/List'
 import {CenteredView, ScrollView} from '#/view/com/util/Views'
-import {ErrorState} from '../ErrorState'
-import {LabelerLabelPreference} from '#/components/moderation/LabelPreference'
+import {atoms as a, useTheme} from '#/alf'
+import {Divider} from '#/components/Divider'
 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
+import {Loader} from '#/components/Loader'
+import {LabelerLabelPreference} from '#/components/moderation/LabelPreference'
+import {Text} from '#/components/Typography'
+import {ErrorState} from '../ErrorState'
+import {SectionRef} from './types'
 
 interface LabelsSectionProps {
   isLabelerLoading: boolean
diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx
index 3967678b4..790ce5ee9 100644
--- a/src/view/screens/Settings/index.tsx
+++ b/src/view/screens/Settings/index.tsx
@@ -3,72 +3,72 @@ import {
   ActivityIndicator,
   Linking,
   Platform,
-  StyleSheet,
   Pressable,
+  StyleSheet,
   TextStyle,
   TouchableOpacity,
   View,
   ViewStyle,
 } from 'react-native'
-import {useFocusEffect, useNavigation} from '@react-navigation/native'
 import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
 } from '@fortawesome/react-native-fontawesome'
-import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
-import * as AppInfo from 'lib/app-info'
-import {usePalette} from 'lib/hooks/usePalette'
-import {useCustomPalette} from 'lib/hooks/useCustomPalette'
-import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
-import {useAccountSwitcher} from 'lib/hooks/useAccountSwitcher'
-import {useAnalytics} from 'lib/analytics/analytics'
-import {NavigationProp} from 'lib/routes/types'
-import {HandIcon, HashtagIcon} from 'lib/icons'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
 import Clipboard from '@react-native-clipboard/clipboard'
-import {makeProfileLink} from 'lib/routes/links'
-import {RQKEY as RQKEY_PROFILE} from '#/state/queries/profile'
+import {useFocusEffect, useNavigation} from '@react-navigation/native'
+import {useQueryClient} from '@tanstack/react-query'
+
+import {isNative} from '#/platform/detection'
 import {useModalControls} from '#/state/modals'
-import {
-  useSetMinimalShellMode,
-  useThemePrefs,
-  useSetThemePrefs,
-  useOnboardingDispatch,
-} from '#/state/shell'
+import {clearLegacyStorage} from '#/state/persisted/legacy'
+// TODO import {useInviteCodesQuery} from '#/state/queries/invites'
+import {clear as clearStorage} from '#/state/persisted/store'
 import {
   useRequireAltTextEnabled,
   useSetRequireAltTextEnabled,
 } from '#/state/preferences'
-import {useSession, useSessionApi, SessionAccount} from '#/state/session'
-import {useProfileQuery} from '#/state/queries/profile'
-import {useClearPreferencesMutation} from '#/state/queries/preferences'
-// TODO import {useInviteCodesQuery} from '#/state/queries/invites'
-import {clear as clearStorage} from '#/state/persisted/store'
-import {clearLegacyStorage} from '#/state/persisted/legacy'
-import {STATUS_PAGE_URL} from 'lib/constants'
-import {Trans, msg} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {useQueryClient} from '@tanstack/react-query'
-import {useLoggedOutViewControls} from '#/state/shell/logged-out'
-import {useCloseAllActiveElements} from '#/state/util'
 import {
   useInAppBrowser,
   useSetInAppBrowser,
 } from '#/state/preferences/in-app-browser'
-import {isNative} from '#/platform/detection'
-import {useDialogControl} from '#/components/Dialog'
-
-import {s, colors} from 'lib/styles'
-import {ScrollView} from 'view/com/util/Views'
+import {useClearPreferencesMutation} from '#/state/queries/preferences'
+import {RQKEY as RQKEY_PROFILE} from '#/state/queries/profile'
+import {useProfileQuery} from '#/state/queries/profile'
+import {SessionAccount, useSession, useSessionApi} from '#/state/session'
+import {
+  useOnboardingDispatch,
+  useSetMinimalShellMode,
+  useSetThemePrefs,
+  useThemePrefs,
+} from '#/state/shell'
+import {useLoggedOutViewControls} from '#/state/shell/logged-out'
+import {useCloseAllActiveElements} from '#/state/util'
+import {useAnalytics} from 'lib/analytics/analytics'
+import * as AppInfo from 'lib/app-info'
+import {STATUS_PAGE_URL} from 'lib/constants'
+import {useAccountSwitcher} from 'lib/hooks/useAccountSwitcher'
+import {useCustomPalette} from 'lib/hooks/useCustomPalette'
+import {usePalette} from 'lib/hooks/usePalette'
+import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
+import {HandIcon, HashtagIcon} from 'lib/icons'
+import {makeProfileLink} from 'lib/routes/links'
+import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
+import {NavigationProp} from 'lib/routes/types'
+import {colors, s} from 'lib/styles'
+import {AccountDropdownBtn} from 'view/com/util/AccountDropdownBtn'
+import {SelectableBtn} from 'view/com/util/forms/SelectableBtn'
+import {ToggleButton} from 'view/com/util/forms/ToggleButton'
 import {Link, TextLink} from 'view/com/util/Link'
+import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader'
 import {Text} from 'view/com/util/text/Text'
 import * as Toast from 'view/com/util/Toast'
 import {UserAvatar} from 'view/com/util/UserAvatar'
-import {ToggleButton} from 'view/com/util/forms/ToggleButton'
-import {SelectableBtn} from 'view/com/util/forms/SelectableBtn'
-import {AccountDropdownBtn} from 'view/com/util/AccountDropdownBtn'
-import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader'
-import {ExportCarDialog} from './ExportCarDialog'
+import {ScrollView} from 'view/com/util/Views'
+import {useDialogControl} from '#/components/Dialog'
 import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings'
+import {ExportCarDialog} from './ExportCarDialog'
 
 function SettingsAccountCard({account}: {account: SessionAccount}) {
   const pal = usePalette('default')
@@ -890,9 +890,7 @@ export function SettingsScreen({}: Props) {
             accessibilityRole="button"
             onPress={onPressBuildInfo}>
             <Text type="sm" style={[styles.buildInfo, pal.textLight]}>
-              <Trans>
-                Build version {AppInfo.appVersion} {AppInfo.updateChannel}
-              </Trans>
+              <Trans>Version {AppInfo.appVersion}</Trans>
             </Text>
           </TouchableOpacity>
           <Text type="sm" style={[pal.textLight]}>