From 0589bd7d9ef0a3a8dca5f8f8e7d3d7be31f49553 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 16 Jan 2026 16:28:17 -0600 Subject: [PATCH] Move `/platform/detection` vars into `/env` (#9707) * Add platform vars to env * Replace /platform/detection with /env --- CLAUDE.md | 4 +- .../src/BottomSheetNativeComponent.tsx | 10 ++--- src/App.native.tsx | 6 +-- src/Navigation.tsx | 12 +++--- .../components/NoAccessScreen.tsx | 10 ++--- .../components/RedirectOverlay.tsx | 8 ++-- src/alf/fonts.ts | 8 ++-- src/alf/typography.tsx | 8 ++-- src/alf/util/systemUI.ts | 4 +- src/alf/util/useColorModeTheme.ts | 4 +- src/components/ContextMenu/index.tsx | 10 ++--- src/components/ContextMenu/types.ts | 4 +- src/components/Dialog/context.ts | 2 +- src/components/Dialog/index.tsx | 22 +++++------ src/components/Dialog/index.web.tsx | 2 +- src/components/Dialog/sheet-wrapper.ts | 4 +- src/components/Dialog/types.ts | 2 +- src/components/FeedInterstitials.tsx | 6 +-- src/components/InterestTabs.tsx | 6 +-- .../InternationalPhoneCodeSelect.tsx | 6 +-- src/components/Layout/Header/index.tsx | 8 ++-- src/components/Layout/index.tsx | 8 ++-- src/components/Link.tsx | 22 +++++------ src/components/Menu/index.tsx | 10 ++--- src/components/Menu/index.web.tsx | 2 +- src/components/Menu/types.ts | 4 +- src/components/NewskieDialog.tsx | 4 +- .../PolicyUpdateOverlay/Overlay.tsx | 6 +-- src/components/PolicyUpdateOverlay/index.tsx | 4 +- .../updates/202508/index.tsx | 4 +- .../Post/Embed/ExternalEmbed/ExternalGif.tsx | 10 ++--- .../Embed/ExternalEmbed/ExternalPlayer.tsx | 4 +- .../Post/Embed/ExternalEmbed/Gif.tsx | 12 +++--- .../Post/Embed/ExternalEmbed/index.tsx | 4 +- .../VideoEmbed/ActiveVideoWebContext.tsx | 6 +-- .../web-controls/VideoControls.tsx | 4 +- .../PostControls/ShareMenu/ShareMenuItems.tsx | 4 +- .../ShareMenu/ShareMenuItems.web.tsx | 4 +- src/components/ProgressGuide/FollowDialog.tsx | 10 ++--- src/components/ProgressGuide/Toast.tsx | 6 +-- src/components/RichTextTag.tsx | 10 ++--- src/components/ScreenTransition.tsx | 6 +-- src/components/Select/index.tsx | 2 +- src/components/Select/index.web.tsx | 2 +- src/components/Select/types.ts | 4 +- src/components/StarterPack/Main/FeedsList.tsx | 6 +-- src/components/StarterPack/Main/PostsList.tsx | 4 +- .../StarterPack/Main/ProfilesList.tsx | 6 +-- .../StarterPack/ProfileStarterPacks.tsx | 4 +- src/components/StarterPack/QrCode.tsx | 12 +++--- src/components/StarterPack/QrCodeDialog.tsx | 12 +++--- src/components/StarterPack/ShareDialog.tsx | 12 ++++-- .../Wizard/WizardEditListDialog.tsx | 6 +-- src/components/SubtleHover.tsx | 6 +-- src/components/WhoCanReply.tsx | 6 +-- .../SubscribeProfileDialog.tsx | 4 +- .../ageAssurance/AgeAssuranceAccountCard.tsx | 4 +- .../AgeAssuranceRedirectDialog.tsx | 6 +-- .../contacts/FindContactsBannerNUX.tsx | 4 +- .../contacts/components/OTPInput.tsx | 6 +-- src/components/dialogs/BirthDateSettings.tsx | 6 +-- .../dialogs/DeviceLocationRequestDialog.tsx | 8 ++-- src/components/dialogs/GifSelect.tsx | 6 +-- .../dialogs/InAppBrowserConsent.tsx | 4 +- src/components/dialogs/MutedWords.tsx | 4 +- .../dialogs/PostInteractionSettingsDialog.tsx | 4 +- .../dialogs/SearchablePeopleList.tsx | 8 ++-- src/components/dialogs/Signin.tsx | 6 +-- src/components/dialogs/StarterPackDialog.tsx | 8 ++-- .../dialogs/lists/CreateOrEditListDialog.tsx | 4 +- .../dialogs/nuxs/ActivitySubscriptions.tsx | 12 +++--- .../dialogs/nuxs/BookmarksAnnouncement.tsx | 10 ++--- .../dialogs/nuxs/FindContactsAnnouncement.tsx | 6 +-- .../nuxs/InitialVerificationAnnouncement.tsx | 4 +- .../dialogs/nuxs/LiveNowBetaDialog.tsx | 10 ++--- src/components/dms/ActionsWrapper.tsx | 2 +- src/components/dms/ActionsWrapper.web.tsx | 8 ++-- src/components/dms/AfterReportDialog.tsx | 4 +- src/components/dms/ChatEmptyPill.tsx | 6 +-- src/components/dms/LeaveConvoPrompt.tsx | 4 +- src/components/dms/MessageContextMenu.tsx | 4 +- src/components/dms/MessageItem.tsx | 6 +-- src/components/dms/MessagesListHeader.tsx | 4 +- src/components/dms/NewMessagesPill.tsx | 10 ++--- src/components/forms/SearchInput.tsx | 4 +- src/components/forms/Toggle/index.tsx | 4 +- src/components/hooks/useFullscreen.ts | 4 +- .../hooks/useStarterPackEntry.native.ts | 4 +- src/components/hooks/useWelcomeModal.ts | 4 +- src/components/images/AutoSizedImage.tsx | 4 +- .../intents/VerifyEmailIntentDialog.tsx | 6 +-- .../moderation/LabelsOnMeDialog.tsx | 4 +- .../moderation/ModerationDetailsDialog.tsx | 6 +-- .../moderation/ReportDialog/index.tsx | 4 +- src/env/index.ts | 11 ++++++ src/env/index.web.ts | 13 +++++++ .../components/LiveEventFeedOptionsMenu.tsx | 4 +- src/features/liveEvents/preferences.ts | 4 +- src/geolocation/device.ts | 4 +- src/geolocation/util.ts | 4 +- src/lib/api/feed/utils.ts | 4 +- src/lib/browser.native.ts | 2 +- src/lib/browser.ts | 2 +- .../custom-animations/AccordionAnimation.tsx | 6 +-- src/lib/custom-animations/PressableScale.tsx | 4 +- src/lib/haptics.ts | 6 +-- src/lib/hooks/useAccountSwitcher.ts | 4 +- src/lib/hooks/useBottomBarOffset.ts | 4 +- src/lib/hooks/useIntentHandler.ts | 6 +-- src/lib/hooks/useIsKeyboardVisible.ts | 6 +-- src/lib/hooks/useNotificationHandler.ts | 6 +-- src/lib/hooks/useOTAUpdates.ts | 8 ++-- src/lib/hooks/useOpenLink.ts | 4 +- src/lib/hooks/usePermissions.ts | 6 +-- src/lib/hooks/useTranslate.ts | 4 +- src/lib/hooks/useWebMediaQueries.tsx | 4 +- src/lib/media/manip.ts | 8 ++-- src/lib/media/picker.shared.ts | 6 +-- src/lib/media/save-image.ios.ts | 4 +- src/lib/media/save-image.ts | 4 +- src/lib/notifications/notifications.ts | 10 ++--- src/lib/react-query.tsx | 6 +-- src/lib/sharing.ts | 8 ++-- src/lib/statsig/statsig.tsx | 4 +- src/lib/strings/embed-player.ts | 8 ++-- src/lib/styles.ts | 4 +- src/logger/index.ts | 4 +- src/logger/transports/console.ts | 4 +- src/platform/detection.ts | 12 ------ src/platform/urls.tsx | 26 ------------- src/screens/Bookmarks/index.tsx | 4 +- src/screens/Deactivated.tsx | 8 ++-- src/screens/FindContactsFlowScreen.tsx | 4 +- src/screens/Login/LoginForm.tsx | 4 +- src/screens/Messages/ChatList.tsx | 6 +-- src/screens/Messages/Conversation.tsx | 4 +- src/screens/Messages/Inbox.tsx | 4 +- src/screens/Messages/Settings.tsx | 4 +- .../Messages/components/ChatListItem.tsx | 8 ++-- .../Messages/components/MessageInput.tsx | 10 ++--- .../Messages/components/MessagesList.tsx | 14 +++---- src/screens/Moderation/index.tsx | 4 +- src/screens/Onboarding/Layout.tsx | 10 ++--- src/screens/Onboarding/StepFinished/index.tsx | 4 +- src/screens/Onboarding/StepProfile/index.tsx | 6 +-- .../StepSuggestedAccounts/index.tsx | 10 ++--- src/screens/Onboarding/index.tsx | 4 +- .../PostThread/components/GrowthHack.tsx | 4 +- .../ThreadItemAnchorFollowButton.tsx | 4 +- src/screens/Profile/Header/GrowableAvatar.tsx | 4 +- src/screens/Profile/Header/GrowableBanner.tsx | 6 +-- src/screens/Profile/Header/Handle.tsx | 6 +-- .../Profile/Header/ProfileHeaderLabeler.tsx | 6 +-- .../Profile/Header/ProfileHeaderStandard.tsx | 6 +-- src/screens/Profile/Header/Shell.tsx | 10 ++--- .../Profile/Header/StatusBarShadow.tsx | 4 +- .../Profile/Header/SuggestedFollows.tsx | 4 +- src/screens/Profile/Header/index.tsx | 4 +- src/screens/Profile/ProfileFeed/index.tsx | 6 +-- src/screens/Profile/Sections/Feed.tsx | 8 ++-- src/screens/Profile/Sections/Labels.tsx | 6 +-- .../Profile/components/ProfileFeedHeader.tsx | 6 +-- src/screens/ProfileList/AboutSection.tsx | 4 +- src/screens/ProfileList/FeedSection.tsx | 4 +- .../components/MoreOptionsMenu.tsx | 8 ++-- src/screens/Search/Shell.tsx | 22 +++++------ .../Search/components/AutocompleteResults.tsx | 4 +- .../Search/modules/ExploreRecommendations.tsx | 4 +- src/screens/Settings/AboutSettings.tsx | 8 ++-- .../Settings/AccessibilitySettings.tsx | 4 +- .../Settings/AppIconSettings/index.tsx | 6 +-- src/screens/Settings/AppearanceSettings.tsx | 4 +- .../Settings/ContentAndMediaSettings.tsx | 4 +- src/screens/Settings/FindContactsSettings.tsx | 4 +- .../Settings/NotificationSettings/index.tsx | 10 ++--- src/screens/Settings/Settings.tsx | 8 ++-- .../components/AddAppPasswordDialog.tsx | 4 +- .../components/ChangePasswordDialog.tsx | 4 +- .../components/DisableEmail2FADialog.tsx | 4 +- src/screens/Signup/StepCaptcha/index.tsx | 14 ++++--- src/screens/Signup/StepInfo/index.tsx | 6 +-- src/screens/Signup/index.tsx | 4 +- src/screens/SignupQueued.tsx | 6 +-- .../StarterPack/StarterPackLandingScreen.tsx | 8 ++-- src/screens/StarterPack/StarterPackScreen.tsx | 8 ++-- .../StarterPack/Wizard/StepProfiles.tsx | 4 +- src/screens/StarterPack/Wizard/index.tsx | 6 +-- src/screens/Takendown.tsx | 4 +- src/screens/VideoFeed/index.tsx | 8 ++-- src/state/a11y.tsx | 4 +- src/state/dialogs/index.tsx | 4 +- src/state/gallery.ts | 10 ++--- src/state/messages/convo/agent.ts | 4 +- src/state/preferences/kawaii.tsx | 4 +- src/state/queries/usePostThread/index.ts | 4 +- src/state/session/index.tsx | 4 +- src/state/shell/logged-out.tsx | 4 +- src/state/shell/selected-feed.tsx | 6 +-- src/view/com/auth/SplashScreen.web.tsx | 4 +- src/view/com/composer/Composer.tsx | 38 +++++++++---------- src/view/com/composer/GifAltText.tsx | 4 +- src/view/com/composer/KeyboardAccessory.tsx | 4 +- src/view/com/composer/SelectMediaButton.tsx | 18 ++++----- src/view/com/composer/labels/LabelsBtn.tsx | 4 +- src/view/com/composer/photos/Gallery.tsx | 4 +- .../composer/photos/ImageAltTextDialog.tsx | 6 +-- .../com/composer/photos/OpenCameraBtn.tsx | 4 +- .../PostLanguageSelectDialog.tsx | 8 ++-- .../com/composer/text-input/TextInput.tsx | 6 +-- .../com/composer/threadgate/ThreadgateBtn.tsx | 4 +- .../com/composer/videos/SubtitleDialog.tsx | 16 +++++--- .../videos/VideoTranscodeProgress.tsx | 4 +- src/view/com/feeds/ComposerPrompt.tsx | 10 ++--- src/view/com/feeds/FeedPage.tsx | 8 ++-- src/view/com/feeds/MissingFeed.tsx | 6 +-- src/view/com/feeds/ProfileFeedgens.tsx | 8 ++-- src/view/com/lightbox/ImageViewing/index.tsx | 10 ++--- src/view/com/lists/ProfileLists.tsx | 8 ++-- src/view/com/modals/DeleteAccount.tsx | 6 +-- src/view/com/modals/UserAddRemoveLists.tsx | 10 ++--- src/view/com/pager/PagerHeaderContext.tsx | 4 +- src/view/com/pager/PagerWithHeader.tsx | 6 +-- src/view/com/posts/CustomFeedEmptyState.tsx | 4 +- src/view/com/posts/FollowingEmptyState.tsx | 4 +- src/view/com/posts/FollowingEndOfFeed.tsx | 4 +- src/view/com/posts/PostFeed.tsx | 8 ++-- src/view/com/profile/ProfileFollows.tsx | 4 +- src/view/com/profile/ProfileMenu.tsx | 12 +++--- src/view/com/util/Link.tsx | 8 ++-- src/view/com/util/List.tsx | 6 +-- src/view/com/util/MainScrollProvider.tsx | 16 ++++---- src/view/com/util/PostMeta.tsx | 6 +-- src/view/com/util/UserAvatar.tsx | 14 +++---- src/view/com/util/UserBanner.tsx | 10 ++--- src/view/com/util/ViewSelector.tsx | 4 +- src/view/com/util/fab/FABInner.tsx | 4 +- src/view/com/util/layouts/LoggedOutLayout.tsx | 4 +- src/view/com/util/text/Text.tsx | 6 +-- src/view/screens/Feeds.tsx | 8 ++-- src/view/screens/Home.tsx | 4 +- src/view/screens/Notifications.tsx | 6 +-- src/view/shell/Drawer.tsx | 4 +- .../createNativeStackNavigatorWithAuth.tsx | 6 +-- src/view/shell/index.tsx | 14 ++++--- 244 files changed, 789 insertions(+), 789 deletions(-) delete mode 100644 src/platform/detection.ts delete mode 100644 src/platform/urls.tsx diff --git a/CLAUDE.md b/CLAUDE.md index e1a7d174ed..9ce958790a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -439,9 +439,9 @@ Example from Dialog: Platform detection: ```tsx -import {isWeb, isNative, isIOS, isAndroid} from '#/platform/detection' +import {IS_WEB, IS_NATIVE, IS_IOS, IS_ANDROID} from '#/env' -if (isNative) { +if (IS_NATIVE) { // Native-specific logic } ``` diff --git a/modules/bottom-sheet/src/BottomSheetNativeComponent.tsx b/modules/bottom-sheet/src/BottomSheetNativeComponent.tsx index aa69cfd599..bbeab8c469 100644 --- a/modules/bottom-sheet/src/BottomSheetNativeComponent.tsx +++ b/modules/bottom-sheet/src/BottomSheetNativeComponent.tsx @@ -11,7 +11,7 @@ import { import {useSafeAreaInsets} from 'react-native-safe-area-context' import {requireNativeModule, requireNativeViewManager} from 'expo-modules-core' -import {isIOS} from '#/platform/detection' +import {IS_IOS} from '#/env' import { type BottomSheetState, type BottomSheetViewProps, @@ -30,7 +30,7 @@ const NativeView: React.ComponentType< const NativeModule = requireNativeModule('BottomSheet') -const isIOS15 = +const IS_IOS15 = Platform.OS === 'ios' && // semvar - can be 3 segments, so can't use Number(Platform.Version) Number(Platform.Version.split('.').at(0)) < 16 @@ -91,7 +91,7 @@ export class BottomSheetNativeComponent extends React.Component< } let extraStyles - if (isIOS15 && this.state.viewHeight) { + if (IS_IOS15 && this.state.viewHeight) { const {viewHeight} = this.state const cornerRadius = this.props.cornerRadius ?? 0 if (viewHeight < screenHeight / 2) { @@ -112,7 +112,7 @@ export class BottomSheetNativeComponent extends React.Component< onStateChange={this.onStateChange} extraStyles={extraStyles} onLayout={e => { - if (isIOS15) { + if (IS_IOS15) { const {height} = e.nativeEvent.layout this.setState({viewHeight: height}) } @@ -153,7 +153,7 @@ function BottomSheetNativeComponentInner({ const insets = useSafeAreaInsets() const cornerRadius = rest.cornerRadius ?? 0 - const sheetHeight = isIOS ? screenHeight - insets.top : screenHeight + const sheetHeight = IS_IOS ? screenHeight - insets.top : screenHeight return ( ) { ) async function handlePushNotificationEntry() { - if (!isNative) return + if (!IS_NATIVE) return // deep links take precedence - on android, // getLastNotificationResponseAsync returns a "notification" @@ -1069,7 +1069,7 @@ function reset(): Promise { navigationRef.dispatch( CommonActions.reset({ index: 0, - routes: [{name: isNative ? 'HomeTab' : 'Home'}], + routes: [{name: IS_NATIVE ? 'HomeTab' : 'Home'}], }), ) return Promise.race([ @@ -1103,7 +1103,7 @@ function logModuleInitTime() { initMs, }) - if (isWeb) { + if (IS_WEB) { const referrerInfo = Referrer.getReferrerInfo() if (referrerInfo && referrerInfo.hostname !== 'bsky.app') { logEvent('deepLink:referrerReceived', { diff --git a/src/ageAssurance/components/NoAccessScreen.tsx b/src/ageAssurance/components/NoAccessScreen.tsx index 8b8ad0ccc1..ca5716a37a 100644 --- a/src/ageAssurance/components/NoAccessScreen.tsx +++ b/src/ageAssurance/components/NoAccessScreen.tsx @@ -10,8 +10,6 @@ import { } from '#/lib/hooks/useCreateSupportLink' import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo' import {logger} from '#/logger' -import {isWeb} from '#/platform/detection' -import {isNative} from '#/platform/detection' import {useIsBirthdateUpdateAllowed} from '#/state/birthdate' import {useSessionApi} from '#/state/session' import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' @@ -38,6 +36,8 @@ import { isLegacyBirthdateBug, useAgeAssuranceRegionConfig, } from '#/ageAssurance/util' +import {IS_WEB} from '#/env' +import {IS_NATIVE} from '#/env' import {useDeviceGeolocationApi} from '#/geolocation' const textStyles = [a.text_md, a.leading_snug] @@ -74,7 +74,7 @@ export function NoAccessScreen() { }, []) const onPressLogout = useCallback(() => { - if (isWeb) { + if (IS_WEB) { // We're switching accounts, which remounts the entire app. // On mobile, this gets us Home, but on the web we also need reset the URL. // We can't change the URL via a navigate() call because the navigator @@ -139,7 +139,7 @@ export function NoAccessScreen() { contentContainerStyle={[ a.px_2xl, { - paddingTop: isWeb + paddingTop: IS_WEB ? a.p_5xl.padding : insets.top + a.p_2xl.padding, paddingBottom: 100, @@ -359,7 +359,7 @@ function AccessSection() { )} - {isNative && ( + {IS_NATIVE && ( <> diff --git a/src/ageAssurance/components/RedirectOverlay.tsx b/src/ageAssurance/components/RedirectOverlay.tsx index dc4475187f..b7bc51df3c 100644 --- a/src/ageAssurance/components/RedirectOverlay.tsx +++ b/src/ageAssurance/components/RedirectOverlay.tsx @@ -15,8 +15,6 @@ import {useLingui} from '@lingui/react' import {retry} from '#/lib/async/retry' import {wait} from '#/lib/async/wait' import {parseLinkingUrl} from '#/lib/parseLinkingUrl' -import {isWeb} from '#/platform/detection' -import {isIOS} from '#/platform/detection' import {useAgent, useSession} from '#/state/session' import {atoms as a, platform, useBreakpoints, useTheme} from '#/alf' import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge' @@ -28,6 +26,8 @@ import {Loader} from '#/components/Loader' import {Text} from '#/components/Typography' import {refetchAgeAssuranceServerState} from '#/ageAssurance' import {logger} from '#/ageAssurance' +import {IS_WEB} from '#/env' +import {IS_IOS} from '#/env' export type RedirectOverlayState = { result: 'success' | 'unknown' @@ -92,7 +92,7 @@ export function Provider({children}: {children?: React.ReactNode}) { actorDid: params.get('actorDid') ?? undefined, }) - if (isWeb) { + if (IS_WEB) { // Clear the URL parameters so they don't re-trigger history.pushState(null, '', '/') } @@ -145,7 +145,7 @@ export function RedirectOverlay() { // setting a zIndex when using FullWindowOverlay on iOS // means the taps pass straight through to the underlying content (???) // so don't set it on iOS. FullWindowOverlay already does the job. - !isIOS && {zIndex: 9999}, + !IS_IOS && {zIndex: 9999}, t.atoms.bg, gtMobile ? a.p_2xl : a.p_xl, a.align_center, diff --git a/src/alf/fonts.ts b/src/alf/fonts.ts index ddf4c0b186..e9988e48dc 100644 --- a/src/alf/fonts.ts +++ b/src/alf/fonts.ts @@ -1,6 +1,6 @@ import {type TextStyle} from 'react-native' -import {isAndroid, isWeb} from '#/platform/detection' +import {IS_ANDROID, IS_WEB} from '#/env' import {type Device, device} from '#/storage' const WEB_FONT_FAMILIES = `system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"` @@ -39,7 +39,7 @@ export function setFontFamily(fontFamily: Device['fontFamily']) { */ export function applyFonts(style: TextStyle, fontFamily: 'system' | 'theme') { if (fontFamily === 'theme') { - if (isAndroid) { + if (IS_ANDROID) { style.fontFamily = { 400: 'Inter-Regular', @@ -71,7 +71,7 @@ export function applyFonts(style: TextStyle, fontFamily: 'system' | 'theme') { } } - if (isWeb) { + if (IS_WEB) { // fallback families only supported on web style.fontFamily += `, ${WEB_FONT_FAMILIES}` } @@ -83,7 +83,7 @@ export function applyFonts(style: TextStyle, fontFamily: 'system' | 'theme') { style.fontVariant = (style.fontVariant || []).concat('no-contextual') } else { // fallback families only supported on web - if (isWeb) { + if (IS_WEB) { style.fontFamily = style.fontFamily || WEB_FONT_FAMILIES } diff --git a/src/alf/typography.tsx b/src/alf/typography.tsx index 2def71a290..d79ad81ead 100644 --- a/src/alf/typography.tsx +++ b/src/alf/typography.tsx @@ -4,9 +4,9 @@ import {type StyleProp, type TextStyle} from 'react-native' import {UITextView} from 'react-native-uitextview' import createEmojiRegex from 'emoji-regex' -import {isNative} from '#/platform/detection' -import {isIOS} from '#/platform/detection' import {type Alf, applyFonts, atoms, flatten} from '#/alf' +import {IS_NATIVE} from '#/env' +import {IS_IOS} from '#/env' /** * Ensures that `lineHeight` defaults to a relative value of `1`, or applies @@ -34,7 +34,7 @@ export function normalizeTextStyles( if (s.lineHeight !== 0 && s.lineHeight <= 2) { s.lineHeight = Math.round(s.fontSize * s.lineHeight) } - } else if (!isNative) { + } else if (!IS_NATIVE) { s.lineHeight = s.fontSize } @@ -81,7 +81,7 @@ export function renderChildrenWithEmoji( props: Omit = {}, emoji: boolean, ) { - if (!isIOS || !emoji) { + if (!IS_IOS || !emoji) { return children } return Children.map(children, child => { diff --git a/src/alf/util/systemUI.ts b/src/alf/util/systemUI.ts index d013f0c3f9..743c9d39db 100644 --- a/src/alf/util/systemUI.ts +++ b/src/alf/util/systemUI.ts @@ -2,10 +2,10 @@ import * as SystemUI from 'expo-system-ui' import {type Theme} from '@bsky.app/alf' import {logger} from '#/logger' -import {isAndroid} from '#/platform/detection' +import {IS_ANDROID} from '#/env' export function setSystemUITheme(themeType: 'theme' | 'lightbox', t: Theme) { - if (isAndroid) { + if (IS_ANDROID) { try { if (themeType === 'theme') { SystemUI.setBackgroundColorAsync(t.atoms.bg.backgroundColor) diff --git a/src/alf/util/useColorModeTheme.ts b/src/alf/util/useColorModeTheme.ts index 83b13a94ba..836aac3073 100644 --- a/src/alf/util/useColorModeTheme.ts +++ b/src/alf/util/useColorModeTheme.ts @@ -2,9 +2,9 @@ import React from 'react' import {type ColorSchemeName, useColorScheme} from 'react-native' import {type ThemeName} from '@bsky.app/alf' -import {isWeb} from '#/platform/detection' import {useThemePrefs} from '#/state/shell' import {dark, dim, light} from '#/alf/themes' +import {IS_WEB} from '#/env' export function useColorModeTheme(): ThemeName { const theme = useThemeName() @@ -40,7 +40,7 @@ function getThemeName( function updateDocument(theme: ThemeName) { // @ts-ignore web only - if (isWeb && typeof window !== 'undefined') { + if (IS_WEB && typeof window !== 'undefined') { // @ts-ignore web only const html = window.document.documentElement // @ts-ignore web only diff --git a/src/components/ContextMenu/index.tsx b/src/components/ContextMenu/index.tsx index 7d078bb2de..225a9cea37 100644 --- a/src/components/ContextMenu/index.tsx +++ b/src/components/ContextMenu/index.tsx @@ -49,7 +49,6 @@ import {HITSLOP_10} from '#/lib/constants' import {useHaptics} from '#/lib/haptics' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {logger} from '#/logger' -import {isAndroid, isIOS} from '#/platform/detection' import {atoms as a, platform, tokens, useTheme} from '#/alf' import { Context, @@ -71,6 +70,7 @@ import { import {useInteractionState} from '#/components/hooks/useInteractionState' import {createPortalGroup} from '#/components/Portal' import {Text} from '#/components/Typography' +import {IS_ANDROID, IS_IOS} from '#/env' import {Backdrop} from './Backdrop' export { @@ -81,14 +81,14 @@ export { const {Provider: PortalProvider, Outlet, Portal} = createPortalGroup() const SPRING_IN: WithSpringConfig = { - mass: isIOS ? 1.25 : 0.75, + mass: IS_IOS ? 1.25 : 0.75, damping: 50, stiffness: 1100, restDisplacementThreshold: 0.01, } const SPRING_OUT: WithSpringConfig = { - mass: isIOS ? 1.25 : 0.75, + mass: IS_IOS ? 1.25 : 0.75, damping: 150, stiffness: 1000, restDisplacementThreshold: 0.01, @@ -209,7 +209,7 @@ export function Root({children}: {children: React.ReactNode}) { ) useEffect(() => { - if (isAndroid && context.isOpen) { + if (IS_ANDROID && context.isOpen) { const listener = BackHandler.addEventListener('hardwareBackPress', () => { context.close() return true @@ -331,7 +331,7 @@ export function Trigger({children, label, contentLabel, style}: TriggerProps) { {children({ - isNative: true, + IS_NATIVE: true, control: {isOpen: context.isOpen, open}, state: { pressed: false, diff --git a/src/components/ContextMenu/types.ts b/src/components/ContextMenu/types.ts index 13f365e533..bda2245420 100644 --- a/src/components/ContextMenu/types.ts +++ b/src/components/ContextMenu/types.ts @@ -85,7 +85,7 @@ export type TriggerProps = { } export type TriggerChildProps = | { - isNative: true + IS_NATIVE: true control: { isOpen: boolean open: (mode: 'full' | 'auxiliary-only') => void @@ -115,7 +115,7 @@ export type TriggerChildProps = } } | { - isNative: false + IS_NATIVE: false control: Dialog.DialogOuterProps['control'] state: { hovered: false diff --git a/src/components/Dialog/context.ts b/src/components/Dialog/context.ts index b7e3c78d5e..2c33b5bdcb 100644 --- a/src/components/Dialog/context.ts +++ b/src/components/Dialog/context.ts @@ -18,7 +18,7 @@ import {BottomSheetSnapPoint} from '../../../modules/bottom-sheet/src/BottomShee export const Context = createContext({ close: () => {}, - isNativeDialog: false, + IS_NATIVEDialog: false, nativeSnapPoint: BottomSheetSnapPoint.Hidden, disableDrag: false, setDisableDrag: () => {}, diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx index b6d47fd3b4..6085b2f070 100644 --- a/src/components/Dialog/index.tsx +++ b/src/components/Dialog/index.tsx @@ -26,7 +26,6 @@ import {useLingui} from '@lingui/react' import {useEnableKeyboardController} from '#/lib/hooks/useEnableKeyboardController' import {ScrollProvider} from '#/lib/ScrollContext' import {logger} from '#/logger' -import {isAndroid, isIOS} from '#/platform/detection' import {useA11y} from '#/state/a11y' import {useDialogStateControlContext} from '#/state/dialogs' import {List, type ListMethods, type ListProps} from '#/view/com/util/List' @@ -39,6 +38,7 @@ import { type DialogOuterProps, } from '#/components/Dialog/types' import {createInput} from '#/components/forms/TextField' +import {IS_ANDROID, IS_IOS} from '#/env' import {BottomSheet, BottomSheetSnapPoint} from '../../../modules/bottom-sheet' import { type BottomSheetSnapPointChangeEvent, @@ -154,7 +154,7 @@ export function Outer({ const context = React.useMemo( () => ({ close, - isNativeDialog: true, + IS_NATIVEDialog: true, nativeSnapPoint: snapPoint, disableDrag, setDisableDrag, @@ -209,7 +209,7 @@ export const ScrollableInner = React.forwardRef( const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext() const insets = useSafeAreaInsets() - useEnableKeyboardController(isIOS) + useEnableKeyboardController(IS_IOS) const [keyboardHeight, setKeyboardHeight] = React.useState(0) @@ -224,7 +224,7 @@ export const ScrollableInner = React.forwardRef( ) let paddingBottom = 0 - if (isIOS) { + if (IS_IOS) { paddingBottom += keyboardHeight / 4 if (nativeSnapPoint === BottomSheetSnapPoint.Full) { paddingBottom += insets.bottom + tokens.space.md @@ -240,7 +240,7 @@ export const ScrollableInner = React.forwardRef( } const onScroll = (e: NativeSyntheticEvent) => { - if (!isAndroid) { + if (!IS_ANDROID) { return } const {contentOffset} = e.nativeEvent @@ -260,12 +260,12 @@ export const ScrollableInner = React.forwardRef( contentContainerStyle, ]} ref={ref} - showsVerticalScrollIndicator={isAndroid ? false : undefined} + showsVerticalScrollIndicator={IS_ANDROID ? false : undefined} {...props} bounces={nativeSnapPoint === BottomSheetSnapPoint.Full} bottomOffset={30} scrollEventThrottle={50} - onScroll={isAndroid ? onScroll : undefined} + onScroll={IS_ANDROID ? onScroll : undefined} keyboardShouldPersistTaps="handled" // TODO: figure out why this positions the header absolutely (rather than stickily) // on Android. fine to disable for now, because we don't have any @@ -289,11 +289,11 @@ export const InnerFlatList = React.forwardRef< const insets = useSafeAreaInsets() const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext() - useEnableKeyboardController(isIOS) + useEnableKeyboardController(IS_IOS) const onScroll = (e: ScrollEvent) => { 'worklet' - if (!isAndroid) { + if (!IS_ANDROID) { return } const {contentOffset} = e @@ -311,7 +311,7 @@ export const InnerFlatList = React.forwardRef< bounces={nativeSnapPoint === BottomSheetSnapPoint.Full} ListFooterComponent={} ref={ref} - showsVerticalScrollIndicator={isAndroid ? false : undefined} + showsVerticalScrollIndicator={IS_ANDROID ? false : undefined} {...props} style={[a.h_full, style]} /> @@ -326,7 +326,7 @@ export function FlatListFooter({children}: {children: React.ReactNode}) { const {height} = useReanimatedKeyboardAnimation() const animatedStyle = useAnimatedStyle(() => { - if (!isIOS) return {} + if (!IS_IOS) return {} return { transform: [{translateY: Math.min(0, height.get() + bottom - 10)}], } diff --git a/src/components/Dialog/index.web.tsx b/src/components/Dialog/index.web.tsx index cf250faf79..e212d1e969 100644 --- a/src/components/Dialog/index.web.tsx +++ b/src/components/Dialog/index.web.tsx @@ -98,7 +98,7 @@ export function Outer({ const context = React.useMemo( () => ({ close, - isNativeDialog: false, + IS_NATIVEDialog: false, nativeSnapPoint: 0, disableDrag: false, setDisableDrag: () => {}, diff --git a/src/components/Dialog/sheet-wrapper.ts b/src/components/Dialog/sheet-wrapper.ts index b655dde000..d6ff687859 100644 --- a/src/components/Dialog/sheet-wrapper.ts +++ b/src/components/Dialog/sheet-wrapper.ts @@ -1,7 +1,7 @@ import {useCallback} from 'react' import {SystemBars} from 'react-native-edge-to-edge' -import {isIOS} from '#/platform/detection' +import {IS_IOS} from '#/env' /** * If we're calling a system API like the image picker that opens a sheet @@ -9,7 +9,7 @@ import {isIOS} from '#/platform/detection' */ export function useSheetWrapper() { return useCallback(async (promise: Promise): Promise => { - if (isIOS) { + if (IS_IOS) { const entry = SystemBars.pushStackEntry({ style: { statusBar: 'light', diff --git a/src/components/Dialog/types.ts b/src/components/Dialog/types.ts index 938d7c744d..eda9ffd29b 100644 --- a/src/components/Dialog/types.ts +++ b/src/components/Dialog/types.ts @@ -39,7 +39,7 @@ export type DialogControlProps = DialogControlRefProps & { export type DialogContextProps = { close: DialogControlProps['close'] - isNativeDialog: boolean + IS_NATIVEDialog: boolean nativeSnapPoint: BottomSheetSnapPoint disableDrag: boolean setDisableDrag: React.Dispatch> diff --git a/src/components/FeedInterstitials.tsx b/src/components/FeedInterstitials.tsx index 1cde13434f..0034e120be 100644 --- a/src/components/FeedInterstitials.tsx +++ b/src/components/FeedInterstitials.tsx @@ -10,7 +10,6 @@ import {type NavigationProp} from '#/lib/routes/types' import {logEvent, useGate} from '#/lib/statsig/statsig' import {logger} from '#/logger' import {type MetricEvents} from '#/logger/metrics' -import {isIOS} from '#/platform/detection' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useGetPopularFeedsQuery} from '#/state/queries/feed' import {type FeedDescriptor} from '#/state/queries/post-feed' @@ -39,6 +38,7 @@ import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import {InlineLinkText} from '#/components/Link' import * as ProfileCard from '#/components/ProfileCard' import {Text} from '#/components/Typography' +import {IS_IOS} from '#/env' import type * as bsky from '#/types/bsky' import {FollowDialogWithoutGuide} from './ProgressGuide/FollowDialog' import {ProgressGuideList} from './ProgressGuide/List' @@ -692,7 +692,7 @@ export function ProfileGrid({ t.atoms.border_contrast_low, t.atoms.bg_contrast_25, ]} - pointerEvents={isIOS ? 'auto' : 'box-none'}> + pointerEvents={IS_IOS ? 'auto' : 'box-none'}> + pointerEvents={IS_IOS ? 'auto' : 'box-none'}> {isFeedContext ? ( Suggested for you diff --git a/src/components/InterestTabs.tsx b/src/components/InterestTabs.tsx index 5fd5742177..082593baf2 100644 --- a/src/components/InterestTabs.tsx +++ b/src/components/InterestTabs.tsx @@ -9,7 +9,6 @@ import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' -import {isWeb} from '#/platform/detection' import {DraggableScrollView} from '#/view/com/pager/DraggableScrollView' import {atoms as a, tokens, useTheme, web} from '#/alf' import {transparentifyColor} from '#/alf/util/colorGeneration' @@ -19,6 +18,7 @@ import { ArrowRight_Stroke2_Corner0_Rounded as ArrowRight, } from '#/components/icons/Arrow' import {Text} from '#/components/Typography' +import {IS_WEB} from '#/env' /** * Tab component that automatically scrolls the selected tab into view - used for interests @@ -236,7 +236,7 @@ export function InterestTabs({ ) })} - {isWeb && canScrollLeft && ( + {IS_WEB && canScrollLeft && ( )} - {isWeb && canScrollRight && ( + {IS_WEB && canScrollRight && ( - {isWeb ? : item.unicodeFlag + ' '} + {IS_WEB ? : item.unicodeFlag + ' '} {item.name} @@ -101,7 +101,7 @@ export function InternationalPhoneCodeSelect({ } function Flag({unicodeFlag, svgFlag}: {unicodeFlag: string; svgFlag: any}) { - if (isWeb) { + if (IS_WEB) { return ( @@ -186,7 +186,7 @@ export function TitleText({ a.text_lg, a.font_semi_bold, a.leading_tight, - isIOS && align === 'platform' && a.text_center, + IS_IOS && align === 'platform' && a.text_center, gtMobile && a.text_xl, style, ]} @@ -205,7 +205,7 @@ export function SubtitleText({children}: {children: React.ReactNode}) { style={[ a.text_sm, a.leading_snug, - isIOS && align === 'platform' && a.text_center, + IS_IOS && align === 'platform' && a.text_center, t.atoms.text_contrast_medium, ]} numberOfLines={2}> diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index 5891ca863a..ae01c05b99 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -11,7 +11,6 @@ import Animated, { } from 'react-native-reanimated' import {useSafeAreaInsets} from 'react-native-safe-area-context' -import {isWeb} from '#/platform/detection' import {useShellLayout} from '#/state/shell/shell-layout' import { atoms as a, @@ -23,6 +22,7 @@ import { import {useDialogContext} from '#/components/Dialog' import {CENTER_COLUMN_OFFSET, SCROLLBAR_OFFSET} from '#/components/Layout/const' import {ScrollbarOffsetContext} from '#/components/Layout/context' +import {IS_WEB} from '#/env' export * from '#/components/Layout/const' export * as Header from '#/components/Layout/Header' @@ -43,7 +43,7 @@ export const Screen = memo(function Screen({ const {top} = useSafeAreaInsets() return ( <> - {isWeb && } + {IS_WEB && } - {isWeb ? ( + {IS_WEB ? (
{/* @ts-expect-error web only -esb */} {children} @@ -145,7 +145,7 @@ export const KeyboardAwareContent = memo(function LayoutKeyboardAwareContent({ ]} keyboardShouldPersistTaps="handled" {...props}> - {isWeb ?
{children}
: children} + {IS_WEB ?
{children}
: children} ) }) diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 8618364281..31e9feb824 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -18,12 +18,12 @@ import { isExternalUrl, linkRequiresWarning, } from '#/lib/strings/url-helpers' -import {isNative, isWeb} from '#/platform/detection' import {useModalControls} from '#/state/modals' import {atoms as a, flatten, type TextStyleProp, useTheme, web} from '#/alf' import {Button, type ButtonProps} from '#/components/Button' import {useInteractionState} from '#/components/hooks/useInteractionState' import {Text, type TextProps} from '#/components/Typography' +import {IS_NATIVE, IS_WEB} from '#/env' import {router} from '#/routes' import {useGlobalDialogsControlContext} from './dialogs/Context' @@ -130,7 +130,7 @@ export function useLink({ linkRequiresWarning(href, displayText), ) - if (isWeb) { + if (IS_WEB) { e.preventDefault() } @@ -162,7 +162,7 @@ export function useLink({ ] // does not apply to web's flat navigator - if (isNative && screen !== 'NotFound') { + if (IS_NATIVE && screen !== 'NotFound') { const state = navigation.getState() // if screen is not in the current navigator, it means it's // most likely a tab screen. note: state can be undefined @@ -246,7 +246,7 @@ export function useLink({ (e: GestureResponderEvent) => { const exitEarlyIfFalse = outerOnLongPress?.(e) if (exitEarlyIfFalse === false) return - return isNative && shareOnLongPress ? handleLongPress() : undefined + return IS_NATIVE && shareOnLongPress ? handleLongPress() : undefined }, [outerOnLongPress, handleLongPress, shareOnLongPress], ) @@ -501,7 +501,7 @@ export function WebOnlyInlineLinkText({ onPress, ...props }: Omit) { - return isWeb ? ( + return IS_WEB ? ( {children} @@ -547,7 +547,7 @@ export function createStaticClickIfUnmodified( ): {onPress: Exclude} { return { onPress(e: GestureResponderEvent) { - if (!isWeb || !isModifiedClickEvent(e)) { + if (!IS_WEB || !isModifiedClickEvent(e)) { e.preventDefault() onPressHandler(e) return false @@ -561,7 +561,7 @@ export function createStaticClickIfUnmodified( * intends to deviate from default behavior. */ export function isClickEventWithMetaKey(e: GestureResponderEvent) { - if (!isWeb) return false + if (!IS_WEB) return false const event = e as unknown as MouseEvent return event.metaKey || event.altKey || event.ctrlKey || event.shiftKey } @@ -570,7 +570,7 @@ export function isClickEventWithMetaKey(e: GestureResponderEvent) { * Determines if the web click target is anything other than `_self` */ export function isClickTargetExternal(e: GestureResponderEvent) { - if (!isWeb) return false + if (!IS_WEB) return false const event = e as unknown as MouseEvent const el = event.currentTarget as HTMLAnchorElement return el && el.target && el.target !== '_self' @@ -582,7 +582,7 @@ export function isClickTargetExternal(e: GestureResponderEvent) { * {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button} */ export function isModifiedClickEvent(e: GestureResponderEvent): boolean { - if (!isWeb) return false + if (!IS_WEB) return false const event = e as unknown as MouseEvent const isPrimaryButton = event.button === 0 return ( @@ -596,8 +596,8 @@ export function isModifiedClickEvent(e: GestureResponderEvent): boolean { * {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button} */ export function shouldClickOpenNewTab(e: GestureResponderEvent) { - if (!isWeb) return false + if (!IS_WEB) return false const event = e as unknown as MouseEvent - const isMiddleClick = isWeb && event.button === 1 + const isMiddleClick = IS_WEB && event.button === 1 return isClickEventWithMetaKey(e) || isClickTargetExternal(e) || isMiddleClick } diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index 5ceaf281c3..a5dda666e5 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -10,7 +10,6 @@ import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import flattenReactChildren from 'react-keyed-flatten-children' -import {isAndroid, isIOS, isNative} from '#/platform/detection' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' @@ -30,6 +29,7 @@ import { type TriggerProps, } from '#/components/Menu/types' import {Text} from '#/components/Typography' +import {IS_ANDROID, IS_IOS, IS_NATIVE} from '#/env' export { type DialogControlProps as MenuControlProps, @@ -70,7 +70,7 @@ export function Trigger({ } = useInteractionState() return children({ - isNative: true, + IS_NATIVE: true, control: context.control, state: { hovered: false, @@ -111,7 +111,7 @@ export function Outer({ {children} - {isNative && showCancel && } + {IS_NATIVE && showCancel && } @@ -137,13 +137,13 @@ export function Item({children, label, style, onPress, ...rest}: ItemProps) { onFocus={onFocus} onBlur={onBlur} onPress={async e => { - if (isAndroid) { + if (IS_ANDROID) { /** * Below fix for iOS doesn't work for Android, this does. */ onPress?.(e) context.control.close() - } else if (isIOS) { + } else if (IS_IOS) { /** * Fixes a subtle bug on iOS * {@link https://github.com/bluesky-social/social-app/pull/5849/files#diff-de516ef5e7bd9840cd639213301df38cf03acfcad5bda85a1d63efd249ba79deL124-L127} diff --git a/src/components/Menu/index.web.tsx b/src/components/Menu/index.web.tsx index 339b26350c..72eaf0e12d 100644 --- a/src/components/Menu/index.web.tsx +++ b/src/components/Menu/index.web.tsx @@ -138,7 +138,7 @@ export function Trigger({ {props => children({ - isNative: false, + IS_NATIVE: false, control, state: { hovered, diff --git a/src/components/Menu/types.ts b/src/components/Menu/types.ts index a5fce102ba..a0f0a1ed60 100644 --- a/src/components/Menu/types.ts +++ b/src/components/Menu/types.ts @@ -43,7 +43,7 @@ export type TriggerProps = { } export type TriggerChildProps = | { - isNative: true + IS_NATIVE: true control: Dialog.DialogOuterProps['control'] state: { /** @@ -73,7 +73,7 @@ export type TriggerChildProps = } } | { - isNative: false + IS_NATIVE: false control: Dialog.DialogOuterProps['control'] state: { hovered: boolean diff --git a/src/components/NewskieDialog.tsx b/src/components/NewskieDialog.tsx index 272cc750f8..4ca98bd8b0 100644 --- a/src/components/NewskieDialog.tsx +++ b/src/components/NewskieDialog.tsx @@ -8,7 +8,6 @@ import {differenceInSeconds} from 'date-fns' import {HITSLOP_10} from '#/lib/constants' import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo' import {sanitizeDisplayName} from '#/lib/strings/display-names' -import {isNative} from '#/platform/detection' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useSession} from '#/state/session' import {atoms as a, useTheme, web} from '#/alf' @@ -18,6 +17,7 @@ import {useDialogControl} from '#/components/Dialog' import {Newskie} from '#/components/icons/Newskie' import * as StarterPackCard from '#/components/StarterPack/StarterPackCard' import {Text} from '#/components/Typography' +import {IS_NATIVE} from '#/env' export function NewskieDialog({ profile, @@ -162,7 +162,7 @@ function DialogInner({ ) : null} - {isNative && ( + {IS_NATIVE && ( - {isNative && ( + {IS_NATIVE && ( )} - {!isWeb && ( + {!IS_WEB && ( - {isAndroid && } + {IS_ANDROID && } ) } diff --git a/src/components/moderation/ModerationDetailsDialog.tsx b/src/components/moderation/ModerationDetailsDialog.tsx index 18cbe26fde..1858f4bcff 100644 --- a/src/components/moderation/ModerationDetailsDialog.tsx +++ b/src/components/moderation/ModerationDetailsDialog.tsx @@ -7,13 +7,13 @@ import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo' import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription' import {makeProfileLink} from '#/lib/routes/links' import {listUriToHref} from '#/lib/strings/url-helpers' -import {isNative} from '#/platform/detection' import {useSession} from '#/state/session' import {atoms as a, useGutters, useTheme} from '#/alf' import * as Dialog from '#/components/Dialog' import {InlineLinkText} from '#/components/Link' import {type AppModerationCause} from '#/components/Pills' import {Text} from '#/components/Typography' +import {IS_NATIVE} from '#/env' export {useDialogControl as useModerationDetailsDialogControl} from '#/components/Dialog' @@ -158,7 +158,7 @@ function ModerationDetailsDialogInner({ xGutters, a.py_md, a.border_t, - !isNative && t.atoms.bg_contrast_25, + !IS_NATIVE && t.atoms.bg_contrast_25, t.atoms.border_contrast_low, { borderBottomLeftRadius: a.rounded_md.borderRadius, @@ -219,7 +219,7 @@ function ModerationDetailsDialogInner({ )} - {isNative && } + {IS_NATIVE && } diff --git a/src/components/moderation/ReportDialog/index.tsx b/src/components/moderation/ReportDialog/index.tsx index 487cf6b299..4c8287f373 100644 --- a/src/components/moderation/ReportDialog/index.tsx +++ b/src/components/moderation/ReportDialog/index.tsx @@ -8,7 +8,6 @@ import {wait} from '#/lib/async/wait' import {getLabelingServiceTitle} from '#/lib/moderation' import {sanitizeHandle} from '#/lib/strings/handles' import {Logger} from '#/logger' -import {isNative} from '#/platform/detection' import {useMyLabelersQuery} from '#/state/queries/preferences' import {CharProgress} from '#/view/com/composer/char-progress/CharProgress' import {UserAvatar} from '#/view/com/util/UserAvatar' @@ -29,6 +28,7 @@ import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import {createStaticClick, InlineLinkText, Link} from '#/components/Link' import {Loader} from '#/components/Loader' import {Text} from '#/components/Typography' +import {IS_NATIVE} from '#/env' import {useSubmitReportMutation} from './action' import { BSKY_LABELER_ONLY_REPORT_REASONS, @@ -253,7 +253,7 @@ function Inner(props: ReportDialogProps) { label={_(msg`Report dialog`)} ref={ref} style={[a.w_full, {maxWidth: 500}]}> - + {isHidingAllFeeds && } - {isNative && ( + {IS_NATIVE && ( - {isNative && ( + {IS_NATIVE && ( ) - const webLayout = isWeb && gtMobile + const webLayout = IS_WEB && gtMobile return ( - {isIOS && } + {IS_IOS && } - {isWeb && ( + {IS_WEB && ( - {isWeb ? ( + {IS_WEB ? ( Copy link ) : ( Share via... )} diff --git a/src/screens/StarterPack/Wizard/StepProfiles.tsx b/src/screens/StarterPack/Wizard/StepProfiles.tsx index 3d4fee4b5b..2943f08925 100644 --- a/src/screens/StarterPack/Wizard/StepProfiles.tsx +++ b/src/screens/StarterPack/Wizard/StepProfiles.tsx @@ -4,7 +4,6 @@ import {KeyboardAwareScrollView} from 'react-native-keyboard-controller' import {type AppBskyActorDefs, type ModerationOpts} from '@atproto/api' import {Trans} from '@lingui/macro' -import {isNative} from '#/platform/detection' import {useA11y} from '#/state/a11y' import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete' import {useActorSearch} from '#/state/queries/actor-search' @@ -16,6 +15,7 @@ import {Loader} from '#/components/Loader' import {ScreenTransition} from '#/components/ScreenTransition' import {WizardProfileCard} from '#/components/StarterPack/Wizard/WizardListCard' import {Text} from '#/components/Typography' +import {IS_NATIVE} from '#/env' import type * as bsky from '#/types/bsky' function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) { @@ -89,7 +89,7 @@ export function StepProfiles({ onEndReached={ !query && !screenReaderEnabled ? () => fetchNextPage() : undefined } - onEndReachedThreshold={isNative ? 2 : 0.25} + onEndReachedThreshold={IS_NATIVE ? 2 : 0.25} keyboardDismissMode="on-drag" ListEmptyComponent={ diff --git a/src/screens/StarterPack/Wizard/index.tsx b/src/screens/StarterPack/Wizard/index.tsx index 5b14ef5201..13678ec853 100644 --- a/src/screens/StarterPack/Wizard/index.tsx +++ b/src/screens/StarterPack/Wizard/index.tsx @@ -31,7 +31,6 @@ import { parseStarterPackUri, } from '#/lib/strings/starter-pack' import {logger} from '#/logger' -import {isNative} from '#/platform/detection' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useAllListMembersQuery} from '#/state/queries/list-members' import {useProfileQuery} from '#/state/queries/profile' @@ -59,6 +58,7 @@ import {ListMaybePlaceholder} from '#/components/Lists' import {Loader} from '#/components/Loader' import {WizardEditListDialog} from '#/components/StarterPack/Wizard/WizardEditListDialog' import {Text} from '#/components/Typography' +import {IS_NATIVE} from '#/env' import type * as bsky from '#/types/bsky' import {Provider} from './State' @@ -435,7 +435,7 @@ function Footer({ { paddingBottom: a.pb_lg.paddingBottom + bottomInset, }, - isNative && [ + IS_NATIVE && [ a.border_l, a.border_r, t.atoms.shadow_md, @@ -601,7 +601,7 @@ function Footer({ a.w_full, a.align_center, a.gap_2xl, - isNative ? a.mt_sm : a.mt_md, + IS_NATIVE ? a.mt_sm : a.mt_md, ]}> {state.currentStep === 'Profiles' && items.length < 8 && ( ) - const webLayout = isWeb && gtMobile + const webLayout = IS_WEB && gtMobile useEnableKeyboardController(true) diff --git a/src/screens/VideoFeed/index.tsx b/src/screens/VideoFeed/index.tsx index 6d7f9bc58f..306731e2c9 100644 --- a/src/screens/VideoFeed/index.tsx +++ b/src/screens/VideoFeed/index.tsx @@ -57,7 +57,6 @@ import {sanitizeDisplayName} from '#/lib/strings/display-names' import {cleanError} from '#/lib/strings/errors' import {sanitizeHandle} from '#/lib/strings/handles' import {logger} from '#/logger' -import {isAndroid} from '#/platform/detection' import {useA11y} from '#/state/a11y' import { POST_TOMBSTONE, @@ -101,6 +100,7 @@ import * as Hider from '#/components/moderation/Hider' import {PostControls} from '#/components/PostControls' import {RichText} from '#/components/RichText' import {Text} from '#/components/Typography' +import {IS_ANDROID} from '#/env' import * as bsky from '#/types/bsky' import {Scrubber, VIDEO_PLAYER_BOTTOM_INSET} from './components/Scrubber' @@ -590,10 +590,10 @@ function VideoItemInner({ embed: AppBskyEmbedVideo.View }) { const {bottom} = useSafeAreaInsets() - const [isReady, setIsReady] = useState(!isAndroid) + const [isReady, setIsReady] = useState(!IS_ANDROID) useEventListener(player, 'timeUpdate', evt => { - if (isAndroid && !isReady && evt.currentTime >= 0.05) { + if (IS_ANDROID && !isReady && evt.currentTime >= 0.05) { setIsReady(true) } }) @@ -920,7 +920,7 @@ function Overlay({ {/* - {isAndroid && status === 'loading' && ( + {IS_ANDROID && status === 'loading' && ( ) { * * @see https://github.com/necolas/react-native-web/discussions/2072 */ - screenReaderEnabled: isWeb ? false : screenReaderEnabled, + screenReaderEnabled: IS_WEB ? false : screenReaderEnabled, } }, [reduceMotionEnabled, screenReaderEnabled]) diff --git a/src/state/dialogs/index.tsx b/src/state/dialogs/index.tsx index 51ca8b5140..b474e9fe0a 100644 --- a/src/state/dialogs/index.tsx +++ b/src/state/dialogs/index.tsx @@ -1,8 +1,8 @@ import React from 'react' -import {isWeb} from '#/platform/detection' import {type DialogControlRefProps} from '#/components/Dialog' import {Provider as GlobalDialogsProvider} from '#/components/dialogs/Context' +import {IS_WEB} from '#/env' import {BottomSheetNativeComponent} from '../../../modules/bottom-sheet' interface IDialogContext { @@ -62,7 +62,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) { const openDialogs = React.useRef>(new Set()) const closeAllDialogs = React.useCallback(() => { - if (isWeb) { + if (IS_WEB) { openDialogs.current.forEach(id => { const dialog = activeDialogs.current.get(id) if (dialog) dialog.current.close() diff --git a/src/state/gallery.ts b/src/state/gallery.ts index c8ddba7026..5b5eedd8b8 100644 --- a/src/state/gallery.ts +++ b/src/state/gallery.ts @@ -18,7 +18,7 @@ import {openCropper} from '#/lib/media/picker' import {type PickerImage} from '#/lib/media/picker.shared' import {getDataUriSize} from '#/lib/media/util' import {isCancelledError} from '#/lib/strings/errors' -import {isNative} from '#/platform/detection' +import {IS_NATIVE} from '#/env' export type ImageTransformation = { crop?: ActionCrop['crop'] @@ -55,7 +55,7 @@ export type ComposerImage = let _imageCacheDirectory: string function getImageCacheDirectory(): string | null { - if (isNative) { + if (IS_NATIVE) { return (_imageCacheDirectory ??= joinPath(cacheDirectory!, 'bsky-composer')) } @@ -120,7 +120,7 @@ export async function pasteImage( } export async function cropImage(img: ComposerImage): Promise { - if (!isNative) { + if (!IS_NATIVE) { return img } @@ -244,7 +244,7 @@ export async function compressImage(img: ComposerImage): Promise { } async function moveIfNecessary(from: string) { - const cacheDir = isNative && getImageCacheDirectory() + const cacheDir = IS_NATIVE && getImageCacheDirectory() if (cacheDir && from.startsWith(cacheDir)) { const to = joinPath(cacheDir, nanoid(36)) @@ -260,7 +260,7 @@ async function moveIfNecessary(from: string) { /** Purge files that were created to accomodate image manipulation */ export async function purgeTemporaryImageFiles() { - const cacheDir = isNative && getImageCacheDirectory() + const cacheDir = IS_NATIVE && getImageCacheDirectory() if (cacheDir) { await deleteAsync(cacheDir, {idempotent: true}) diff --git a/src/state/messages/convo/agent.ts b/src/state/messages/convo/agent.ts index 4e301b8420..14fb73f849 100644 --- a/src/state/messages/convo/agent.ts +++ b/src/state/messages/convo/agent.ts @@ -16,7 +16,6 @@ import { isNetworkError, } from '#/lib/strings/errors' import {Logger} from '#/logger' -import {isNative} from '#/platform/detection' import { ACTIVE_POLL_INTERVAL, BACKGROUND_POLL_INTERVAL, @@ -37,6 +36,7 @@ import { } from '#/state/messages/convo/types' import {type MessagesEventBus} from '#/state/messages/events/agent' import {type MessagesEventBusError} from '#/state/messages/events/types' +import {IS_NATIVE} from '#/env' const logger = Logger.create(Logger.Context.ConversationAgent) @@ -639,7 +639,7 @@ export class Convo { { cursor: nextCursor, convoId: this.convoId, - limit: isNative ? 30 : 60, + limit: IS_NATIVE ? 30 : 60, }, {headers: DM_SERVICE_HEADERS}, ) diff --git a/src/state/preferences/kawaii.tsx b/src/state/preferences/kawaii.tsx index 1835e03599..a2df07c33c 100644 --- a/src/state/preferences/kawaii.tsx +++ b/src/state/preferences/kawaii.tsx @@ -1,7 +1,7 @@ import React from 'react' -import {isWeb} from '#/platform/detection' import * as persisted from '#/state/persisted' +import {IS_WEB} from '#/env' type StateContext = persisted.Schema['kawaii'] @@ -30,7 +30,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) { React.useEffect(() => { // dumb and stupid but it's web only so just refresh the page if you want to change it - if (isWeb) { + if (IS_WEB) { const kawaii = new URLSearchParams(window.location.search).get('kawaii') switch (kawaii) { case 'true': diff --git a/src/state/queries/usePostThread/index.ts b/src/state/queries/usePostThread/index.ts index 4b318a356e..ceb87fe47b 100644 --- a/src/state/queries/usePostThread/index.ts +++ b/src/state/queries/usePostThread/index.ts @@ -1,7 +1,6 @@ import {useCallback, useMemo, useState} from 'react' import {useQuery, useQueryClient} from '@tanstack/react-query' -import {isWeb} from '#/platform/detection' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useThreadPreferences} from '#/state/queries/preferences/useThreadPreferences' import { @@ -31,6 +30,7 @@ import * as views from '#/state/queries/usePostThread/views' import {useAgent, useSession} from '#/state/session' import {useMergeThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' import {useBreakpoints} from '#/alf' +import {IS_WEB} from '#/env' export * from '#/state/queries/usePostThread/context' export {useUpdatePostThreadThreadgateQueryCache} from '#/state/queries/usePostThread/queryCache' @@ -53,7 +53,7 @@ export function usePostThread({anchor}: {anchor?: string}) { const below = useMemo(() => { return view === 'linear' ? LINEAR_VIEW_BELOW - : isWeb && gtPhone + : IS_WEB && gtPhone ? TREE_VIEW_BELOW_DESKTOP : TREE_VIEW_BELOW }, [view, gtPhone]) diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx index 4d4cb67282..88f2df4b8b 100644 --- a/src/state/session/index.tsx +++ b/src/state/session/index.tsx @@ -1,10 +1,10 @@ import React from 'react' import {type AtpSessionEvent, type BskyAgent} from '@atproto/api' -import {isWeb} from '#/platform/detection' import * as persisted from '#/state/persisted' import {useCloseAllActiveElements} from '#/state/util' import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' +import {IS_WEB} from '#/env' import {emitSessionDropped} from '../events' import { agentToSessionAccount, @@ -340,7 +340,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) { ) // @ts-expect-error window type is not declared, debug only - if (__DEV__ && isWeb) window.agent = state.currentAgentState.agent + if (__DEV__ && IS_WEB) window.agent = state.currentAgentState.agent const agent = state.currentAgentState.agent as BskyAppAgent const currentAgentRef = React.useRef(agent) diff --git a/src/state/shell/logged-out.tsx b/src/state/shell/logged-out.tsx index bda5251ffd..384507a34f 100644 --- a/src/state/shell/logged-out.tsx +++ b/src/state/shell/logged-out.tsx @@ -1,8 +1,8 @@ import React from 'react' -import {isWeb} from '#/platform/detection' import {useSession} from '#/state/session' import {useActiveStarterPack} from '#/state/shell/starter-pack' +import {IS_WEB} from '#/env' type State = { showLoggedOut: boolean @@ -55,7 +55,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) { const [state, setState] = React.useState({ showLoggedOut: shouldShowStarterPack, requestedAccountSwitchTo: shouldShowStarterPack - ? isWeb + ? IS_WEB ? 'starterpack' : 'new' : undefined, diff --git a/src/state/shell/selected-feed.tsx b/src/state/shell/selected-feed.tsx index 460adcd055..ef7aeb15ea 100644 --- a/src/state/shell/selected-feed.tsx +++ b/src/state/shell/selected-feed.tsx @@ -1,8 +1,8 @@ import {createContext, useCallback, useContext, useState} from 'react' -import {isWeb} from '#/platform/detection' import {type FeedDescriptor} from '#/state/queries/post-feed' import {useSession} from '#/state/session' +import {IS_WEB} from '#/env' import {account} from '#/storage' type StateContext = FeedDescriptor | null @@ -14,7 +14,7 @@ const setContext = createContext((_: string) => {}) setContext.displayName = 'SelectedFeedSetContext' function getInitialFeed(did?: string): FeedDescriptor | null { - if (isWeb) { + if (IS_WEB) { if (window.location.pathname === '/') { const params = new URLSearchParams(window.location.search) const feedFromUrl = params.get('feed') @@ -49,7 +49,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) { const saveState = useCallback( (feed: FeedDescriptor) => { setState(feed) - if (isWeb) { + if (IS_WEB) { try { sessionStorage.setItem('lastSelectedHomeFeed', feed) } catch {} diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx index 22dd23d7f7..d9185d7789 100644 --- a/src/view/com/auth/SplashScreen.web.tsx +++ b/src/view/com/auth/SplashScreen.web.tsx @@ -31,7 +31,7 @@ export const SplashScreen = ({ }) => { const {_} = useLingui() const t = useTheme() - const {isTabletOrMobile: isMobileWeb} = useWebMediaQueries() + const {isTabletOrMobile: IS_WEB_MOBILE} = useWebMediaQueries() const [showClipOverlay, setShowClipOverlay] = React.useState(false) React.useEffect(() => { @@ -78,7 +78,7 @@ export const SplashScreen = ({ a.justify_center, // @ts-expect-error web only {paddingBottom: '20vh'}, - isMobileWeb && a.pb_5xl, + IS_WEB_MOBILE && a.pb_5xl, t.atoms.border_contrast_medium, a.align_center, a.gap_5xl, diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 2b2eaed64e..892c07d4a4 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -76,7 +76,6 @@ import {logEvent} from '#/lib/statsig/statsig' import {cleanError} from '#/lib/strings/errors' import {colors} from '#/lib/styles' import {logger} from '#/logger' -import {isAndroid, isIOS, isNative, isWeb} from '#/platform/detection' import {useDialogStateControlContext} from '#/state/dialogs' import {emitPostCreated} from '#/state/events' import { @@ -130,6 +129,7 @@ import {LazyQuoteEmbed} from '#/components/Post/Embed/LazyQuoteEmbed' import * as Prompt from '#/components/Prompt' import * as Toast from '#/components/Toast' import {Text as NewText} from '#/components/Typography' +import {IS_ANDROID, IS_IOS, IS_NATIVE, IS_WEB} from '#/env' import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet' import {PostLanguageSelect} from './select-language/PostLanguageSelect' import { @@ -329,14 +329,14 @@ export const ComposePost = ({ const insets = useSafeAreaInsets() const viewStyles = useMemo( () => ({ - paddingTop: isAndroid ? insets.top : 0, + paddingTop: IS_ANDROID ? insets.top : 0, paddingBottom: // iOS - when keyboard is closed, keep the bottom bar in the safe area - (isIOS && !isKeyboardVisible) || + (IS_IOS && !isKeyboardVisible) || // Android - Android >=35 KeyboardAvoidingView adds double padding when // keyboard is closed, so we subtract that in the offset and add it back // here when the keyboard is open - (isAndroid && isKeyboardVisible) + (IS_ANDROID && isKeyboardVisible) ? insets.bottom : 0, }), @@ -366,7 +366,7 @@ export const ComposePost = ({ // On Android, pressing Back should ask confirmation. useEffect(() => { - if (!isAndroid) { + if (!IS_ANDROID) { return } const backHandler = BackHandler.addEventListener( @@ -671,7 +671,7 @@ export const ComposePost = ({ composerState.mutableNeedsFocusActive = false // On Android, this risks getting the cursor stuck behind the keyboard. // Not worth it. - if (!isAndroid) { + if (!IS_ANDROID) { textInput.current?.focus() } } @@ -727,12 +727,12 @@ export const ComposePost = ({ ) - const isWebFooterSticky = !isNative && thread.posts.length > 1 + const IS_WEBFooterSticky = !IS_NATIVE && thread.posts.length > 1 return ( - {isWebFooterSticky && post.id === activePost.id && ( + {IS_WEBFooterSticky && post.id === activePost.id && ( {footer} )} ))} - {!isWebFooterSticky && footer} + {!IS_WEBFooterSticky && footer} { if ( uri.startsWith('data:video/') || - (isWeb && uri.startsWith('data:image/gif')) + (IS_WEB && uri.startsWith('data:image/gif')) ) { - if (isNative) return // web only + if (IS_NATIVE) return // web only const [mimeType] = uri.slice('data:'.length).split(';') if (!SUPPORTED_MIME_TYPES.includes(mimeType as SupportedMimeTypes)) { Toast.show(_(msg`Unsupported video type: ${mimeType}`), { @@ -921,9 +921,9 @@ let ComposerPost = React.memo(function ComposerPost({ a.mb_sm, !isActive && isLastPost && a.mb_lg, !isActive && styles.inactivePost, - isTextOnly && isNative && a.flex_grow, + isTextOnly && IS_NATIVE && a.flex_grow, ]}> - + {embed.quote?.uri ? ( + style={[a.pb_sm, video ? [a.pt_md] : [a.pt_xl], IS_WEB && [a.pb_md]]}> @@ -1652,7 +1652,7 @@ function useKeyboardVerticalOffset() { const {top, bottom} = useSafeAreaInsets() // Android etc - if (!isIOS) { + if (!IS_IOS) { // need to account for the edge-to-edge nav bar return bottom * -1 } @@ -1696,7 +1696,7 @@ function useHideKeyboardOnBackground() { const appState = useAppState() useEffect(() => { - if (isIOS) { + if (IS_IOS) { if (appState === 'inactive') { Keyboard.dismiss() } @@ -1846,7 +1846,7 @@ function ToolbarWrapper({ style: StyleProp children: React.ReactNode }) { - if (isWeb) return children + if (IS_WEB) return children return ( {/* Maybe fix this later -h */} - {isAndroid ? : null} + {IS_ANDROID ? : null} ) } diff --git a/src/view/com/composer/KeyboardAccessory.tsx b/src/view/com/composer/KeyboardAccessory.tsx index f3fb761dc3..8f4a0dc87a 100644 --- a/src/view/com/composer/KeyboardAccessory.tsx +++ b/src/view/com/composer/KeyboardAccessory.tsx @@ -3,8 +3,8 @@ import {KeyboardStickyView} from 'react-native-keyboard-controller' import {useSafeAreaInsets} from 'react-native-safe-area-context' import type React from 'react' -import {isWeb} from '#/platform/detection' import {atoms as a, useTheme} from '#/alf' +import {IS_WEB} from '#/env' export function KeyboardAccessory({children}: {children: React.ReactNode}) { const t = useTheme() @@ -22,7 +22,7 @@ export function KeyboardAccessory({children}: {children: React.ReactNode}) { ] // todo: when iPad support is added, it should also not use the KeyboardStickyView - if (isWeb) { + if (IS_WEB) { return {children} } diff --git a/src/view/com/composer/SelectMediaButton.tsx b/src/view/com/composer/SelectMediaButton.tsx index 946ece5b9f..b6440685ef 100644 --- a/src/view/com/composer/SelectMediaButton.tsx +++ b/src/view/com/composer/SelectMediaButton.tsx @@ -11,13 +11,13 @@ import { } from '#/lib/hooks/usePermissions' import {openUnifiedPicker} from '#/lib/media/picker' import {extractDataUriMime} from '#/lib/media/util' -import {isNative, isWeb} from '#/platform/detection' import {MAX_IMAGES} from '#/view/com/composer/state/composer' import {atoms as a, useTheme} from '#/alf' import {Button} from '#/components/Button' import {useSheetWrapper} from '#/components/Dialog/sheet-wrapper' import {Image_Stroke2_Corner0_Rounded as ImageIcon} from '#/components/icons/Image' import * as toast from '#/components/Toast' +import {IS_NATIVE, IS_WEB} from '#/env' export type SelectMediaButtonProps = { disabled?: boolean @@ -91,7 +91,7 @@ const SUPPORTED_IMAGE_MIME_TYPES = ( 'image/svg+xml', 'image/webp', 'image/avif', - isNative && 'image/heic', + IS_NATIVE && 'image/heic', ] as const ).filter(Boolean) type SupportedImageMimeType = Exclude< @@ -261,7 +261,7 @@ async function processImagePickerAssets( * We don't care too much about mimeType at this point on native, * since the `processVideo` step later on will convert to `.mp4`. */ - if (isWeb && !isSupportedVideoMimeType(mimeType)) { + if (IS_WEB && !isSupportedVideoMimeType(mimeType)) { errors.add(SelectedAssetError.Unsupported) continue } @@ -271,7 +271,7 @@ async function processImagePickerAssets( * to filter out large files on web. On native, we compress these anyway, * so we only check on web. */ - if (isWeb && asset.fileSize && asset.fileSize > VIDEO_MAX_SIZE) { + if (IS_WEB && asset.fileSize && asset.fileSize > VIDEO_MAX_SIZE) { errors.add(SelectedAssetError.FileTooBig) continue } @@ -290,7 +290,7 @@ async function processImagePickerAssets( * to filter out large files on web. On native, we compress GIFs as * videos anyway, so we only check on web. */ - if (isWeb && asset.fileSize && asset.fileSize > VIDEO_MAX_SIZE) { + if (IS_WEB && asset.fileSize && asset.fileSize > VIDEO_MAX_SIZE) { errors.add(SelectedAssetError.FileTooBig) continue } @@ -308,7 +308,7 @@ async function processImagePickerAssets( * base64 data-uri, so we construct it here for web only. */ uri: - isWeb && asset.base64 + IS_WEB && asset.base64 ? `data:${mimeType};base64,${asset.base64}` : asset.uri, }) @@ -327,7 +327,7 @@ async function processImagePickerAssets( } if (supportedAssets[0].duration) { - if (isWeb) { + if (IS_WEB) { /* * Web reports duration as seconds */ @@ -432,7 +432,7 @@ export function SelectMediaButton({ ) const onPressSelectMedia = useCallback(async () => { - if (isNative) { + if (IS_NATIVE) { const [photoAccess, videoAccess] = await Promise.all([ requestPhotoAccessIfNeeded(), requestVideoAccessIfNeeded(), @@ -446,7 +446,7 @@ export function SelectMediaButton({ } } - if (isNative && Keyboard.isVisible()) { + if (IS_NATIVE && Keyboard.isVisible()) { Keyboard.dismiss() } diff --git a/src/view/com/composer/labels/LabelsBtn.tsx b/src/view/com/composer/labels/LabelsBtn.tsx index 95a93490cd..6d897933b0 100644 --- a/src/view/com/composer/labels/LabelsBtn.tsx +++ b/src/view/com/composer/labels/LabelsBtn.tsx @@ -9,7 +9,6 @@ import { type OtherSelfLabel, type SelfLabel, } from '#/lib/moderation' -import {isWeb} from '#/platform/detection' import {atoms as a, useTheme, web} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' @@ -18,6 +17,7 @@ import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' import {TinyChevronBottom_Stroke2_Corner0_Rounded as TinyChevronIcon} from '#/components/icons/Chevron' import {Shield_Stroke2_Corner0_Rounded} from '#/components/icons/Shield' import {Text} from '#/components/Typography' +import {IS_WEB} from '#/env' export function LabelsBtn({ labels, @@ -218,7 +218,7 @@ function DialogInner({ label={_(msg`Done`)} onPress={() => control.close()} color="primary" - size={isWeb ? 'small' : 'large'} + size={IS_WEB ? 'small' : 'large'} variant="solid" testID="confirmBtn"> diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx index a1c6660d4b..4b165d92d7 100644 --- a/src/view/com/composer/photos/Gallery.tsx +++ b/src/view/com/composer/photos/Gallery.tsx @@ -16,12 +16,12 @@ import {useLingui} from '@lingui/react' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {type Dimensions} from '#/lib/media/types' import {colors, s} from '#/lib/styles' -import {isNative} from '#/platform/detection' import {type ComposerImage, cropImage} from '#/state/gallery' import {Text} from '#/view/com/util/text/Text' import {tokens, useTheme} from '#/alf' import * as Dialog from '#/components/Dialog' import {MediaInsetBorder} from '#/components/MediaInsetBorder' +import {IS_NATIVE} from '#/env' import {type PostAction} from '../state/composer' import {EditImageDialog} from './EditImageDialog' import {ImageAltTextDialog} from './ImageAltTextDialog' @@ -145,7 +145,7 @@ const GalleryItem = ({ const editControl = Dialog.useDialogControl() const onImageEdit = () => { - if (isNative) { + if (IS_NATIVE) { cropImage(image).then(next => { onChange(next) }) diff --git a/src/view/com/composer/photos/ImageAltTextDialog.tsx b/src/view/com/composer/photos/ImageAltTextDialog.tsx index 405b7a87ca..1315eab8c0 100644 --- a/src/view/com/composer/photos/ImageAltTextDialog.tsx +++ b/src/view/com/composer/photos/ImageAltTextDialog.tsx @@ -6,7 +6,6 @@ import {useLingui} from '@lingui/react' import {MAX_ALT_TEXT} from '#/lib/constants' import {enforceLen} from '#/lib/strings/helpers' -import {isAndroid, isWeb} from '#/platform/detection' import {type ComposerImage} from '#/state/gallery' import {AltTextCounterWrapper} from '#/view/com/composer/AltTextCounterWrapper' import {atoms as a, useTheme} from '#/alf' @@ -16,6 +15,7 @@ import {type DialogControlProps} from '#/components/Dialog' import * as TextField from '#/components/forms/TextField' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {Text} from '#/components/Typography' +import {IS_ANDROID, IS_WEB} from '#/env' type Props = { control: Dialog.DialogOuterProps['control'] @@ -66,7 +66,7 @@ const ImageAltTextInner = ({ const windim = useWindowDimensions() const imageStyle = React.useMemo(() => { - const maxWidth = isWeb ? 450 : windim.width + const maxWidth = IS_WEB ? 450 : windim.width const source = image.transformed ?? image.source if (source.height > source.width) { @@ -165,7 +165,7 @@ const ImageAltTextInner = ({ {/* Maybe fix this later -h */} - {isAndroid ? : null} + {IS_ANDROID ? : null} ) } diff --git a/src/view/com/composer/photos/OpenCameraBtn.tsx b/src/view/com/composer/photos/OpenCameraBtn.tsx index 8bd1aa27b0..f917e4ee2e 100644 --- a/src/view/com/composer/photos/OpenCameraBtn.tsx +++ b/src/view/com/composer/photos/OpenCameraBtn.tsx @@ -7,11 +7,11 @@ import {POST_IMG_MAX} from '#/lib/constants' import {useCameraPermission} from '#/lib/hooks/usePermissions' import {openCamera} from '#/lib/media/picker' import {logger} from '#/logger' -import {isMobileWeb, isNative} from '#/platform/detection' import {type ComposerImage, createComposerImage} from '#/state/gallery' import {atoms as a, useTheme} from '#/alf' import {Button} from '#/components/Button' import {Camera_Stroke2_Corner0_Rounded as Camera} from '#/components/icons/Camera' +import {IS_NATIVE, IS_WEB_MOBILE} from '#/env' type Props = { disabled?: boolean @@ -58,7 +58,7 @@ export function OpenCameraBtn({disabled, onAdd}: Props) { requestMediaPermission, ]) - const shouldShowCameraButton = isNative || isMobileWeb + const shouldShowCameraButton = IS_NATIVE || IS_WEB_MOBILE if (!shouldShowCameraButton) { return null } diff --git a/src/view/com/composer/select-language/PostLanguageSelectDialog.tsx b/src/view/com/composer/select-language/PostLanguageSelectDialog.tsx index 36d4b1ff94..610cf08d70 100644 --- a/src/view/com/composer/select-language/PostLanguageSelectDialog.tsx +++ b/src/view/com/composer/select-language/PostLanguageSelectDialog.tsx @@ -6,7 +6,6 @@ import {useLingui} from '@lingui/react' import {languageName} from '#/locale/helpers' import {type Language, LANGUAGES, LANGUAGES_MAP_CODE2} from '#/locale/languages' -import {isNative, isWeb} from '#/platform/detection' import { toPostLanguages, useLanguagePrefs, @@ -21,6 +20,7 @@ import {SearchInput} from '#/components/forms/SearchInput' import * as Toggle from '#/components/forms/Toggle' import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times' import {Text} from '#/components/Typography' +import {IS_NATIVE, IS_WEB} from '#/env' export function PostLanguageSelectDialog({ control, @@ -168,7 +168,7 @@ export function DialogInner({ const listHeader = ( setHeaderHeight(evt.nativeEvent.layout.height)}> @@ -195,7 +195,7 @@ export function DialogInner({ - {isWeb && ( + {IS_WEB && ( @@ -134,7 +138,7 @@ function SubtitleDialogInner({ )} - {isWeb && ( + {IS_WEB && ( <>