Move /platform/detection vars into /env (#9707)

* Add platform vars to env

* Replace /platform/detection with /env
This commit is contained in:
Eric Bailey
2026-01-16 16:28:17 -06:00
committed by GitHub
parent bd510d8468
commit 0589bd7d9e
244 changed files with 789 additions and 789 deletions
+2 -2
View File
@@ -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
}
```
@@ -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 (
<NativeView
+3 -3
View File
@@ -23,7 +23,6 @@ import {s} from '#/lib/styles'
import {ThemeProvider} from '#/lib/ThemeContext'
import I18nProvider from '#/locale/i18nProvider'
import {logger} from '#/logger'
import {isAndroid, isIOS} from '#/platform/detection'
import {Provider as A11yProvider} from '#/state/a11y'
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
import {Provider as DialogStateProvider} from '#/state/dialogs'
@@ -69,6 +68,7 @@ import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbe
import {ToastOutlet} from '#/components/Toast'
import {Provider as AgeAssuranceV2Provider} from '#/ageAssurance'
import {prefetchAgeAssuranceConfig} from '#/ageAssurance'
import {IS_ANDROID, IS_IOS} from '#/env'
import {
prefetchLiveEvents,
Provider as LiveEventsProvider,
@@ -79,10 +79,10 @@ import {BottomSheetProvider} from '../modules/bottom-sheet'
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
SplashScreen.preventAutoHideAsync()
if (isIOS) {
if (IS_IOS) {
SystemUI.setBackgroundColorAsync('black')
}
if (isAndroid) {
if (IS_ANDROID) {
// iOS is handled by the config plugin -sfn
ScreenOrientation.lockAsync(
ScreenOrientation.OrientationLock.PORTRAIT_UP,
+6 -6
View File
@@ -44,7 +44,6 @@ import {type RouteParams, type State} from '#/lib/routes/types'
import {attachRouteToLogEvents, logEvent} from '#/lib/statsig/statsig'
import {bskyTitle} from '#/lib/strings/headings'
import {logger} from '#/logger'
import {isNative, isWeb} from '#/platform/detection'
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
import {useSession} from '#/state/session'
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
@@ -138,6 +137,7 @@ import {
EmailDialogScreenID,
useEmailDialogControl,
} from '#/components/dialogs/EmailDialog'
import {IS_NATIVE, IS_WEB} from '#/env'
import {router} from '#/routes'
import {Referrer} from '../modules/expo-bluesky-swiss-army'
@@ -842,11 +842,11 @@ const LINKING = {
// native, since the home tab and the home screen are defined as initial routes, we don't need to return a state
// since it will be created by react-navigation.
if (path.includes('intent/')) {
if (isNative) return
if (IS_NATIVE) return
return buildStateObject('Flat', 'Home', params)
}
if (isNative) {
if (IS_NATIVE) {
if (name === 'Search') {
return buildStateObject('SearchTab', 'Search', params)
}
@@ -921,7 +921,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
)
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<void> {
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', {
@@ -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() {
)}
<View style={[a.gap_xs]}>
{isNative && (
{IS_NATIVE && (
<>
<Admonition>
<Trans>
@@ -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,
+4 -4
View File
@@ -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
}
+4 -4
View File
@@ -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<TextProps, 'children'> = {},
emoji: boolean,
) {
if (!isIOS || !emoji) {
if (!IS_IOS || !emoji) {
return children
}
return Children.map(children, child => {
+2 -2
View File
@@ -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)
+2 -2
View File
@@ -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
+5 -5
View File
@@ -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) {
<GestureDetector gesture={composedGestures}>
<View ref={ref} style={[{opacity: context.isOpen ? 0 : 1}, style]}>
{children({
isNative: true,
IS_NATIVE: true,
control: {isOpen: context.isOpen, open},
state: {
pressed: false,
+2 -2
View File
@@ -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
+1 -1
View File
@@ -18,7 +18,7 @@ import {BottomSheetSnapPoint} from '../../../modules/bottom-sheet/src/BottomShee
export const Context = createContext<DialogContextProps>({
close: () => {},
isNativeDialog: false,
IS_NATIVEDialog: false,
nativeSnapPoint: BottomSheetSnapPoint.Hidden,
disableDrag: false,
setDisableDrag: () => {},
+11 -11
View File
@@ -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<ScrollView, DialogInnerProps>(
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<ScrollView, DialogInnerProps>(
)
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<ScrollView, DialogInnerProps>(
}
const onScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
if (!isAndroid) {
if (!IS_ANDROID) {
return
}
const {contentOffset} = e.nativeEvent
@@ -260,12 +260,12 @@ export const ScrollableInner = React.forwardRef<ScrollView, DialogInnerProps>(
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={<View style={{height: insets.bottom + 100}} />}
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)}],
}
+1 -1
View File
@@ -98,7 +98,7 @@ export function Outer({
const context = React.useMemo(
() => ({
close,
isNativeDialog: false,
IS_NATIVEDialog: false,
nativeSnapPoint: 0,
disableDrag: false,
setDisableDrag: () => {},
+2 -2
View File
@@ -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 <T>(promise: Promise<T>): Promise<T> => {
if (isIOS) {
if (IS_IOS) {
const entry = SystemBars.pushStackEntry({
style: {
statusBar: 'light',
+1 -1
View File
@@ -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<React.SetStateAction<boolean>>
+3 -3
View File
@@ -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'}>
<View
style={[
a.px_lg,
@@ -701,7 +701,7 @@ export function ProfileGrid({
a.align_center,
a.justify_between,
]}
pointerEvents={isIOS ? 'auto' : 'box-none'}>
pointerEvents={IS_IOS ? 'auto' : 'box-none'}>
<Text style={[a.text_sm, a.font_semi_bold, t.atoms.text]}>
{isFeedContext ? (
<Trans>Suggested for you</Trans>
+3 -3
View File
@@ -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({
)
})}
</DraggableScrollView>
{isWeb && canScrollLeft && (
{IS_WEB && canScrollLeft && (
<View
style={[
a.absolute,
@@ -270,7 +270,7 @@ export function InterestTabs({
</Button>
</View>
)}
{isWeb && canScrollRight && (
{IS_WEB && canScrollRight && (
<View
style={[
a.absolute,
@@ -10,9 +10,9 @@ import {
INTERNATIONAL_TELEPHONE_CODES,
} from '#/lib/international-telephone-codes'
import {regionName} from '#/locale/helpers'
import {isWeb} from '#/platform/detection'
import {atoms as a, web} from '#/alf'
import * as Select from '#/components/Select'
import {IS_WEB} from '#/env'
import {useGeolocation} from '#/geolocation'
/**
@@ -84,7 +84,7 @@ export function InternationalPhoneCodeSelect({
<Select.Item value={item.value} label={item.label}>
<Select.ItemIndicator />
<Select.ItemText style={[a.flex_1]} emoji>
{isWeb ? <Flag {...item} /> : item.unicodeFlag + ' '}
{IS_WEB ? <Flag {...item} /> : item.unicodeFlag + ' '}
{item.name}
</Select.ItemText>
<Select.ItemText style={[a.text_right]}>
@@ -101,7 +101,7 @@ export function InternationalPhoneCodeSelect({
}
function Flag({unicodeFlag, svgFlag}: {unicodeFlag: string; svgFlag: any}) {
if (isWeb) {
if (IS_WEB) {
return (
<Image
source={svgFlag}
+4 -4
View File
@@ -6,7 +6,6 @@ import {useNavigation} from '@react-navigation/native'
import {HITSLOP_30} from '#/lib/constants'
import {type NavigationProp} from '#/lib/routes/types'
import {isIOS} from '#/platform/detection'
import {useSetDrawerOpen} from '#/state/shell'
import {
atoms as a,
@@ -29,6 +28,7 @@ import {
} from '#/components/Layout/const'
import {ScrollbarOffsetContext} from '#/components/Layout/context'
import {Text} from '#/components/Typography'
import {IS_IOS} from '#/env'
export function Outer({
children,
@@ -91,7 +91,7 @@ export function Content({
style={[
a.flex_1,
a.justify_center,
isIOS && align === 'platform' && a.align_center,
IS_IOS && align === 'platform' && a.align_center,
{minHeight: HEADER_SLOT_SIZE},
]}>
<AlignmentContext.Provider value={align}>
@@ -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}>
+4 -4
View File
@@ -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 && <WebCenterBorders />}
{IS_WEB && <WebCenterBorders />}
<View
style={[a.util_screen_outer, {paddingTop: noInsetTop ? 0 : top}, style]}
{...props}
@@ -98,7 +98,7 @@ export const Content = memo(
contentContainerStyle,
]}
{...props}>
{isWeb ? (
{IS_WEB ? (
<Center ignoreTabletLayoutOffset={ignoreTabletLayoutOffset}>
{/* @ts-expect-error web only -esb */}
{children}
@@ -145,7 +145,7 @@ export const KeyboardAwareContent = memo(function LayoutKeyboardAwareContent({
]}
keyboardShouldPersistTaps="handled"
{...props}>
{isWeb ? <Center>{children}</Center> : children}
{IS_WEB ? <Center>{children}</Center> : children}
</KeyboardAwareScrollView>
)
})
+11 -11
View File
@@ -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<InlineLinkProps, 'onLongPress'>) {
return isWeb ? (
return IS_WEB ? (
<InlineLinkText {...props} to={to} onPress={onPress}>
{children}
</InlineLinkText>
@@ -547,7 +547,7 @@ export function createStaticClickIfUnmodified(
): {onPress: Exclude<BaseLinkProps['onPress'], undefined>} {
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
}
+5 -5
View File
@@ -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({
<Dialog.ScrollableInner label={_(msg`Menu`)}>
<View style={[a.gap_lg]}>
{children}
{isNative && showCancel && <Cancel />}
{IS_NATIVE && showCancel && <Cancel />}
</View>
</Dialog.ScrollableInner>
</Context.Provider>
@@ -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}
+1 -1
View File
@@ -138,7 +138,7 @@ export function Trigger({
<RadixTriggerPassThrough>
{props =>
children({
isNative: false,
IS_NATIVE: false,
control,
state: {
hovered,
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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({
</StarterPackCard.Link>
) : null}
{isNative && (
{IS_NATIVE && (
<Button
label={_(msg`Close`)}
color="secondary"
@@ -7,11 +7,11 @@ import {
import {LinearGradient} from 'expo-linear-gradient'
import {utils} from '@bsky.app/alf'
import {isAndroid, isNative} from '#/platform/detection'
import {useA11y} from '#/state/a11y'
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
import {FocusScope} from '#/components/FocusScope'
import {LockScroll} from '#/components/LockScroll'
import {IS_ANDROID, IS_NATIVE} from '#/env'
const GUTTER = 24
@@ -80,7 +80,7 @@ export function Overlay({
a.z_20,
a.align_center,
!gtPhone && [a.justify_end, {minHeight: frame.height}],
isNative && [
IS_NATIVE && [
{
paddingBottom: Math.max(insets.bottom, a.p_2xl.padding),
},
@@ -109,7 +109,7 @@ export function Overlay({
<FocusScope>
<View
accessible={isAndroid}
accessible={IS_ANDROID}
role="dialog"
aria-role="dialog"
aria-label={label}
+2 -2
View File
@@ -1,12 +1,12 @@
import {useEffect} from 'react'
import {View} from 'react-native'
import {isIOS} from '#/platform/detection'
import {atoms as a} from '#/alf'
import {FullWindowOverlay} from '#/components/FullWindowOverlay'
import {usePolicyUpdateContext} from '#/components/PolicyUpdateOverlay/context'
import {Portal} from '#/components/PolicyUpdateOverlay/Portal'
import {Content} from '#/components/PolicyUpdateOverlay/updates/202508'
import {IS_IOS} from '#/env'
export {Provider} from '#/components/PolicyUpdateOverlay/context'
export {usePolicyUpdateContext} from '#/components/PolicyUpdateOverlay/context'
@@ -39,7 +39,7 @@ export function PolicyUpdateOverlay() {
// 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},
]}>
<Content state={state} />
</View>
@@ -3,7 +3,6 @@ import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isAndroid} from '#/platform/detection'
import {useA11y} from '#/state/a11y'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
@@ -12,6 +11,7 @@ import {Badge} from '#/components/PolicyUpdateOverlay/Badge'
import {Overlay} from '#/components/PolicyUpdateOverlay/Overlay'
import {type PolicyUpdateState} from '#/components/PolicyUpdateOverlay/usePolicyUpdateState'
import {Text} from '#/components/Typography'
import {IS_ANDROID} from '#/env'
export function Content({state}: {state: PolicyUpdateState}) {
const t = useTheme()
@@ -56,7 +56,7 @@ export function Content({state}: {state: PolicyUpdateState}) {
size: 'small',
} as const
const label = isAndroid
const label = IS_ANDROID
? _(
msg`Were updating our Terms of Service, Privacy Policy, and Copyright Policy, effective September 15th, 2025. We're also updating our Community Guidelines, and we want your input! These new guidelines will take effect on October 15th, 2025. Learn more about these changes and how to share your thoughts with us by reading our blog post.`,
)
@@ -10,13 +10,13 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {type EmbedPlayerParams} from '#/lib/strings/embed-player'
import {isIOS, isNative, isWeb} from '#/platform/detection'
import {useExternalEmbedsPrefs} from '#/state/preferences'
import {atoms as a, useTheme} from '#/alf'
import {useDialogControl} from '#/components/Dialog'
import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent'
import {Fill} from '#/components/Fill'
import {PlayButtonIcon} from '#/components/video/PlayButtonIcon'
import {IS_IOS, IS_NATIVE, IS_WEB} from '#/env'
export function ExternalGif({
link,
@@ -66,12 +66,12 @@ export function ExternalGif({
// Control animation on native
setIsAnimating(prev => {
if (prev) {
if (isNative) {
if (IS_NATIVE) {
imageRef.current?.stopAnimating()
}
return false
} else {
if (isNative) {
if (IS_NATIVE) {
imageRef.current?.startAnimating()
}
return true
@@ -112,7 +112,7 @@ export function ExternalGif({
<Image
source={{
uri:
!isPrefetched || (isWeb && !isAnimating)
!isPrefetched || (IS_WEB && !isAnimating)
? link.thumb
: params.playerUri,
}} // Web uses the thumb to control playback
@@ -123,7 +123,7 @@ export function ExternalGif({
accessibilityIgnoresInvertColors
accessibilityLabel={link.title}
accessibilityHint={link.title}
cachePolicy={isIOS ? 'disk' : 'memory-disk'} // cant control playback with memory-disk on ios
cachePolicy={IS_IOS ? 'disk' : 'memory-disk'} // cant control playback with memory-disk on ios
/>
{(!isPrefetched || !isAnimating) && (
@@ -26,7 +26,6 @@ import {
type EmbedPlayerParams,
getPlayerAspect,
} from '#/lib/strings/embed-player'
import {isNative} from '#/platform/detection'
import {useExternalEmbedsPrefs} from '#/state/preferences'
import {EventStopper} from '#/view/com/util/EventStopper'
import {atoms as a, useTheme} from '#/alf'
@@ -34,6 +33,7 @@ import {useDialogControl} from '#/components/Dialog'
import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent'
import {Fill} from '#/components/Fill'
import {PlayButtonIcon} from '#/components/video/PlayButtonIcon'
import {IS_NATIVE} from '#/env'
interface ShouldStartLoadRequest {
url: string
@@ -148,7 +148,7 @@ export function ExternalPlayer({
const {height: winHeight, width: winWidth} = windowDims
// Get the proper screen height depending on what is going on
const realWinHeight = isNative // If it is native, we always want the larger number
const realWinHeight = IS_NATIVE // If it is native, we always want the larger number
? winHeight > winWidth
? winHeight
: winWidth
@@ -13,7 +13,6 @@ import {useLingui} from '@lingui/react'
import {HITSLOP_20} from '#/lib/constants'
import {clamp} from '#/lib/numbers'
import {type EmbedPlayerParams} from '#/lib/strings/embed-player'
import {isWeb} from '#/platform/detection'
import {useAutoplayDisabled} from '#/state/preferences'
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
import {atoms as a, useTheme} from '#/alf'
@@ -22,6 +21,7 @@ import {Loader} from '#/components/Loader'
import * as Prompt from '#/components/Prompt'
import {Text} from '#/components/Typography'
import {PlayButtonIcon} from '#/components/video/PlayButtonIcon'
import {IS_WEB} from '#/env'
import {GifView} from '../../../../../modules/expo-bluesky-gif-view'
import {type GifViewStateChangeEvent} from '../../../../../modules/expo-bluesky-gif-view/src/GifView.types'
@@ -218,18 +218,18 @@ const styles = StyleSheet.create({
altContainer: {
backgroundColor: 'rgba(0, 0, 0, 0.75)',
borderRadius: 6,
paddingHorizontal: isWeb ? 8 : 6,
paddingVertical: isWeb ? 6 : 3,
paddingHorizontal: IS_WEB ? 8 : 6,
paddingVertical: IS_WEB ? 6 : 3,
position: 'absolute',
// Related to margin/gap hack. This keeps the alt label in the same position
// on all platforms
right: isWeb ? 8 : 5,
bottom: isWeb ? 8 : 5,
right: IS_WEB ? 8 : 5,
bottom: IS_WEB ? 8 : 5,
zIndex: 2,
},
alt: {
color: 'white',
fontSize: isWeb ? 10 : 7,
fontSize: IS_WEB ? 10 : 7,
fontWeight: '600',
},
})
@@ -10,13 +10,13 @@ import {useHaptics} from '#/lib/haptics'
import {shareUrl} from '#/lib/sharing'
import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player'
import {toNiceDomain} from '#/lib/strings/url-helpers'
import {isNative} from '#/platform/detection'
import {useExternalEmbedsPrefs} from '#/state/preferences'
import {atoms as a, useTheme} from '#/alf'
import {Divider} from '#/components/Divider'
import {Earth_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe'
import {Link} from '#/components/Link'
import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
import {ExternalGif} from './ExternalGif'
import {ExternalPlayer} from './ExternalPlayer'
import {GifEmbed} from './Gif'
@@ -53,7 +53,7 @@ export const ExternalEmbed = ({
}, [playHaptic, onOpen])
const onShareExternal = useCallback(() => {
if (link.uri && isNative) {
if (link.uri && IS_NATIVE) {
playHaptic('Heavy')
shareUrl(link.uri)
}
@@ -8,7 +8,7 @@ import React, {
} from 'react'
import {useWindowDimensions} from 'react-native'
import {isNative, isWeb} from '#/platform/detection'
import {IS_NATIVE, IS_WEB} from '#/env'
const Context = React.createContext<{
activeViewId: string | null
@@ -18,7 +18,7 @@ const Context = React.createContext<{
Context.displayName = 'ActiveVideoWebContext'
export function Provider({children}: {children: React.ReactNode}) {
if (!isWeb) {
if (!IS_WEB) {
throw new Error('ActiveVideoWebContext may only be used on web.')
}
@@ -47,7 +47,7 @@ export function Provider({children}: {children: React.ReactNode}) {
const sendViewPosition = useCallback(
(viewId: string, y: number) => {
if (isNative) return
if (IS_NATIVE) return
if (viewId === activeViewIdRef.current) {
activeViewLocationRef.current = y
@@ -6,7 +6,6 @@ import type Hls from 'hls.js'
import {isTouchDevice} from '#/lib/browser'
import {clamp} from '#/lib/numbers'
import {isIPhoneWeb} from '#/platform/detection'
import {
useAutoplayDisabled,
useSetSubtitlesEnabled,
@@ -28,6 +27,7 @@ import {Pause_Filled_Corner0_Rounded as PauseIcon} from '#/components/icons/Paus
import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
import {IS_WEB_MOBILE_IOS} from '#/env'
import {TimeIndicator} from '../TimeIndicator'
import {ControlButton} from './ControlButton'
import {Scrubber} from './Scrubber'
@@ -400,7 +400,7 @@ export function Controls({
onEndHover={onVolumeEndHover}
drawFocus={drawFocus}
/>
{!isIPhoneWeb && (
{!IS_WEB_MOBILE_IOS && (
<ControlButton
active={isFullscreen}
activeLabel={_(msg`Exit fullscreen`)}
@@ -10,7 +10,6 @@ import {type NavigationProp} from '#/lib/routes/types'
import {shareText, shareUrl} from '#/lib/sharing'
import {toShareUrl} from '#/lib/strings/url-helpers'
import {logger} from '#/logger'
import {isIOS} from '#/platform/detection'
import {useProfileShadow} from '#/state/cache/profile-shadow'
import {useSession} from '#/state/session'
import * as Toast from '#/view/com/util/Toast'
@@ -24,6 +23,7 @@ import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/i
import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlaneIcon} from '#/components/icons/PaperPlane'
import * as Menu from '#/components/Menu'
import {useAgeAssurance} from '#/ageAssurance'
import {IS_IOS} from '#/env'
import {useDevMode} from '#/storage/hooks/dev-mode'
import {RecentChats} from './RecentChats'
import {type ShareMenuItemsProps} from './ShareMenuItems.types'
@@ -63,7 +63,7 @@ let ShareMenuItems = ({
const onCopyLink = async () => {
logger.metric('share:press:copyLink', {}, {statsig: true})
const url = toShareUrl(href)
if (isIOS) {
if (IS_IOS) {
// iOS only
await ExpoClipboard.setUrlAsync(url)
} else {
@@ -9,7 +9,6 @@ import {type NavigationProp} from '#/lib/routes/types'
import {shareText, shareUrl} from '#/lib/sharing'
import {toShareUrl} from '#/lib/strings/url-helpers'
import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {useProfileShadow} from '#/state/cache/profile-shadow'
import {useSession} from '#/state/session'
import {useBreakpoints} from '#/alf'
@@ -22,6 +21,7 @@ import {CodeBrackets_Stroke2_Corner0_Rounded as CodeBracketsIcon} from '#/compon
import {PaperPlane_Stroke2_Corner0_Rounded as Send} from '#/components/icons/PaperPlane'
import * as Menu from '#/components/Menu'
import {useAgeAssurance} from '#/ageAssurance'
import {IS_WEB} from '#/env'
import {useDevMode} from '#/storage/hooks/dev-mode'
import {type ShareMenuItemsProps} from './ShareMenuItems.types'
@@ -70,7 +70,7 @@ let ShareMenuItems = ({
})
}
const canEmbed = isWeb && gtMobile && !hideInPWI
const canEmbed = IS_WEB && gtMobile && !hideInPWI
const onShareATURI = () => {
shareText(postUri)
@@ -12,7 +12,6 @@ import {useLingui} from '@lingui/react'
import {popularInterests, useInterestsDisplayNames} from '#/lib/interests'
import {logEvent} from '#/lib/statsig/statsig'
import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useActorSearch} from '#/state/queries/actor-search'
import {usePreferencesQuery} from '#/state/queries/preferences'
@@ -37,6 +36,7 @@ import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import {boostInterests, InterestTabs} from '#/components/InterestTabs'
import * as ProfileCard from '#/components/ProfileCard'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import type * as bsky from '#/types/bsky'
import {ProgressGuideTask} from './Task'
@@ -431,7 +431,7 @@ function HeaderTop({guide}: {guide?: Follow10ProgressGuide}) {
<Trans>Find people to follow</Trans>
</Text>
{guide && (
<View style={isWeb && {paddingRight: 36}}>
<View style={IS_WEB && {paddingRight: 36}}>
<ProgressGuideTask
current={guide.numFollows + 1}
total={10 + 1}
@@ -440,12 +440,12 @@ function HeaderTop({guide}: {guide?: Follow10ProgressGuide}) {
/>
</View>
)}
{isWeb ? (
{IS_WEB ? (
<Button
label={_(msg`Close`)}
size="small"
shape="round"
variant={isWeb ? 'ghost' : 'solid'}
variant={IS_WEB ? 'ghost' : 'solid'}
color="secondary"
style={[
a.absolute,
@@ -579,7 +579,7 @@ function FollowProfileCardInner({
<ProfileCard.Outer>
<ProfileCard.Header>
<ProfileCard.Avatar
disabledPreview={!isWeb}
disabledPreview={!IS_WEB}
profile={profile}
moderationOpts={moderationOpts}
/>
+3 -3
View File
@@ -11,9 +11,9 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isWeb} from '#/platform/detection'
import {atoms as a, useTheme} from '#/alf'
import {Portal} from '#/components/Portal'
import {IS_WEB} from '#/env'
import {AnimatedCheck, type AnimatedCheckRef} from '../anim/AnimatedCheck'
import {Text} from '../Typography'
@@ -108,11 +108,11 @@ export const ProgressGuideToast = React.forwardRef<
const containerStyle = React.useMemo(() => {
let left = 10
let right = 10
if (isWeb && winDim.width > 400) {
if (IS_WEB && winDim.width > 400) {
left = right = (winDim.width - 380) / 2
}
return {
position: isWeb ? 'fixed' : 'absolute',
position: IS_WEB ? 'fixed' : 'absolute',
top: 0,
left,
right,
+5 -5
View File
@@ -6,7 +6,6 @@ import {useNavigation} from '@react-navigation/native'
import {type NavigationProp} from '#/lib/routes/types'
import {isInvalidHandle} from '#/lib/strings/handles'
import {isNative, isWeb} from '#/platform/detection'
import {
usePreferencesQuery,
useRemoveMutedWordsMutation,
@@ -22,6 +21,7 @@ import {
} from '#/components/Link'
import {Loader} from '#/components/Loader'
import * as Menu from '#/components/Menu'
import {IS_NATIVE, IS_WEB} from '#/env'
export function RichTextTag({
tag,
@@ -50,7 +50,7 @@ export function RichTextTag({
const navigation = useNavigation<NavigationProp>()
const isCashtag = tag.startsWith('$')
const label = isCashtag ? _(msg`Cashtag ${tag}`) : _(msg`Hashtag ${tag}`)
const hint = isNative
const hint = IS_NATIVE
? _(msg`Long press to open tag menu for ${isCashtag ? tag : `#${tag}`}`)
: _(msg`Click to open tag menu for ${isCashtag ? tag : `#${tag}`}`)
@@ -86,9 +86,9 @@ export function RichTextTag({
}}
{...menuProps}
onPress={e => {
if (isWeb) {
if (IS_WEB) {
return createStaticClickIfUnmodified(() => {
if (!isNative) {
if (!IS_NATIVE) {
menuProps.onPress()
}
}).onPress(e)
@@ -99,7 +99,7 @@ export function RichTextTag({
label={label}
style={textStyle}
emoji>
{isNative ? (
{IS_NATIVE ? (
display
) : (
<RNText ref={menuProps.ref}>{display}</RNText>
+3 -3
View File
@@ -8,7 +8,7 @@ import Animated, {
} from 'react-native-reanimated'
import type React from 'react'
import {isWeb} from '#/platform/detection'
import {IS_WEB} from '#/env'
export function ScreenTransition({
direction,
@@ -31,8 +31,8 @@ export function ScreenTransition({
return (
<Animated.View
entering={isWeb ? webEntering : entering}
exiting={isWeb ? webExiting : exiting}
entering={IS_WEB ? webEntering : entering}
exiting={IS_WEB ? webExiting : exiting}
style={style}>
{children}
</Animated.View>
+1 -1
View File
@@ -82,7 +82,7 @@ export function Trigger({children, label}: TriggerProps) {
if (typeof children === 'function') {
return children({
isNative: true,
IS_NATIVE: true,
control,
state: {
hovered: false,
+1 -1
View File
@@ -68,7 +68,7 @@ export function Trigger({children, label}: TriggerProps) {
<RadixTriggerPassThrough>
{props =>
children({
isNative: false,
IS_NATIVE: false,
state: {
hovered,
focused,
+2 -2
View File
@@ -65,7 +65,7 @@ export type TriggerProps = {
export type TriggerChildProps =
| {
isNative: true
IS_NATIVE: true
control: DialogControlProps
state: {
/**
@@ -92,7 +92,7 @@ export type TriggerChildProps =
}
}
| {
isNative: false
IS_NATIVE: false
state: {
hovered: boolean
focused: boolean
@@ -3,11 +3,11 @@ import {type ListRenderItemInfo, View} from 'react-native'
import {type AppBskyFeedDefs} from '@atproto/api'
import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset'
import {isNative, isWeb} from '#/platform/detection'
import {List, type ListRef} from '#/view/com/util/List'
import {type SectionRef} from '#/screens/Profile/Sections/types'
import {atoms as a, useTheme} from '#/alf'
import * as FeedCard from '#/components/FeedCard'
import {IS_NATIVE, IS_WEB} from '#/env'
function keyExtractor(item: AppBskyFeedDefs.GeneratorView) {
return item.uri
@@ -27,7 +27,7 @@ export const FeedsList = React.forwardRef<SectionRef, ProfilesListProps>(
const onScrollToTop = useCallback(() => {
scrollElRef.current?.scrollToOffset({
animated: isNative,
animated: IS_NATIVE,
offset: -headerHeight,
})
}, [scrollElRef, headerHeight])
@@ -44,7 +44,7 @@ export const FeedsList = React.forwardRef<SectionRef, ProfilesListProps>(
<View
style={[
a.p_lg,
(isWeb || index !== 0) && a.border_t,
(IS_WEB || index !== 0) && a.border_t,
t.atoms.border_contrast_low,
]}>
<FeedCard.Default view={item} />
@@ -3,13 +3,13 @@ import {View} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isNative} from '#/platform/detection'
import {type FeedDescriptor} from '#/state/queries/post-feed'
import {PostFeed} from '#/view/com/posts/PostFeed'
import {EmptyState} from '#/view/com/util/EmptyState'
import {type ListRef} from '#/view/com/util/List'
import {type SectionRef} from '#/screens/Profile/Sections/types'
import {HashtagWide_Stroke1_Corner0_Rounded as HashtagWideIcon} from '#/components/icons/Hashtag'
import {IS_NATIVE} from '#/env'
interface ProfilesListProps {
listUri: string
@@ -24,7 +24,7 @@ export const PostsList = React.forwardRef<SectionRef, ProfilesListProps>(
const onScrollToTop = useCallback(() => {
scrollElRef.current?.scrollToOffset({
animated: isNative,
animated: IS_NATIVE,
offset: -headerHeight,
})
}, [scrollElRef, headerHeight])
@@ -14,7 +14,6 @@ import {
import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted'
import {isNative, isWeb} from '#/platform/detection'
import {useAllListMembersQuery} from '#/state/queries/list-members'
import {useSession} from '#/state/session'
import {List, type ListRef} from '#/view/com/util/List'
@@ -22,6 +21,7 @@ import {type SectionRef} from '#/screens/Profile/Sections/types'
import {atoms as a, useTheme} from '#/alf'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
import {Default as ProfileCard} from '#/components/ProfileCard'
import {IS_NATIVE, IS_WEB} from '#/env'
function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic, index: number) {
return `${item.did}-${index}`
@@ -75,7 +75,7 @@ export const ProfilesList = React.forwardRef<SectionRef, ProfilesListProps>(
}
const onScrollToTop = useCallback(() => {
scrollElRef.current?.scrollToOffset({
animated: isNative,
animated: IS_NATIVE,
offset: -headerHeight,
})
}, [scrollElRef, headerHeight])
@@ -93,7 +93,7 @@ export const ProfilesList = React.forwardRef<SectionRef, ProfilesListProps>(
style={[
a.p_lg,
t.atoms.border_contrast_low,
(isWeb || index !== 0) && a.border_t,
(IS_WEB || index !== 0) && a.border_t,
]}>
<ProfileCard
profile={item}
@@ -19,7 +19,6 @@ import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {type NavigationProp} from '#/lib/routes/types'
import {parseStarterPackUri} from '#/lib/strings/starter-pack'
import {logger} from '#/logger'
import {isIOS} from '#/platform/detection'
import {useActorStarterPacksQuery} from '#/state/queries/actor-starter-packs'
import {
EmptyState,
@@ -36,6 +35,7 @@ import {Loader} from '#/components/Loader'
import * as Prompt from '#/components/Prompt'
import {Default as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
import {Text} from '#/components/Typography'
import {IS_IOS} from '#/env'
interface SectionRef {
scrollToTop: () => void
@@ -136,7 +136,7 @@ export function ProfileStarterPacks({
}, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])
useEffect(() => {
if (isIOS && enabled && scrollElRef.current) {
if (IS_IOS && enabled && scrollElRef.current) {
const nativeTag = findNodeHandle(scrollElRef.current)
setScrollViewTag(nativeTag)
}
+6 -6
View File
@@ -6,13 +6,13 @@ import type ViewShot from 'react-native-view-shot'
import {type AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
import {Trans} from '@lingui/macro'
import {isWeb} from '#/platform/detection'
import {Logo} from '#/view/icons/Logo'
import {Logotype} from '#/view/icons/Logotype'
import {useTheme} from '#/alf'
import {atoms as a} from '#/alf'
import {LinearGradientBackground} from '#/components/LinearGradientBackground'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import * as bsky from '#/types/bsky'
const LazyViewShot = lazy(
@@ -121,7 +121,7 @@ export function QrCodeInner({link}: {link: string}) {
return (
<View style={{position: 'relative'}}>
{/* An SVG version of the logo is placed on top of normal `QRCode` `logo` prop, since the PNG fails to load before the export completes on web. */}
{isWeb && logoArea && (
{IS_WEB && logoArea && (
<View
style={{
position: 'absolute',
@@ -139,9 +139,9 @@ export function QrCodeInner({link}: {link: string}) {
a.rounded_sm,
{height: 225, width: 225, backgroundColor: '#f3f3f3'},
]}
pieceSize={isWeb ? 8 : 6}
pieceSize={IS_WEB ? 8 : 6}
padding={20}
pieceBorderRadius={isWeb ? 4.5 : 3.5}
pieceBorderRadius={IS_WEB ? 4.5 : 3.5}
outerEyesOptions={{
topLeft: {
borderRadius: [12, 12, 0, 12],
@@ -159,11 +159,11 @@ export function QrCodeInner({link}: {link: string}) {
innerEyesOptions={{borderRadius: 3}}
logo={{
href: require('../../../assets/logo.png'),
...(isWeb && {
...(IS_WEB && {
onChange: onLogoAreaChange,
padding: 28,
}),
...(!isWeb && {
...(!IS_WEB && {
padding: 2,
scale: 0.95,
}),
+6 -6
View File
@@ -9,7 +9,6 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {logger} from '#/logger'
import {isNative, isWeb} from '#/platform/detection'
import {atoms as a, useBreakpoints} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
@@ -20,6 +19,7 @@ import {FloppyDisk_Stroke2_Corner0_Rounded as FloppyDiskIcon} from '#/components
import {Loader} from '#/components/Loader'
import {QrCode} from '#/components/StarterPack/QrCode'
import * as Toast from '#/components/Toast'
import {IS_NATIVE, IS_WEB} from '#/env'
import * as bsky from '#/types/bsky'
export function QrCodeDialog({
@@ -56,7 +56,7 @@ export function QrCodeDialog({
const onSavePress = async () => {
ref.current?.capture?.().then(async (uri: string) => {
if (isNative) {
if (IS_NATIVE) {
const res = await requestMediaLibraryPermissionsAsync()
if (!res.granted) {
@@ -111,7 +111,7 @@ export function QrCodeDialog({
})
setIsSaveProcessing(false)
Toast.show(
isWeb
IS_WEB
? _(msg`QR code has been downloaded!`)
: _(msg`QR code saved to your camera roll!`),
)
@@ -178,18 +178,18 @@ export function QrCodeDialog({
label={_(msg`Copy QR code`)}
color="primary_subtle"
size="large"
onPress={isWeb ? onCopyPress : onSharePress}>
onPress={IS_WEB ? onCopyPress : onSharePress}>
<ButtonIcon
icon={
isCopyProcessing
? Loader
: isWeb
: IS_WEB
? ChainLinkIcon
: ShareIcon
}
/>
<ButtonText>
{isWeb ? <Trans>Copy</Trans> : <Trans>Share</Trans>}
{IS_WEB ? <Trans>Copy</Trans> : <Trans>Share</Trans>}
</ButtonText>
</Button>
<Button
+8 -4
View File
@@ -8,7 +8,6 @@ import {useSaveImageToMediaLibrary} from '#/lib/media/save-image'
import {shareUrl} from '#/lib/sharing'
import {getStarterPackOgCard} from '#/lib/strings/starter-pack'
import {logger} from '#/logger'
import {isNative, isWeb} from '#/platform/detection'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {type DialogControlProps} from '#/components/Dialog'
@@ -18,6 +17,7 @@ import {Download_Stroke2_Corner0_Rounded as DownloadIcon} from '#/components/ico
import {QrCode_Stroke2_Corner0_Rounded as QrCodeIcon} from '#/components/icons/QrCode'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
import {IS_NATIVE, IS_WEB} from '#/env'
interface Props {
starterPack: AppBskyGraphDefs.StarterPackView
@@ -110,13 +110,17 @@ function ShareDialogInner({
],
]}>
<Button
label={isWeb ? _(msg`Copy link`) : _(msg`Share link`)}
label={IS_WEB ? _(msg`Copy link`) : _(msg`Share link`)}
color="primary_subtle"
size="large"
onPress={onShareLink}>
<ButtonIcon icon={ChainLinkIcon} />
<ButtonText>
{isWeb ? <Trans>Copy Link</Trans> : <Trans>Share link</Trans>}
{IS_WEB ? (
<Trans>Copy Link</Trans>
) : (
<Trans>Share link</Trans>
)}
</ButtonText>
</Button>
<Button
@@ -133,7 +137,7 @@ function ShareDialogInner({
<Trans>Share QR code</Trans>
</ButtonText>
</Button>
{isNative && (
{IS_NATIVE && (
<Button
label={_(msg`Save image`)}
color="secondary"
@@ -10,7 +10,6 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {isWeb} from '#/platform/detection'
import {type ListMethods} from '#/view/com/util/List'
import {
type WizardAction,
@@ -24,6 +23,7 @@ import {
WizardProfileCard,
} from '#/components/StarterPack/Wizard/WizardListCard'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
function keyExtractor(
item: AppBskyActorDefs.ProfileViewBasic | AppBskyFeedDefs.GeneratorView,
@@ -95,7 +95,7 @@ export function WizardEditListDialog({
a.mb_sm,
t.atoms.bg,
t.atoms.border_contrast_medium,
isWeb
IS_WEB
? [
a.align_center,
{
@@ -113,7 +113,7 @@ export function WizardEditListDialog({
)}
</Text>
<View style={{width: 60}}>
{isWeb && (
{IS_WEB && (
<Button
label={_(msg`Close`)}
variant="ghost"
+3 -3
View File
@@ -1,8 +1,8 @@
import {View} from 'react-native'
import {isTouchDevice} from '#/lib/browser'
import {isNative, isWeb} from '#/platform/detection'
import {atoms as a, useTheme, type ViewStyleProp} from '#/alf'
import {IS_NATIVE, IS_WEB} from '#/env'
export function SubtleHover({
style,
@@ -39,9 +39,9 @@ export function SubtleHover({
/>
)
if (isWeb && web) {
if (IS_WEB && web) {
return isTouchDevice ? null : el
} else if (isNative && native) {
} else if (IS_NATIVE && native) {
return el
}
+3 -3
View File
@@ -18,7 +18,6 @@ import {useLingui} from '@lingui/react'
import {HITSLOP_10} from '#/lib/constants'
import {makeListLink, makeProfileLink} from '#/lib/routes/links'
import {logger} from '#/logger'
import {isNative} from '#/platform/detection'
import {
type ThreadgateAllowUISetting,
threadgateViewToAllowUISetting,
@@ -37,6 +36,7 @@ import {Earth_Stroke2_Corner0_Rounded as EarthIcon} from '#/components/icons/Glo
import {Group3_Stroke2_Corner0_Rounded as GroupIcon} from '#/components/icons/Group'
import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
import * as bsky from '#/types/bsky'
interface WhoCanReplyProps {
@@ -86,7 +86,7 @@ export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
: _(msg`Some people can reply`)
const onPressOpen = () => {
if (isNative && Keyboard.isVisible()) {
if (IS_NATIVE && Keyboard.isVisible()) {
Keyboard.dismiss()
}
if (isThreadAuthor) {
@@ -229,7 +229,7 @@ function WhoCanReplyDialog({
embeddingDisabled={embeddingDisabled}
/>
</View>
{isNative && (
{IS_NATIVE && (
<Button
label={_(msg`Close`)}
onPress={() => control.close()}
@@ -18,7 +18,6 @@ import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-disp
import {cleanError} from '#/lib/strings/errors'
import {sanitizeHandle} from '#/lib/strings/handles'
import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {updateProfileShadow} from '#/state/cache/profile-shadow'
import {RQKEY_getActivitySubscriptions} from '#/state/queries/activity-subscriptions'
import {useAgent} from '#/state/session'
@@ -37,6 +36,7 @@ import * as Toggle from '#/components/forms/Toggle'
import {Loader} from '#/components/Loader'
import * as ProfileCard from '#/components/ProfileCard'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import type * as bsky from '#/types/bsky'
export function SubscribeProfileDialog({
@@ -195,7 +195,7 @@ function DialogInner({
}
} else {
// on web, a disabled save button feels more natural than a massive close button
if (isWeb) {
if (IS_WEB) {
return {
label: _(msg`Save changes`),
color: 'secondary',
@@ -3,7 +3,6 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
import {isNative} from '#/platform/detection'
import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {AgeAssuranceAppealDialog} from '#/components/ageAssurance/AgeAssuranceAppealDialog'
@@ -23,6 +22,7 @@ import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
import {logger, useAgeAssurance} from '#/ageAssurance'
import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess'
import {IS_NATIVE} from '#/env'
import {useDeviceGeolocationApi} from '#/geolocation'
export function AgeAssuranceAccountCard({style}: ViewStyleProp & {}) {
@@ -86,7 +86,7 @@ function Inner({style}: ViewStyleProp & {}) {
<View style={[a.pb_md, a.gap_xs]}>
<Text style={[a.text_sm, a.leading_snug]}>{copy.notice}</Text>
{isNative && (
{IS_NATIVE && (
<>
<Text style={[a.text_sm, a.leading_snug]}>
<Trans>
@@ -5,7 +5,6 @@ import {useLingui} from '@lingui/react'
import {retry} from '#/lib/async/retry'
import {wait} from '#/lib/async/wait'
import {isNative} from '#/platform/detection'
import {useAgent} from '#/state/session'
import {atoms as a, useTheme, web} from '#/alf'
import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge'
@@ -18,6 +17,7 @@ import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
import {refetchAgeAssuranceServerState} from '#/ageAssurance'
import {logger} from '#/ageAssurance'
import {IS_NATIVE} from '#/env'
export type AgeAssuranceRedirectDialogState = {
result: 'success' | 'unknown'
@@ -166,7 +166,7 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
</Trans>
</Text>
{isNative && (
{IS_NATIVE && (
<View style={[a.w_full, a.pt_lg]}>
<Button
label={_(msg`Close`)}
@@ -225,7 +225,7 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
)}
</Text>
{error && isNative && (
{error && IS_NATIVE && (
<View style={[a.w_full, a.pt_lg]}>
<Button
label={_(msg`Close`)}
@@ -8,12 +8,12 @@ import {useLingui} from '@lingui/react'
import {HITSLOP_10} from '#/lib/constants'
import {useGate} from '#/lib/statsig/statsig'
import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs'
import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import {Link} from '../Link'
import {useIsFindContactsFeatureEnabledBasedOnGeolocation} from './country-allowlist'
@@ -92,7 +92,7 @@ function useInternalState() {
const gate = useGate()
const visible = useMemo(() => {
if (isWeb) return false
if (IS_WEB) return false
if (hidden) return false
if (nux && nux.completed) return false
if (!isFeatureEnabled) return false
@@ -9,10 +9,10 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {mergeRefs} from '#/lib/merge-refs'
import {isAndroid, isIOS} from '#/platform/detection'
import {atoms as a, ios, platform, useTheme} from '#/alf'
import {useInteractionState} from '#/components/hooks/useInteractionState'
import {Text} from '#/components/Typography'
import {IS_ANDROID, IS_IOS} from '#/env'
export function OTPInput({
label,
@@ -95,7 +95,7 @@ export function OTPInput({
<TextInput
// SMS autofill is borked on iOS if you open the keyboard immediately -sfn
onLayout={ios(() => setTimeout(() => innerRef.current?.focus(), 100))}
autoFocus={isAndroid}
autoFocus={IS_ANDROID}
accessible
accessibilityLabel={label}
accessibilityHint=""
@@ -135,7 +135,7 @@ export function OTPInput({
android: {opacity: 0},
}),
]}
caretHidden={isIOS}
caretHidden={IS_IOS}
clearTextOnFocus
/>
</Pressable>
+3 -3
View File
@@ -7,7 +7,6 @@ import {useCleanError} from '#/lib/hooks/useCleanError'
import {isAppPassword} from '#/lib/jwt'
import {getAge, getDateAgo} from '#/lib/strings/time'
import {logger} from '#/logger'
import {isIOS, isWeb} from '#/platform/detection'
import {
useBirthdateMutation,
useIsBirthdateUpdateAllowed,
@@ -26,6 +25,7 @@ import {DateField} from '#/components/forms/DateField'
import {SimpleInlineLinkText} from '#/components/Link'
import {Loader} from '#/components/Loader'
import {Span, Text} from '#/components/Typography'
import {IS_IOS, IS_WEB} from '#/env'
export function BirthDateSettingsDialog({
control,
@@ -154,7 +154,7 @@ function BirthdayInner({
return (
<View style={a.gap_lg} testID="birthDateSettingsDialog">
<View style={isIOS && [a.w_full, a.align_center]}>
<View style={IS_IOS && [a.w_full, a.align_center]}>
<DateField
testID="birthdayInput"
value={date}
@@ -191,7 +191,7 @@ function BirthdayInner({
<ErrorMessage message={errorMessage} style={[a.rounded_sm]} />
) : undefined}
<View style={isWeb && [a.flex_row, a.justify_end]}>
<View style={IS_WEB && [a.flex_row, a.justify_end]}>
<Button
label={hasChanged ? _(msg`Save birthdate`) : _(msg`Done`)}
size="large"
@@ -6,7 +6,6 @@ import {useLingui} from '@lingui/react'
import {wait} from '#/lib/async/wait'
import {isNetworkError, useCleanError} from '#/lib/hooks/useCleanError'
import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {atoms as a, useTheme, web} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
@@ -14,6 +13,7 @@ import * as Dialog from '#/components/Dialog'
import {PinLocation_Stroke2_Corner0_Rounded as LocationIcon} from '#/components/icons/PinLocation'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import {type Geolocation, useRequestDeviceGeolocation} from '#/geolocation'
export type Props = {
@@ -138,7 +138,7 @@ function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) {
disabled={isRequesting}
label={_(msg`Allow location access`)}
onPress={onPressConfirm}
size={isWeb ? 'small' : 'large'}
size={IS_WEB ? 'small' : 'large'}
color="primary">
<ButtonIcon icon={isRequesting ? Loader : LocationIcon} />
<ButtonText>
@@ -147,11 +147,11 @@ function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) {
</Button>
)}
{!isWeb && (
{!IS_WEB && (
<Button
label={_(msg`Cancel`)}
onPress={() => close()}
size={isWeb ? 'small' : 'large'}
size={IS_WEB ? 'small' : 'large'}
color="secondary">
<ButtonText>
<Trans>Cancel</Trans>
+3 -3
View File
@@ -13,7 +13,6 @@ import {useLingui} from '@lingui/react'
import {logEvent} from '#/lib/statsig/statsig'
import {cleanError} from '#/lib/strings/errors'
import {isWeb} from '#/platform/detection'
import {
type Gif,
tenorUrlToBskyGifUrl,
@@ -31,6 +30,7 @@ import {useThrottledValue} from '#/components/hooks/useThrottledValue'
import {ArrowLeft_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arrow'
import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
import {IS_WEB} from '#/env'
export function GifSelectDialog({
controlRef,
@@ -149,7 +149,7 @@ function GifList({
a.pb_sm,
t.atoms.bg,
]}>
{!gtMobile && isWeb && (
{!gtMobile && IS_WEB && (
<Button
size="small"
variant="ghost"
@@ -161,7 +161,7 @@ function GifList({
</Button>
)}
<TextField.Root style={[!gtMobile && isWeb && a.flex_1]}>
<TextField.Root style={[!gtMobile && IS_WEB && a.flex_1]}>
<TextField.Icon icon={Search} />
<TextField.Input
label={_(msg`Search GIFs`)}
@@ -4,19 +4,19 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useOpenLink} from '#/lib/hooks/useOpenLink'
import {isWeb} from '#/platform/detection'
import {useSetInAppBrowser} from '#/state/preferences/in-app-browser'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {SquareArrowTopRight_Stroke2_Corner0_Rounded as External} from '#/components/icons/SquareArrowTopRight'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import {useGlobalDialogsControlContext} from './Context'
export function InAppBrowserConsentDialog() {
const {inAppBrowserConsentControl} = useGlobalDialogsControlContext()
if (isWeb) return null
if (IS_WEB) return null
return (
<Dialog.Outer
+2 -2
View File
@@ -5,7 +5,6 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {logger} from '#/logger'
import {isNative} from '#/platform/detection'
import {
usePreferencesQuery,
useRemoveMutedWordMutation,
@@ -32,6 +31,7 @@ import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import {Loader} from '#/components/Loader'
import * as Prompt from '#/components/Prompt'
import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
const ONE_DAY = 24 * 60 * 60 * 1000
@@ -406,7 +406,7 @@ function MutedWordsInner() {
)}
</View>
{isNative && <View style={{height: 20}} />}
{IS_NATIVE && <View style={{height: 20}} />}
</View>
<Dialog.Close />
@@ -12,7 +12,6 @@ import {useQueryClient} from '@tanstack/react-query'
import {useHaptics} from '#/lib/haptics'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {logger} from '#/logger'
import {isIOS} from '#/platform/detection'
import {STALE} from '#/state/queries'
import {useMyListsQuery} from '#/state/queries/my-lists'
import {useGetPost} from '#/state/queries/post'
@@ -52,6 +51,7 @@ import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/ico
import {CloseQuote_Stroke2_Corner1_Rounded as QuoteIcon} from '#/components/icons/Quote'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
import {IS_IOS} from '#/env'
export type PostInteractionSettingsFormProps = {
canSave?: boolean
@@ -531,7 +531,7 @@ export function PostInteractionSettingsForm({
hitSlop={0}
onPress={() => {
playHaptic('Light')
if (isIOS && !showLists) {
if (IS_IOS && !showLists) {
LayoutAnimation.configureNext({
...LayoutAnimation.Presets.linear,
duration: 175,
@@ -13,7 +13,6 @@ import {useLingui} from '@lingui/react'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {isWeb} from '#/platform/detection'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
import {useListConvosQuery} from '#/state/queries/messages/list-conversations'
@@ -29,6 +28,7 @@ import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/ic
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import * as ProfileCard from '#/components/ProfileCard'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import type * as bsky from '#/types/bsky'
export type ProfileItem = {
@@ -254,7 +254,7 @@ export function SearchablePeopleList({
)
useLayoutEffect(() => {
if (isWeb) {
if (IS_WEB) {
setImmediate(() => {
inputRef?.current?.focus()
})
@@ -290,12 +290,12 @@ export function SearchablePeopleList({
]}>
{title}
</Text>
{isWeb ? (
{IS_WEB ? (
<Button
label={_(msg`Close`)}
size="small"
shape="round"
variant={isWeb ? 'ghost' : 'solid'}
variant={IS_WEB ? 'ghost' : 'solid'}
color="secondary"
style={[
a.absolute,
+3 -3
View File
@@ -3,7 +3,6 @@ import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isNative} from '#/platform/detection'
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import {useCloseAllActiveElements} from '#/state/util'
import {Logo} from '#/view/icons/Logo'
@@ -13,6 +12,7 @@ import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
export function SigninDialog() {
const {signinDialogControl: control} = useGlobalDialogsControlContext()
@@ -45,7 +45,7 @@ function SigninDialogInner({}: {control: Dialog.DialogOuterProps['control']}) {
<Dialog.ScrollableInner
label={_(msg`Sign in to Bluesky or create a new account`)}
style={[gtMobile ? {width: 'auto', maxWidth: 420} : a.w_full]}>
<View style={[!isNative && a.p_2xl]}>
<View style={[!IS_NATIVE && a.p_2xl]}>
<View
style={[
a.flex_row,
@@ -101,7 +101,7 @@ function SigninDialogInner({}: {control: Dialog.DialogOuterProps['control']}) {
</Button>
</View>
{isNative && <View style={{height: 10}} />}
{IS_NATIVE && <View style={{height: 10}} />}
</View>
<Dialog.Close />
+4 -4
View File
@@ -12,7 +12,6 @@ import {useQueryClient} from '@tanstack/react-query'
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
import {type NavigationProp} from '#/lib/routes/types'
import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {
invalidateActorStarterPacksWithMembershipQuery,
useActorStarterPacksWithMembershipsQuery,
@@ -32,6 +31,7 @@ import {StarterPack} from '#/components/icons/StarterPack'
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import * as bsky from '#/types/bsky'
type StarterPackWithMembership =
@@ -91,7 +91,7 @@ function Empty({onStartWizard}: {onStartWizard: () => void}) {
const t = useTheme()
return (
<View style={[a.gap_2xl, {paddingTop: isWeb ? 100 : 64}]}>
<View style={[a.gap_2xl, {paddingTop: IS_WEB ? 100 : 64}]}>
<View style={[a.gap_xs, a.align_center]}>
<StarterPack
width={48}
@@ -169,7 +169,7 @@ function StarterPackList({
<View
style={[
{justifyContent: 'space-between', flexDirection: 'row'},
isWeb ? a.mb_2xl : a.my_lg,
IS_WEB ? a.mb_2xl : a.my_lg,
a.align_center,
]}>
<Text style={[a.text_lg, a.font_semi_bold]}>
@@ -232,7 +232,7 @@ function StarterPackList({
onEndReachedThreshold={0.1}
ListHeaderComponent={listHeader}
ListEmptyComponent={<Empty onStartWizard={onStartWizard} />}
style={isWeb ? [a.px_md, {minHeight: 500}] : [a.px_2xl, a.pt_lg]}
style={IS_WEB ? [a.px_md, {minHeight: 500}] : [a.px_2xl, a.pt_lg]}
/>
)
}
@@ -9,7 +9,6 @@ import {isOverMaxGraphemeCount} from '#/lib/strings/helpers'
import {richTextToString} from '#/lib/strings/rich-text-helpers'
import {shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip'
import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {type ImageMeta} from '#/state/gallery'
import {
useListCreateMutation,
@@ -26,6 +25,7 @@ import * as TextField from '#/components/forms/TextField'
import {Loader} from '#/components/Loader'
import * as Prompt from '#/components/Prompt'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
const DISPLAY_NAME_MAX_GRAPHEMES = 64
const DESCRIPTION_MAX_GRAPHEMES = 300
@@ -48,7 +48,7 @@ export function CreateOrEditListDialog({
// 'You might lose unsaved changes' warning
useEffect(() => {
if (isWeb && dirty) {
if (IS_WEB && dirty) {
const abortController = new AbortController()
const {signal} = abortController
window.addEventListener('beforeunload', evt => evt.preventDefault(), {
@@ -4,13 +4,13 @@ import {Image} from 'expo-image'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isWeb} from '#/platform/detection'
import {atoms as a, useTheme, web} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {useNuxDialogContext} from '#/components/dialogs/nuxs'
import {Sparkle_Stroke2_Corner0_Rounded as SparkleIcon} from '#/components/icons/Sparkle'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
export function ActivitySubscriptionsNUX() {
const t = useTheme()
@@ -44,8 +44,8 @@ export function ActivitySubscriptionsNUX() {
a.overflow_hidden,
t.atoms.bg_contrast_25,
{
gap: isWeb ? 16 : 24,
paddingTop: isWeb ? 24 : 48,
gap: IS_WEB ? 16 : 24,
paddingTop: IS_WEB ? 24 : 48,
borderTopLeftRadius: a.rounded_md.borderRadius,
borderTopRightRadius: a.rounded_md.borderRadius,
},
@@ -120,7 +120,7 @@ export function ActivitySubscriptionsNUX() {
style={[
a.align_center,
a.px_xl,
isWeb ? [a.pt_xl, a.gap_xl, a.pb_sm] : [a.pt_3xl, a.gap_3xl],
IS_WEB ? [a.pt_xl, a.gap_xl, a.pb_sm] : [a.pt_3xl, a.gap_3xl],
]}>
<View style={[a.gap_md, a.align_center]}>
<Text
@@ -130,7 +130,7 @@ export function ActivitySubscriptionsNUX() {
a.font_bold,
a.text_center,
{
fontSize: isWeb ? 28 : 32,
fontSize: IS_WEB ? 28 : 32,
maxWidth: 300,
},
]}>
@@ -153,7 +153,7 @@ export function ActivitySubscriptionsNUX() {
</Text>
</View>
{!isWeb && (
{!IS_WEB && (
<Button
label={_(msg`Close`)}
size="large"
@@ -5,7 +5,6 @@ import {LinearGradient} from 'expo-linear-gradient'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isWeb} from '#/platform/detection'
import {atoms as a, useTheme, web} from '#/alf'
import {transparentifyColor} from '#/alf/util/colorGeneration'
import {Button, ButtonText} from '#/components/Button'
@@ -13,6 +12,7 @@ import * as Dialog from '#/components/Dialog'
import {useNuxDialogContext} from '#/components/dialogs/nuxs'
import {Sparkle_Stroke2_Corner0_Rounded as SparkleIcon} from '#/components/icons/Sparkle'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
export function BookmarksAnnouncement() {
const t = useTheme()
@@ -49,7 +49,7 @@ export function BookmarksAnnouncement() {
a.overflow_hidden,
{
gap: 16,
paddingTop: isWeb ? 24 : 40,
paddingTop: IS_WEB ? 24 : 40,
borderTopLeftRadius: a.rounded_md.borderRadius,
borderTopRightRadius: a.rounded_md.borderRadius,
},
@@ -90,7 +90,7 @@ export function BookmarksAnnouncement() {
borderRadius: 24,
aspectRatio: 333 / 104,
},
isWeb
IS_WEB
? [
{
boxShadow: `0px 10px 15px -3px ${transparentifyColor(t.palette.black, 0.2)}`,
@@ -136,7 +136,7 @@ export function BookmarksAnnouncement() {
a.font_bold,
a.text_center,
{
fontSize: isWeb ? 28 : 32,
fontSize: IS_WEB ? 28 : 32,
maxWidth: 300,
},
]}>
@@ -158,7 +158,7 @@ export function BookmarksAnnouncement() {
</Text>
</View>
{!isWeb && (
{!IS_WEB && (
<Button
label={_(msg`Close`)}
size="large"
@@ -6,7 +6,6 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {logger} from '#/logger'
import {isNative, isWeb} from '#/platform/detection'
import {atoms as a, useTheme, web} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import {isFindContactsFeatureEnabled} from '#/components/contacts/country-allowlist'
@@ -17,13 +16,14 @@ import {
isExistingUserAsOf,
} from '#/components/dialogs/nuxs/utils'
import {Text} from '#/components/Typography'
import {IS_NATIVE, IS_WEB} from '#/env'
import {IS_E2E} from '#/env'
import {navigate} from '#/Navigation'
export const enabled = createIsEnabledCheck(props => {
return (
!IS_E2E &&
isNative &&
IS_NATIVE &&
isExistingUserAsOf(
'2025-12-16T00:00:00.000Z',
props.currentProfile.createdAt,
@@ -89,7 +89,7 @@ export function FindContactsAnnouncement() {
a.font_bold,
a.text_center,
{
fontSize: isWeb ? 28 : 32,
fontSize: IS_WEB ? 28 : 32,
maxWidth: 300,
},
]}>
@@ -6,7 +6,6 @@ import {useLingui} from '@lingui/react'
import {urls} from '#/lib/constants'
import {logger} from '#/logger'
import {isNative} from '#/platform/detection'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
@@ -15,6 +14,7 @@ import {Sparkle_Stroke2_Corner0_Rounded as SparkleIcon} from '#/components/icons
import {VerifierCheck} from '#/components/icons/VerifierCheck'
import {Link} from '#/components/Link'
import {Span, Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
export function InitialVerificationAnnouncement() {
const t = useTheme()
@@ -173,7 +173,7 @@ export function InitialVerificationAnnouncement() {
<Trans>Read blog post</Trans>
</ButtonText>
</Link>
{isNative && (
{IS_NATIVE && (
<Button
label={_(msg`Close`)}
size="small"
@@ -5,7 +5,6 @@ import {LinearGradient} from 'expo-linear-gradient'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isWeb} from '#/platform/detection'
import {atoms as a, select, useTheme, utils, web} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
@@ -16,6 +15,7 @@ import {
} from '#/components/dialogs/nuxs/utils'
import {Beaker_Stroke2_Corner2_Rounded as BeakerIcon} from '#/components/icons/Beaker'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import {IS_E2E} from '#/env'
export const enabled = createIsEnabledCheck(props => {
@@ -72,7 +72,7 @@ export function LiveNowBetaDialog() {
a.overflow_hidden,
{
gap: 16,
paddingTop: isWeb ? 24 : 40,
paddingTop: IS_WEB ? 24 : 40,
borderTopLeftRadius: a.rounded_md.borderRadius,
borderTopRightRadius: a.rounded_md.borderRadius,
},
@@ -116,7 +116,7 @@ export function LiveNowBetaDialog() {
borderRadius: 24,
aspectRatio: 652 / 211,
},
isWeb
IS_WEB
? [
{
boxShadow: `0px 10px 15px -3px ${shadowColor}`,
@@ -163,7 +163,7 @@ export function LiveNowBetaDialog() {
a.font_bold,
a.text_center,
{
fontSize: isWeb ? 28 : 32,
fontSize: IS_WEB ? 28 : 32,
maxWidth: 360,
},
]}>
@@ -186,7 +186,7 @@ export function LiveNowBetaDialog() {
</Text>
</View>
{!isWeb && (
{!IS_WEB && (
<Button
label={_(msg`Close`)}
size="large"
+1 -1
View File
@@ -21,7 +21,7 @@ export function ActionsWrapper({
<MessageContextMenu message={message}>
{trigger =>
// will always be true, since this file is platform split
trigger.isNative && (
trigger.IS_NATIVE && (
<View style={[a.flex_1, a.relative]}>
<View
style={[
+4 -4
View File
@@ -89,9 +89,9 @@ export function ActionsWrapper({
: [a.ml_xs, {marginRight: 'auto'}],
]}>
<EmojiReactionPicker message={message} onEmojiSelect={onEmojiSelect}>
{({props, state, isNative, control}) => {
{({props, state, IS_NATIVE, control}) => {
// always false, file is platform split
if (isNative) return null
if (IS_NATIVE) return null
const showMenuTrigger = showActions || control.isOpen ? 1 : 0
return (
<Pressable
@@ -111,9 +111,9 @@ export function ActionsWrapper({
}}
</EmojiReactionPicker>
<MessageContextMenu message={message}>
{({props, state, isNative, control}) => {
{({props, state, IS_NATIVE, control}) => {
// always false, file is platform split
if (isNative) return null
if (IS_NATIVE) return null
const showMenuTrigger = showActions || control.isOpen ? 1 : 0
return (
<Pressable
+2 -2
View File
@@ -7,7 +7,6 @@ import {StackActions, useNavigation} from '@react-navigation/native'
import type React from 'react'
import {type NavigationProp} from '#/lib/routes/types'
import {isNative} from '#/platform/detection'
import {useProfileShadow} from '#/state/cache/profile-shadow'
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
import {
@@ -21,6 +20,7 @@ import * as Dialog from '#/components/Dialog'
import * as Toggle from '#/components/forms/Toggle'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
type ReportDialogParams = {
convoId: string
@@ -130,7 +130,7 @@ function DoneStep({
onMutate: () => {
if (currentScreen === 'conversation') {
navigation.dispatch(
StackActions.replace('Messages', isNative ? {animation: 'pop'} : {}),
StackActions.replace('Messages', IS_NATIVE ? {animation: 'pop'} : {}),
)
}
},
+3 -3
View File
@@ -12,9 +12,9 @@ import {useLingui} from '@lingui/react'
import {ScaleAndFadeIn} from '#/lib/custom-animations/ScaleAndFade'
import {ShrinkAndPop} from '#/lib/custom-animations/ShrinkAndPop'
import {useHaptics} from '#/lib/haptics'
import {isWeb} from '#/platform/detection'
import {atoms as a, useTheme} from '#/alf'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
const AnimatedPressable = Animated.createAnimatedComponent(Pressable)
@@ -41,12 +41,12 @@ export function ChatEmptyPill() {
}, [_])
const onPressIn = React.useCallback(() => {
if (isWeb) return
if (IS_WEB) return
scale.set(() => withTiming(1.075, {duration: 100}))
}, [scale])
const onPressOut = React.useCallback(() => {
if (isWeb) return
if (IS_WEB) return
scale.set(() => withTiming(1, {duration: 100}))
}, [scale])
+2 -2
View File
@@ -3,11 +3,11 @@ import {useLingui} from '@lingui/react'
import {StackActions, useNavigation} from '@react-navigation/native'
import {type NavigationProp} from '#/lib/routes/types'
import {isNative} from '#/platform/detection'
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
import * as Toast from '#/view/com/util/Toast'
import {type DialogOuterProps} from '#/components/Dialog'
import * as Prompt from '#/components/Prompt'
import {IS_NATIVE} from '#/env'
export function LeaveConvoPrompt({
control,
@@ -27,7 +27,7 @@ export function LeaveConvoPrompt({
onMutate: () => {
if (currentScreen === 'conversation') {
navigation.dispatch(
StackActions.replace('Messages', isNative ? {animation: 'pop'} : {}),
StackActions.replace('Messages', IS_NATIVE ? {animation: 'pop'} : {}),
)
}
},
+2 -2
View File
@@ -8,7 +8,6 @@ import {useLingui} from '@lingui/react'
import {useTranslate} from '#/lib/hooks/useTranslate'
import {richTextToString} from '#/lib/strings/rich-text-helpers'
import {logger} from '#/logger'
import {isNative} from '#/platform/detection'
import {useConvoActive} from '#/state/messages/convo'
import {useLanguagePrefs} from '#/state/preferences'
import {useSession} from '#/state/session'
@@ -23,6 +22,7 @@ import {Warning_Stroke2_Corner0_Rounded as Warning} from '#/components/icons/War
import {ReportDialog} from '#/components/moderation/ReportDialog'
import * as Prompt from '#/components/Prompt'
import {usePromptControl} from '#/components/Prompt'
import {IS_NATIVE} from '#/env'
import {EmojiReactionPicker} from './EmojiReactionPicker'
import {hasReachedReactionLimit} from './util'
@@ -112,7 +112,7 @@ export let MessageContextMenu = ({
return (
<>
<ContextMenu.Root>
{isNative && (
{IS_NATIVE && (
<ContextMenu.AuxiliaryView align={isFromSelf ? 'right' : 'left'}>
<EmojiReactionPicker
message={message}
+3 -3
View File
@@ -21,7 +21,6 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {isNative} from '#/platform/detection'
import {useConvoActive} from '#/state/messages/convo'
import {type ConvoItem} from '#/state/messages/convo/types'
import {useSession} from '#/state/session'
@@ -32,6 +31,7 @@ import {ActionsWrapper} from '#/components/dms/ActionsWrapper'
import {InlineLinkText} from '#/components/Link'
import {RichText} from '#/components/RichText'
import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
import {DateDivider} from './DateDivider'
import {MessageItemEmbed} from './MessageItemEmbed'
import {localDateString} from './util'
@@ -218,10 +218,10 @@ let MessageItem = ({
</View>
)}
{isNative && appliedReactions}
{IS_NATIVE && appliedReactions}
</ActionsWrapper>
{!isNative && appliedReactions}
{!IS_NATIVE && appliedReactions}
{isLastInGroup && (
<MessageItemMetadata
+2 -2
View File
@@ -10,7 +10,6 @@ import {useLingui} from '@lingui/react'
import {makeProfileLink} from '#/lib/routes/links'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {isWeb} from '#/platform/detection'
import {type Shadow} from '#/state/cache/profile-shadow'
import {isConvoActive, useConvo} from '#/state/messages/convo'
import {type ConvoItem} from '#/state/messages/convo/types'
@@ -24,8 +23,9 @@ import {PostAlerts} from '#/components/moderation/PostAlerts'
import {Text} from '#/components/Typography'
import {useSimpleVerificationState} from '#/components/verification'
import {VerificationCheck} from '#/components/verification/VerificationCheck'
import {IS_WEB} from '#/env'
const PFP_SIZE = isWeb ? 40 : Layout.HEADER_SLOT_SIZE
const PFP_SIZE = IS_WEB ? 40 : Layout.HEADER_SLOT_SIZE
export function MessagesListHeader({
profile,
+5 -5
View File
@@ -14,9 +14,9 @@ import {
ScaleAndFadeOut,
} from '#/lib/custom-animations/ScaleAndFade'
import {useHaptics} from '#/lib/haptics'
import {isAndroid, isIOS, isWeb} from '#/platform/detection'
import {atoms as a, useTheme} from '#/alf'
import {Text} from '#/components/Typography'
import {IS_ANDROID, IS_IOS, IS_WEB} from '#/env'
const AnimatedPressable = Animated.createAnimatedComponent(Pressable)
@@ -28,18 +28,18 @@ export function NewMessagesPill({
const t = useTheme()
const playHaptic = useHaptics()
const {bottom: bottomInset} = useSafeAreaInsets()
const bottomBarHeight = isIOS ? 42 : isAndroid ? 60 : 0
const bottomOffset = isWeb ? 0 : bottomInset + bottomBarHeight
const bottomBarHeight = IS_IOS ? 42 : IS_ANDROID ? 60 : 0
const bottomOffset = IS_WEB ? 0 : bottomInset + bottomBarHeight
const scale = useSharedValue(1)
const onPressIn = React.useCallback(() => {
if (isWeb) return
if (IS_WEB) return
scale.set(() => withTiming(1.075, {duration: 100}))
}, [scale])
const onPressOut = React.useCallback(() => {
if (isWeb) return
if (IS_WEB) return
scale.set(() => withTiming(1, {duration: 100}))
}, [scale])
+2 -2
View File
@@ -4,12 +4,12 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {HITSLOP_10} from '#/lib/constants'
import {isNative} from '#/platform/detection'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
import * as TextField from '#/components/forms/TextField'
import {MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlassIcon} from '#/components/icons/MagnifyingGlass'
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import {IS_NATIVE} from '#/env'
type SearchInputProps = Omit<TextField.InputProps, 'label'> & {
label?: TextField.InputProps['label']
@@ -36,7 +36,7 @@ export const SearchInput = React.forwardRef<TextInput, SearchInputProps>(
placeholder={_(msg`Search`)}
returnKeyType="search"
keyboardAppearance={t.scheme}
selectTextOnFocus={isNative}
selectTextOnFocus={IS_NATIVE}
autoFocus={false}
accessibilityRole="search"
autoCorrect={false}
+2 -2
View File
@@ -10,7 +10,6 @@ import Animated, {Easing, LinearTransition} from 'react-native-reanimated'
import {HITSLOP_10} from '#/lib/constants'
import {useHaptics} from '#/lib/haptics'
import {isNative} from '#/platform/detection'
import {
atoms as a,
native,
@@ -22,6 +21,7 @@ import {
import {useInteractionState} from '#/components/hooks/useInteractionState'
import {CheckThick_Stroke2_Corner0_Rounded as Checkmark} from '#/components/icons/Check'
import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
export * from './Panel'
@@ -562,4 +562,4 @@ export function BaseRadio({
)
}
export const Platform = isNative ? Switch : Checkbox
export const Platform = IS_NATIVE ? Switch : Checkbox
+2 -2
View File
@@ -7,7 +7,7 @@ import {
} from 'react'
import {isFirefox, isSafari} from '#/lib/browser'
import {isWeb} from '#/platform/detection'
import {IS_WEB} from '#/env'
function fullscreenSubscribe(onChange: () => void) {
document.addEventListener('fullscreenchange', onChange)
@@ -15,7 +15,7 @@ function fullscreenSubscribe(onChange: () => void) {
}
export function useFullscreen(ref?: React.RefObject<HTMLElement | null>) {
if (!isWeb) throw new Error("'useFullscreen' is a web-only hook")
if (!IS_WEB) throw new Error("'useFullscreen' is a web-only hook")
const isFullscreen = useSyncExternalStore(fullscreenSubscribe, () =>
Boolean(document.fullscreenElement),
)
@@ -4,9 +4,9 @@ import {
createStarterPackLinkFromAndroidReferrer,
httpStarterPackUriToAtUri,
} from '#/lib/strings/starter-pack'
import {isAndroid} from '#/platform/detection'
import {useHasCheckedForStarterPack} from '#/state/preferences/used-starter-packs'
import {useSetActiveStarterPack} from '#/state/shell/starter-pack'
import {IS_ANDROID} from '#/env'
import {Referrer, SharedPrefs} from '../../../modules/expo-bluesky-swiss-army'
export function useStarterPackEntry() {
@@ -32,7 +32,7 @@ export function useStarterPackEntry() {
;(async () => {
let uri: string | null | undefined
if (isAndroid) {
if (IS_ANDROID) {
const res = await Referrer.getGooglePlayReferrerInfoAsync()
if (res && res.installReferrer) {
+2 -2
View File
@@ -1,7 +1,7 @@
import {useEffect, useState} from 'react'
import {isWeb} from '#/platform/detection'
import {useSession} from '#/state/session'
import {IS_WEB} from '#/env'
export function useWelcomeModal() {
const {hasSession} = useSession()
@@ -22,7 +22,7 @@ export function useWelcomeModal() {
// 2. We're on the web (this is a web-only feature)
// 3. We're on the homepage (path is '/' or '/home')
// 4. User hasn't actively closed the modal in this session
if (isWeb && !hasSession && typeof window !== 'undefined') {
if (IS_WEB && !hasSession && typeof window !== 'undefined') {
const currentPath = window.location.pathname
const isHomePage = currentPath === '/'
const hasUserClosedModal =
+2 -2
View File
@@ -11,12 +11,12 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {type Dimensions} from '#/lib/media/types'
import {isNative} from '#/platform/detection'
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
import {atoms as a, useTheme} from '#/alf'
import {ArrowsDiagonalOut_Stroke2_Corner0_Rounded as Fullscreen} from '#/components/icons/ArrowsDiagonal'
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
export function ConstrainedImage({
aspectRatio,
@@ -35,7 +35,7 @@ export function ConstrainedImage({
* the height of the image.
*/
const outerAspectRatio = useMemo<DimensionValue>(() => {
const ratio = isNative
const ratio = IS_NATIVE
? Math.min(1 / aspectRatio, minMobileAspectRatio ?? 16 / 9) // 9:16 bounding box
: Math.min(1 / aspectRatio, 1) // 1:1 bounding box
return `${ratio * 100}%`
@@ -3,7 +3,6 @@ import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isNative} from '#/platform/detection'
import {useAgent, useSession} from '#/state/session'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
@@ -15,6 +14,7 @@ import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as Resend} from '#/c
import {useIntentDialogs} from '#/components/intents/IntentDialogs'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
export function VerifyEmailIntentDialog() {
const {verifyEmailDialogControl: control} = useIntentDialogs()
@@ -68,7 +68,7 @@ function Inner({}: {control: DialogControlProps}) {
<Loader size="xl" fill={t.atoms.text_contrast_low.color} />
</View>
) : status === 'success' ? (
<View style={[a.gap_sm, isNative && a.pb_xl]}>
<View style={[a.gap_sm, IS_NATIVE && a.pb_xl]}>
<Text style={[a.font_bold, a.text_2xl]}>
<Trans>Email Verified</Trans>
</Text>
@@ -93,7 +93,7 @@ function Inner({}: {control: DialogControlProps}) {
</Text>
</View>
) : (
<View style={[a.gap_sm, isNative && a.pb_xl]}>
<View style={[a.gap_sm, IS_NATIVE && a.pb_xl]}>
<Text style={[a.font_bold, a.text_2xl]}>
<Trans>Email Resent</Trans>
</Text>
@@ -12,7 +12,6 @@ import {useLabelInfo} from '#/lib/moderation/useLabelInfo'
import {makeProfileLink} from '#/lib/routes/links'
import {sanitizeHandle} from '#/lib/strings/handles'
import {logger} from '#/logger'
import {isAndroid} from '#/platform/detection'
import {useAgent, useSession} from '#/state/session'
import * as Toast from '#/view/com/util/Toast'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
@@ -20,6 +19,7 @@ import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography'
import {IS_ANDROID} from '#/env'
import {Admonition} from '../Admonition'
import {Divider} from '../Divider'
import {Loader} from '../Loader'
@@ -344,7 +344,7 @@ function AppealForm({
{isPending && <ButtonIcon icon={Loader} />}
</Button>
</View>
{isAndroid && <View style={{height: 300}} />}
{IS_ANDROID && <View style={{height: 300}} />}
</>
)
}
@@ -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({
</View>
)}
{isNative && <View style={{height: 40}} />}
{IS_NATIVE && <View style={{height: 40}} />}
<Dialog.Close />
</Dialog.ScrollableInner>
@@ -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}]}>
<View style={[a.gap_2xl, isNative && a.pt_md]}>
<View style={[a.gap_2xl, IS_NATIVE && a.pt_md]}>
<StepOuter>
<StepTitle
index={1}
+11
View File
@@ -1,3 +1,4 @@
import {Platform} from 'react-native'
import {nativeBuildVersion} from 'expo-application'
import {BUNDLE_IDENTIFIER, IS_TESTFLIGHT, RELEASE_VERSION} from '#/env/common'
@@ -17,3 +18,13 @@ export const APP_VERSION = `${RELEASE_VERSION}.${nativeBuildVersion}`
export const APP_METADATA = `${BUNDLE_IDENTIFIER.slice(0, 7)} (${
__DEV__ ? 'dev' : IS_TESTFLIGHT ? 'tf' : 'prod'
})`
/**
* Platform detection
*/
export const IS_IOS: boolean = Platform.OS === 'ios'
export const IS_ANDROID: boolean = Platform.OS === 'android'
export const IS_NATIVE: boolean = true
export const IS_WEB: boolean = false
export const IS_WEB_MOBILE: boolean = false
export const IS_WEB_MOBILE_IOS: boolean = false
+13
View File
@@ -13,3 +13,16 @@ export const APP_VERSION = RELEASE_VERSION
* The short commit hash and environment of the current bundle.
*/
export const APP_METADATA = `${BUNDLE_IDENTIFIER.slice(0, 7)} (${__DEV__ ? 'dev' : 'prod'})`
/**
* Platform detection
*/
export const IS_IOS: boolean = false
export const IS_ANDROID: boolean = false
export const IS_NATIVE: boolean = false
export const IS_WEB: boolean = true
// @ts-ignore we know window exists -prf
export const IS_WEB_MOBILE: boolean = global.window.matchMedia(
'only screen and (max-width: 1300px)',
)?.matches
export const IS_WEB_MOBILE_IOS: boolean = /iPhone/.test(navigator.userAgent)
@@ -3,7 +3,6 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useCleanError} from '#/lib/hooks/useCleanError'
import {isNative} from '#/platform/detection'
import {atoms as a, web} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
@@ -11,6 +10,7 @@ import * as Dialog from '#/components/Dialog'
import {Loader} from '#/components/Loader'
import * as Toast from '#/components/Toast'
import {Span, Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
import {useUpdateLiveEventPreferences} from '#/features/liveEvents/preferences'
import {
type LiveEventFeed,
@@ -146,7 +146,7 @@ function Inner({
</ButtonText>
{isHidingAllFeeds && <ButtonIcon icon={Loader} />}
</Button>
{isNative && (
{IS_NATIVE && (
<Button
label={_(msg`Cancel`)}
size="large"
+2 -2
View File
@@ -3,12 +3,12 @@ import {type Agent, AppBskyActorDefs, asPredicate} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query'
import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {
preferencesQueryKey,
usePreferencesQuery,
} from '#/state/queries/preferences'
import {useAgent} from '#/state/session'
import {IS_WEB} from '#/env'
import * as env from '#/env'
import {
type LiveEventFeed,
@@ -41,7 +41,7 @@ function useWebOnlyDebugLiveEventPreferences() {
const agent = useAgent()
useEffect(() => {
if (env.IS_DEV && isWeb && typeof window !== 'undefined') {
if (env.IS_DEV && IS_WEB && typeof window !== 'undefined') {
// @ts-ignore
window.__updateLiveEventPreferences = async (
action: LiveEventPreferencesAction,
+2 -2
View File
@@ -3,7 +3,7 @@ import {Platform} from 'react-native'
import * as Location from 'expo-location'
import {createPermissionHook} from 'expo-modules-core'
import {isNative} from '#/platform/detection'
import {IS_NATIVE} from '#/env'
import * as debug from '#/geolocation/debug'
import {logger} from '#/geolocation/logger'
import {type Geolocation} from '#/geolocation/types'
@@ -118,7 +118,7 @@ export function useSyncDeviceGeolocationOnStartup(
const synced = useRef(false)
const [status] = useForegroundPermissions()
useEffect(() => {
if (!isNative) return
if (!IS_NATIVE) return
async function get() {
// no need to set this more than once per session
+2 -2
View File
@@ -1,6 +1,6 @@
import {type LocationGeocodedAddress} from 'expo-location'
import {isAndroid} from '#/platform/detection'
import {IS_ANDROID} from '#/env'
import {logger} from '#/geolocation/logger'
import {type Geolocation} from '#/geolocation/types'
@@ -81,7 +81,7 @@ export function normalizeDeviceLocation(
/*
* Android doesn't give us ISO 3166-2 short codes. We need these for US
*/
if (isAndroid) {
if (IS_ANDROID) {
if (region && isoCountryCode === 'US') {
/*
* We need short codes for US states. If we can't remap it, just drop it

Some files were not shown because too many files have changed in this diff Show More