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: Platform detection:
```tsx ```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 // Native-specific logic
} }
``` ```
@@ -11,7 +11,7 @@ import {
import {useSafeAreaInsets} from 'react-native-safe-area-context' import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {requireNativeModule, requireNativeViewManager} from 'expo-modules-core' import {requireNativeModule, requireNativeViewManager} from 'expo-modules-core'
import {isIOS} from '#/platform/detection' import {IS_IOS} from '#/env'
import { import {
type BottomSheetState, type BottomSheetState,
type BottomSheetViewProps, type BottomSheetViewProps,
@@ -30,7 +30,7 @@ const NativeView: React.ComponentType<
const NativeModule = requireNativeModule('BottomSheet') const NativeModule = requireNativeModule('BottomSheet')
const isIOS15 = const IS_IOS15 =
Platform.OS === 'ios' && Platform.OS === 'ios' &&
// semvar - can be 3 segments, so can't use Number(Platform.Version) // semvar - can be 3 segments, so can't use Number(Platform.Version)
Number(Platform.Version.split('.').at(0)) < 16 Number(Platform.Version.split('.').at(0)) < 16
@@ -91,7 +91,7 @@ export class BottomSheetNativeComponent extends React.Component<
} }
let extraStyles let extraStyles
if (isIOS15 && this.state.viewHeight) { if (IS_IOS15 && this.state.viewHeight) {
const {viewHeight} = this.state const {viewHeight} = this.state
const cornerRadius = this.props.cornerRadius ?? 0 const cornerRadius = this.props.cornerRadius ?? 0
if (viewHeight < screenHeight / 2) { if (viewHeight < screenHeight / 2) {
@@ -112,7 +112,7 @@ export class BottomSheetNativeComponent extends React.Component<
onStateChange={this.onStateChange} onStateChange={this.onStateChange}
extraStyles={extraStyles} extraStyles={extraStyles}
onLayout={e => { onLayout={e => {
if (isIOS15) { if (IS_IOS15) {
const {height} = e.nativeEvent.layout const {height} = e.nativeEvent.layout
this.setState({viewHeight: height}) this.setState({viewHeight: height})
} }
@@ -153,7 +153,7 @@ function BottomSheetNativeComponentInner({
const insets = useSafeAreaInsets() const insets = useSafeAreaInsets()
const cornerRadius = rest.cornerRadius ?? 0 const cornerRadius = rest.cornerRadius ?? 0
const sheetHeight = isIOS ? screenHeight - insets.top : screenHeight const sheetHeight = IS_IOS ? screenHeight - insets.top : screenHeight
return ( return (
<NativeView <NativeView
+3 -3
View File
@@ -23,7 +23,6 @@ import {s} from '#/lib/styles'
import {ThemeProvider} from '#/lib/ThemeContext' import {ThemeProvider} from '#/lib/ThemeContext'
import I18nProvider from '#/locale/i18nProvider' import I18nProvider from '#/locale/i18nProvider'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isAndroid, isIOS} from '#/platform/detection'
import {Provider as A11yProvider} from '#/state/a11y' import {Provider as A11yProvider} from '#/state/a11y'
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
import {Provider as DialogStateProvider} from '#/state/dialogs' 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 {ToastOutlet} from '#/components/Toast'
import {Provider as AgeAssuranceV2Provider} from '#/ageAssurance' import {Provider as AgeAssuranceV2Provider} from '#/ageAssurance'
import {prefetchAgeAssuranceConfig} from '#/ageAssurance' import {prefetchAgeAssuranceConfig} from '#/ageAssurance'
import {IS_ANDROID, IS_IOS} from '#/env'
import { import {
prefetchLiveEvents, prefetchLiveEvents,
Provider as LiveEventsProvider, Provider as LiveEventsProvider,
@@ -79,10 +79,10 @@ import {BottomSheetProvider} from '../modules/bottom-sheet'
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
SplashScreen.preventAutoHideAsync() SplashScreen.preventAutoHideAsync()
if (isIOS) { if (IS_IOS) {
SystemUI.setBackgroundColorAsync('black') SystemUI.setBackgroundColorAsync('black')
} }
if (isAndroid) { if (IS_ANDROID) {
// iOS is handled by the config plugin -sfn // iOS is handled by the config plugin -sfn
ScreenOrientation.lockAsync( ScreenOrientation.lockAsync(
ScreenOrientation.OrientationLock.PORTRAIT_UP, 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 {attachRouteToLogEvents, logEvent} from '#/lib/statsig/statsig'
import {bskyTitle} from '#/lib/strings/headings' import {bskyTitle} from '#/lib/strings/headings'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isNative, isWeb} from '#/platform/detection'
import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {useUnreadNotifications} from '#/state/queries/notifications/unread'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useLoggedOutViewControls} from '#/state/shell/logged-out'
@@ -138,6 +137,7 @@ import {
EmailDialogScreenID, EmailDialogScreenID,
useEmailDialogControl, useEmailDialogControl,
} from '#/components/dialogs/EmailDialog' } from '#/components/dialogs/EmailDialog'
import {IS_NATIVE, IS_WEB} from '#/env'
import {router} from '#/routes' import {router} from '#/routes'
import {Referrer} from '../modules/expo-bluesky-swiss-army' 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 // 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. // since it will be created by react-navigation.
if (path.includes('intent/')) { if (path.includes('intent/')) {
if (isNative) return if (IS_NATIVE) return
return buildStateObject('Flat', 'Home', params) return buildStateObject('Flat', 'Home', params)
} }
if (isNative) { if (IS_NATIVE) {
if (name === 'Search') { if (name === 'Search') {
return buildStateObject('SearchTab', 'Search', params) return buildStateObject('SearchTab', 'Search', params)
} }
@@ -921,7 +921,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
) )
async function handlePushNotificationEntry() { async function handlePushNotificationEntry() {
if (!isNative) return if (!IS_NATIVE) return
// deep links take precedence - on android, // deep links take precedence - on android,
// getLastNotificationResponseAsync returns a "notification" // getLastNotificationResponseAsync returns a "notification"
@@ -1069,7 +1069,7 @@ function reset(): Promise<void> {
navigationRef.dispatch( navigationRef.dispatch(
CommonActions.reset({ CommonActions.reset({
index: 0, index: 0,
routes: [{name: isNative ? 'HomeTab' : 'Home'}], routes: [{name: IS_NATIVE ? 'HomeTab' : 'Home'}],
}), }),
) )
return Promise.race([ return Promise.race([
@@ -1103,7 +1103,7 @@ function logModuleInitTime() {
initMs, initMs,
}) })
if (isWeb) { if (IS_WEB) {
const referrerInfo = Referrer.getReferrerInfo() const referrerInfo = Referrer.getReferrerInfo()
if (referrerInfo && referrerInfo.hostname !== 'bsky.app') { if (referrerInfo && referrerInfo.hostname !== 'bsky.app') {
logEvent('deepLink:referrerReceived', { logEvent('deepLink:referrerReceived', {
@@ -10,8 +10,6 @@ import {
} from '#/lib/hooks/useCreateSupportLink' } from '#/lib/hooks/useCreateSupportLink'
import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo' import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {isNative} from '#/platform/detection'
import {useIsBirthdateUpdateAllowed} from '#/state/birthdate' import {useIsBirthdateUpdateAllowed} from '#/state/birthdate'
import {useSessionApi} from '#/state/session' import {useSessionApi} from '#/state/session'
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
@@ -38,6 +36,8 @@ import {
isLegacyBirthdateBug, isLegacyBirthdateBug,
useAgeAssuranceRegionConfig, useAgeAssuranceRegionConfig,
} from '#/ageAssurance/util' } from '#/ageAssurance/util'
import {IS_WEB} from '#/env'
import {IS_NATIVE} from '#/env'
import {useDeviceGeolocationApi} from '#/geolocation' import {useDeviceGeolocationApi} from '#/geolocation'
const textStyles = [a.text_md, a.leading_snug] const textStyles = [a.text_md, a.leading_snug]
@@ -74,7 +74,7 @@ export function NoAccessScreen() {
}, []) }, [])
const onPressLogout = useCallback(() => { const onPressLogout = useCallback(() => {
if (isWeb) { if (IS_WEB) {
// We're switching accounts, which remounts the entire app. // 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. // 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 // We can't change the URL via a navigate() call because the navigator
@@ -139,7 +139,7 @@ export function NoAccessScreen() {
contentContainerStyle={[ contentContainerStyle={[
a.px_2xl, a.px_2xl,
{ {
paddingTop: isWeb paddingTop: IS_WEB
? a.p_5xl.padding ? a.p_5xl.padding
: insets.top + a.p_2xl.padding, : insets.top + a.p_2xl.padding,
paddingBottom: 100, paddingBottom: 100,
@@ -359,7 +359,7 @@ function AccessSection() {
)} )}
<View style={[a.gap_xs]}> <View style={[a.gap_xs]}>
{isNative && ( {IS_NATIVE && (
<> <>
<Admonition> <Admonition>
<Trans> <Trans>
@@ -15,8 +15,6 @@ import {useLingui} from '@lingui/react'
import {retry} from '#/lib/async/retry' import {retry} from '#/lib/async/retry'
import {wait} from '#/lib/async/wait' import {wait} from '#/lib/async/wait'
import {parseLinkingUrl} from '#/lib/parseLinkingUrl' import {parseLinkingUrl} from '#/lib/parseLinkingUrl'
import {isWeb} from '#/platform/detection'
import {isIOS} from '#/platform/detection'
import {useAgent, useSession} from '#/state/session' import {useAgent, useSession} from '#/state/session'
import {atoms as a, platform, useBreakpoints, useTheme} from '#/alf' import {atoms as a, platform, useBreakpoints, useTheme} from '#/alf'
import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge' import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge'
@@ -28,6 +26,8 @@ import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {refetchAgeAssuranceServerState} from '#/ageAssurance' import {refetchAgeAssuranceServerState} from '#/ageAssurance'
import {logger} from '#/ageAssurance' import {logger} from '#/ageAssurance'
import {IS_WEB} from '#/env'
import {IS_IOS} from '#/env'
export type RedirectOverlayState = { export type RedirectOverlayState = {
result: 'success' | 'unknown' result: 'success' | 'unknown'
@@ -92,7 +92,7 @@ export function Provider({children}: {children?: React.ReactNode}) {
actorDid: params.get('actorDid') ?? undefined, actorDid: params.get('actorDid') ?? undefined,
}) })
if (isWeb) { if (IS_WEB) {
// Clear the URL parameters so they don't re-trigger // Clear the URL parameters so they don't re-trigger
history.pushState(null, '', '/') history.pushState(null, '', '/')
} }
@@ -145,7 +145,7 @@ export function RedirectOverlay() {
// setting a zIndex when using FullWindowOverlay on iOS // setting a zIndex when using FullWindowOverlay on iOS
// means the taps pass straight through to the underlying content (???) // means the taps pass straight through to the underlying content (???)
// so don't set it on iOS. FullWindowOverlay already does the job. // so don't set it on iOS. FullWindowOverlay already does the job.
!isIOS && {zIndex: 9999}, !IS_IOS && {zIndex: 9999},
t.atoms.bg, t.atoms.bg,
gtMobile ? a.p_2xl : a.p_xl, gtMobile ? a.p_2xl : a.p_xl,
a.align_center, a.align_center,
+4 -4
View File
@@ -1,6 +1,6 @@
import {type TextStyle} from 'react-native' 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' 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"` 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') { export function applyFonts(style: TextStyle, fontFamily: 'system' | 'theme') {
if (fontFamily === 'theme') { if (fontFamily === 'theme') {
if (isAndroid) { if (IS_ANDROID) {
style.fontFamily = style.fontFamily =
{ {
400: 'Inter-Regular', 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 // fallback families only supported on web
style.fontFamily += `, ${WEB_FONT_FAMILIES}` style.fontFamily += `, ${WEB_FONT_FAMILIES}`
} }
@@ -83,7 +83,7 @@ export function applyFonts(style: TextStyle, fontFamily: 'system' | 'theme') {
style.fontVariant = (style.fontVariant || []).concat('no-contextual') style.fontVariant = (style.fontVariant || []).concat('no-contextual')
} else { } else {
// fallback families only supported on web // fallback families only supported on web
if (isWeb) { if (IS_WEB) {
style.fontFamily = style.fontFamily || WEB_FONT_FAMILIES 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 {UITextView} from 'react-native-uitextview'
import createEmojiRegex from 'emoji-regex' import createEmojiRegex from 'emoji-regex'
import {isNative} from '#/platform/detection'
import {isIOS} from '#/platform/detection'
import {type Alf, applyFonts, atoms, flatten} from '#/alf' 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 * 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) { if (s.lineHeight !== 0 && s.lineHeight <= 2) {
s.lineHeight = Math.round(s.fontSize * s.lineHeight) s.lineHeight = Math.round(s.fontSize * s.lineHeight)
} }
} else if (!isNative) { } else if (!IS_NATIVE) {
s.lineHeight = s.fontSize s.lineHeight = s.fontSize
} }
@@ -81,7 +81,7 @@ export function renderChildrenWithEmoji(
props: Omit<TextProps, 'children'> = {}, props: Omit<TextProps, 'children'> = {},
emoji: boolean, emoji: boolean,
) { ) {
if (!isIOS || !emoji) { if (!IS_IOS || !emoji) {
return children return children
} }
return Children.map(children, child => { 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 {type Theme} from '@bsky.app/alf'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isAndroid} from '#/platform/detection' import {IS_ANDROID} from '#/env'
export function setSystemUITheme(themeType: 'theme' | 'lightbox', t: Theme) { export function setSystemUITheme(themeType: 'theme' | 'lightbox', t: Theme) {
if (isAndroid) { if (IS_ANDROID) {
try { try {
if (themeType === 'theme') { if (themeType === 'theme') {
SystemUI.setBackgroundColorAsync(t.atoms.bg.backgroundColor) 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 ColorSchemeName, useColorScheme} from 'react-native'
import {type ThemeName} from '@bsky.app/alf' import {type ThemeName} from '@bsky.app/alf'
import {isWeb} from '#/platform/detection'
import {useThemePrefs} from '#/state/shell' import {useThemePrefs} from '#/state/shell'
import {dark, dim, light} from '#/alf/themes' import {dark, dim, light} from '#/alf/themes'
import {IS_WEB} from '#/env'
export function useColorModeTheme(): ThemeName { export function useColorModeTheme(): ThemeName {
const theme = useThemeName() const theme = useThemeName()
@@ -40,7 +40,7 @@ function getThemeName(
function updateDocument(theme: ThemeName) { function updateDocument(theme: ThemeName) {
// @ts-ignore web only // @ts-ignore web only
if (isWeb && typeof window !== 'undefined') { if (IS_WEB && typeof window !== 'undefined') {
// @ts-ignore web only // @ts-ignore web only
const html = window.document.documentElement const html = window.document.documentElement
// @ts-ignore web only // @ts-ignore web only
+5 -5
View File
@@ -49,7 +49,6 @@ import {HITSLOP_10} from '#/lib/constants'
import {useHaptics} from '#/lib/haptics' import {useHaptics} from '#/lib/haptics'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isAndroid, isIOS} from '#/platform/detection'
import {atoms as a, platform, tokens, useTheme} from '#/alf' import {atoms as a, platform, tokens, useTheme} from '#/alf'
import { import {
Context, Context,
@@ -71,6 +70,7 @@ import {
import {useInteractionState} from '#/components/hooks/useInteractionState' import {useInteractionState} from '#/components/hooks/useInteractionState'
import {createPortalGroup} from '#/components/Portal' import {createPortalGroup} from '#/components/Portal'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_ANDROID, IS_IOS} from '#/env'
import {Backdrop} from './Backdrop' import {Backdrop} from './Backdrop'
export { export {
@@ -81,14 +81,14 @@ export {
const {Provider: PortalProvider, Outlet, Portal} = createPortalGroup() const {Provider: PortalProvider, Outlet, Portal} = createPortalGroup()
const SPRING_IN: WithSpringConfig = { const SPRING_IN: WithSpringConfig = {
mass: isIOS ? 1.25 : 0.75, mass: IS_IOS ? 1.25 : 0.75,
damping: 50, damping: 50,
stiffness: 1100, stiffness: 1100,
restDisplacementThreshold: 0.01, restDisplacementThreshold: 0.01,
} }
const SPRING_OUT: WithSpringConfig = { const SPRING_OUT: WithSpringConfig = {
mass: isIOS ? 1.25 : 0.75, mass: IS_IOS ? 1.25 : 0.75,
damping: 150, damping: 150,
stiffness: 1000, stiffness: 1000,
restDisplacementThreshold: 0.01, restDisplacementThreshold: 0.01,
@@ -209,7 +209,7 @@ export function Root({children}: {children: React.ReactNode}) {
) )
useEffect(() => { useEffect(() => {
if (isAndroid && context.isOpen) { if (IS_ANDROID && context.isOpen) {
const listener = BackHandler.addEventListener('hardwareBackPress', () => { const listener = BackHandler.addEventListener('hardwareBackPress', () => {
context.close() context.close()
return true return true
@@ -331,7 +331,7 @@ export function Trigger({children, label, contentLabel, style}: TriggerProps) {
<GestureDetector gesture={composedGestures}> <GestureDetector gesture={composedGestures}>
<View ref={ref} style={[{opacity: context.isOpen ? 0 : 1}, style]}> <View ref={ref} style={[{opacity: context.isOpen ? 0 : 1}, style]}>
{children({ {children({
isNative: true, IS_NATIVE: true,
control: {isOpen: context.isOpen, open}, control: {isOpen: context.isOpen, open},
state: { state: {
pressed: false, pressed: false,
+2 -2
View File
@@ -85,7 +85,7 @@ export type TriggerProps = {
} }
export type TriggerChildProps = export type TriggerChildProps =
| { | {
isNative: true IS_NATIVE: true
control: { control: {
isOpen: boolean isOpen: boolean
open: (mode: 'full' | 'auxiliary-only') => void open: (mode: 'full' | 'auxiliary-only') => void
@@ -115,7 +115,7 @@ export type TriggerChildProps =
} }
} }
| { | {
isNative: false IS_NATIVE: false
control: Dialog.DialogOuterProps['control'] control: Dialog.DialogOuterProps['control']
state: { state: {
hovered: false hovered: false
+1 -1
View File
@@ -18,7 +18,7 @@ import {BottomSheetSnapPoint} from '../../../modules/bottom-sheet/src/BottomShee
export const Context = createContext<DialogContextProps>({ export const Context = createContext<DialogContextProps>({
close: () => {}, close: () => {},
isNativeDialog: false, IS_NATIVEDialog: false,
nativeSnapPoint: BottomSheetSnapPoint.Hidden, nativeSnapPoint: BottomSheetSnapPoint.Hidden,
disableDrag: false, disableDrag: false,
setDisableDrag: () => {}, setDisableDrag: () => {},
+11 -11
View File
@@ -26,7 +26,6 @@ import {useLingui} from '@lingui/react'
import {useEnableKeyboardController} from '#/lib/hooks/useEnableKeyboardController' import {useEnableKeyboardController} from '#/lib/hooks/useEnableKeyboardController'
import {ScrollProvider} from '#/lib/ScrollContext' import {ScrollProvider} from '#/lib/ScrollContext'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isAndroid, isIOS} from '#/platform/detection'
import {useA11y} from '#/state/a11y' import {useA11y} from '#/state/a11y'
import {useDialogStateControlContext} from '#/state/dialogs' import {useDialogStateControlContext} from '#/state/dialogs'
import {List, type ListMethods, type ListProps} from '#/view/com/util/List' import {List, type ListMethods, type ListProps} from '#/view/com/util/List'
@@ -39,6 +38,7 @@ import {
type DialogOuterProps, type DialogOuterProps,
} from '#/components/Dialog/types' } from '#/components/Dialog/types'
import {createInput} from '#/components/forms/TextField' import {createInput} from '#/components/forms/TextField'
import {IS_ANDROID, IS_IOS} from '#/env'
import {BottomSheet, BottomSheetSnapPoint} from '../../../modules/bottom-sheet' import {BottomSheet, BottomSheetSnapPoint} from '../../../modules/bottom-sheet'
import { import {
type BottomSheetSnapPointChangeEvent, type BottomSheetSnapPointChangeEvent,
@@ -154,7 +154,7 @@ export function Outer({
const context = React.useMemo( const context = React.useMemo(
() => ({ () => ({
close, close,
isNativeDialog: true, IS_NATIVEDialog: true,
nativeSnapPoint: snapPoint, nativeSnapPoint: snapPoint,
disableDrag, disableDrag,
setDisableDrag, setDisableDrag,
@@ -209,7 +209,7 @@ export const ScrollableInner = React.forwardRef<ScrollView, DialogInnerProps>(
const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext() const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext()
const insets = useSafeAreaInsets() const insets = useSafeAreaInsets()
useEnableKeyboardController(isIOS) useEnableKeyboardController(IS_IOS)
const [keyboardHeight, setKeyboardHeight] = React.useState(0) const [keyboardHeight, setKeyboardHeight] = React.useState(0)
@@ -224,7 +224,7 @@ export const ScrollableInner = React.forwardRef<ScrollView, DialogInnerProps>(
) )
let paddingBottom = 0 let paddingBottom = 0
if (isIOS) { if (IS_IOS) {
paddingBottom += keyboardHeight / 4 paddingBottom += keyboardHeight / 4
if (nativeSnapPoint === BottomSheetSnapPoint.Full) { if (nativeSnapPoint === BottomSheetSnapPoint.Full) {
paddingBottom += insets.bottom + tokens.space.md paddingBottom += insets.bottom + tokens.space.md
@@ -240,7 +240,7 @@ export const ScrollableInner = React.forwardRef<ScrollView, DialogInnerProps>(
} }
const onScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => { const onScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
if (!isAndroid) { if (!IS_ANDROID) {
return return
} }
const {contentOffset} = e.nativeEvent const {contentOffset} = e.nativeEvent
@@ -260,12 +260,12 @@ export const ScrollableInner = React.forwardRef<ScrollView, DialogInnerProps>(
contentContainerStyle, contentContainerStyle,
]} ]}
ref={ref} ref={ref}
showsVerticalScrollIndicator={isAndroid ? false : undefined} showsVerticalScrollIndicator={IS_ANDROID ? false : undefined}
{...props} {...props}
bounces={nativeSnapPoint === BottomSheetSnapPoint.Full} bounces={nativeSnapPoint === BottomSheetSnapPoint.Full}
bottomOffset={30} bottomOffset={30}
scrollEventThrottle={50} scrollEventThrottle={50}
onScroll={isAndroid ? onScroll : undefined} onScroll={IS_ANDROID ? onScroll : undefined}
keyboardShouldPersistTaps="handled" keyboardShouldPersistTaps="handled"
// TODO: figure out why this positions the header absolutely (rather than stickily) // 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 // 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 insets = useSafeAreaInsets()
const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext() const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext()
useEnableKeyboardController(isIOS) useEnableKeyboardController(IS_IOS)
const onScroll = (e: ScrollEvent) => { const onScroll = (e: ScrollEvent) => {
'worklet' 'worklet'
if (!isAndroid) { if (!IS_ANDROID) {
return return
} }
const {contentOffset} = e const {contentOffset} = e
@@ -311,7 +311,7 @@ export const InnerFlatList = React.forwardRef<
bounces={nativeSnapPoint === BottomSheetSnapPoint.Full} bounces={nativeSnapPoint === BottomSheetSnapPoint.Full}
ListFooterComponent={<View style={{height: insets.bottom + 100}} />} ListFooterComponent={<View style={{height: insets.bottom + 100}} />}
ref={ref} ref={ref}
showsVerticalScrollIndicator={isAndroid ? false : undefined} showsVerticalScrollIndicator={IS_ANDROID ? false : undefined}
{...props} {...props}
style={[a.h_full, style]} style={[a.h_full, style]}
/> />
@@ -326,7 +326,7 @@ export function FlatListFooter({children}: {children: React.ReactNode}) {
const {height} = useReanimatedKeyboardAnimation() const {height} = useReanimatedKeyboardAnimation()
const animatedStyle = useAnimatedStyle(() => { const animatedStyle = useAnimatedStyle(() => {
if (!isIOS) return {} if (!IS_IOS) return {}
return { return {
transform: [{translateY: Math.min(0, height.get() + bottom - 10)}], transform: [{translateY: Math.min(0, height.get() + bottom - 10)}],
} }
+1 -1
View File
@@ -98,7 +98,7 @@ export function Outer({
const context = React.useMemo( const context = React.useMemo(
() => ({ () => ({
close, close,
isNativeDialog: false, IS_NATIVEDialog: false,
nativeSnapPoint: 0, nativeSnapPoint: 0,
disableDrag: false, disableDrag: false,
setDisableDrag: () => {}, setDisableDrag: () => {},
+2 -2
View File
@@ -1,7 +1,7 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import {SystemBars} from 'react-native-edge-to-edge' 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 * 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() { export function useSheetWrapper() {
return useCallback(async <T>(promise: Promise<T>): Promise<T> => { return useCallback(async <T>(promise: Promise<T>): Promise<T> => {
if (isIOS) { if (IS_IOS) {
const entry = SystemBars.pushStackEntry({ const entry = SystemBars.pushStackEntry({
style: { style: {
statusBar: 'light', statusBar: 'light',
+1 -1
View File
@@ -39,7 +39,7 @@ export type DialogControlProps = DialogControlRefProps & {
export type DialogContextProps = { export type DialogContextProps = {
close: DialogControlProps['close'] close: DialogControlProps['close']
isNativeDialog: boolean IS_NATIVEDialog: boolean
nativeSnapPoint: BottomSheetSnapPoint nativeSnapPoint: BottomSheetSnapPoint
disableDrag: boolean disableDrag: boolean
setDisableDrag: React.Dispatch<React.SetStateAction<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 {logEvent, useGate} from '#/lib/statsig/statsig'
import {logger} from '#/logger' import {logger} from '#/logger'
import {type MetricEvents} from '#/logger/metrics' import {type MetricEvents} from '#/logger/metrics'
import {isIOS} from '#/platform/detection'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useGetPopularFeedsQuery} from '#/state/queries/feed' import {useGetPopularFeedsQuery} from '#/state/queries/feed'
import {type FeedDescriptor} from '#/state/queries/post-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 {InlineLinkText} from '#/components/Link'
import * as ProfileCard from '#/components/ProfileCard' import * as ProfileCard from '#/components/ProfileCard'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_IOS} from '#/env'
import type * as bsky from '#/types/bsky' import type * as bsky from '#/types/bsky'
import {FollowDialogWithoutGuide} from './ProgressGuide/FollowDialog' import {FollowDialogWithoutGuide} from './ProgressGuide/FollowDialog'
import {ProgressGuideList} from './ProgressGuide/List' import {ProgressGuideList} from './ProgressGuide/List'
@@ -692,7 +692,7 @@ export function ProfileGrid({
t.atoms.border_contrast_low, t.atoms.border_contrast_low,
t.atoms.bg_contrast_25, t.atoms.bg_contrast_25,
]} ]}
pointerEvents={isIOS ? 'auto' : 'box-none'}> pointerEvents={IS_IOS ? 'auto' : 'box-none'}>
<View <View
style={[ style={[
a.px_lg, a.px_lg,
@@ -701,7 +701,7 @@ export function ProfileGrid({
a.align_center, a.align_center,
a.justify_between, 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]}> <Text style={[a.text_sm, a.font_semi_bold, t.atoms.text]}>
{isFeedContext ? ( {isFeedContext ? (
<Trans>Suggested for you</Trans> <Trans>Suggested for you</Trans>
+3 -3
View File
@@ -9,7 +9,6 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {isWeb} from '#/platform/detection'
import {DraggableScrollView} from '#/view/com/pager/DraggableScrollView' import {DraggableScrollView} from '#/view/com/pager/DraggableScrollView'
import {atoms as a, tokens, useTheme, web} from '#/alf' import {atoms as a, tokens, useTheme, web} from '#/alf'
import {transparentifyColor} from '#/alf/util/colorGeneration' import {transparentifyColor} from '#/alf/util/colorGeneration'
@@ -19,6 +18,7 @@ import {
ArrowRight_Stroke2_Corner0_Rounded as ArrowRight, ArrowRight_Stroke2_Corner0_Rounded as ArrowRight,
} from '#/components/icons/Arrow' } from '#/components/icons/Arrow'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
/** /**
* Tab component that automatically scrolls the selected tab into view - used for interests * Tab component that automatically scrolls the selected tab into view - used for interests
@@ -236,7 +236,7 @@ export function InterestTabs({
) )
})} })}
</DraggableScrollView> </DraggableScrollView>
{isWeb && canScrollLeft && ( {IS_WEB && canScrollLeft && (
<View <View
style={[ style={[
a.absolute, a.absolute,
@@ -270,7 +270,7 @@ export function InterestTabs({
</Button> </Button>
</View> </View>
)} )}
{isWeb && canScrollRight && ( {IS_WEB && canScrollRight && (
<View <View
style={[ style={[
a.absolute, a.absolute,
@@ -10,9 +10,9 @@ import {
INTERNATIONAL_TELEPHONE_CODES, INTERNATIONAL_TELEPHONE_CODES,
} from '#/lib/international-telephone-codes' } from '#/lib/international-telephone-codes'
import {regionName} from '#/locale/helpers' import {regionName} from '#/locale/helpers'
import {isWeb} from '#/platform/detection'
import {atoms as a, web} from '#/alf' import {atoms as a, web} from '#/alf'
import * as Select from '#/components/Select' import * as Select from '#/components/Select'
import {IS_WEB} from '#/env'
import {useGeolocation} from '#/geolocation' import {useGeolocation} from '#/geolocation'
/** /**
@@ -84,7 +84,7 @@ export function InternationalPhoneCodeSelect({
<Select.Item value={item.value} label={item.label}> <Select.Item value={item.value} label={item.label}>
<Select.ItemIndicator /> <Select.ItemIndicator />
<Select.ItemText style={[a.flex_1]} emoji> <Select.ItemText style={[a.flex_1]} emoji>
{isWeb ? <Flag {...item} /> : item.unicodeFlag + ' '} {IS_WEB ? <Flag {...item} /> : item.unicodeFlag + ' '}
{item.name} {item.name}
</Select.ItemText> </Select.ItemText>
<Select.ItemText style={[a.text_right]}> <Select.ItemText style={[a.text_right]}>
@@ -101,7 +101,7 @@ export function InternationalPhoneCodeSelect({
} }
function Flag({unicodeFlag, svgFlag}: {unicodeFlag: string; svgFlag: any}) { function Flag({unicodeFlag, svgFlag}: {unicodeFlag: string; svgFlag: any}) {
if (isWeb) { if (IS_WEB) {
return ( return (
<Image <Image
source={svgFlag} source={svgFlag}
+4 -4
View File
@@ -6,7 +6,6 @@ import {useNavigation} from '@react-navigation/native'
import {HITSLOP_30} from '#/lib/constants' import {HITSLOP_30} from '#/lib/constants'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {isIOS} from '#/platform/detection'
import {useSetDrawerOpen} from '#/state/shell' import {useSetDrawerOpen} from '#/state/shell'
import { import {
atoms as a, atoms as a,
@@ -29,6 +28,7 @@ import {
} from '#/components/Layout/const' } from '#/components/Layout/const'
import {ScrollbarOffsetContext} from '#/components/Layout/context' import {ScrollbarOffsetContext} from '#/components/Layout/context'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_IOS} from '#/env'
export function Outer({ export function Outer({
children, children,
@@ -91,7 +91,7 @@ export function Content({
style={[ style={[
a.flex_1, a.flex_1,
a.justify_center, a.justify_center,
isIOS && align === 'platform' && a.align_center, IS_IOS && align === 'platform' && a.align_center,
{minHeight: HEADER_SLOT_SIZE}, {minHeight: HEADER_SLOT_SIZE},
]}> ]}>
<AlignmentContext.Provider value={align}> <AlignmentContext.Provider value={align}>
@@ -186,7 +186,7 @@ export function TitleText({
a.text_lg, a.text_lg,
a.font_semi_bold, a.font_semi_bold,
a.leading_tight, a.leading_tight,
isIOS && align === 'platform' && a.text_center, IS_IOS && align === 'platform' && a.text_center,
gtMobile && a.text_xl, gtMobile && a.text_xl,
style, style,
]} ]}
@@ -205,7 +205,7 @@ export function SubtitleText({children}: {children: React.ReactNode}) {
style={[ style={[
a.text_sm, a.text_sm,
a.leading_snug, a.leading_snug,
isIOS && align === 'platform' && a.text_center, IS_IOS && align === 'platform' && a.text_center,
t.atoms.text_contrast_medium, t.atoms.text_contrast_medium,
]} ]}
numberOfLines={2}> numberOfLines={2}>
+4 -4
View File
@@ -11,7 +11,6 @@ import Animated, {
} from 'react-native-reanimated' } from 'react-native-reanimated'
import {useSafeAreaInsets} from 'react-native-safe-area-context' import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {isWeb} from '#/platform/detection'
import {useShellLayout} from '#/state/shell/shell-layout' import {useShellLayout} from '#/state/shell/shell-layout'
import { import {
atoms as a, atoms as a,
@@ -23,6 +22,7 @@ import {
import {useDialogContext} from '#/components/Dialog' import {useDialogContext} from '#/components/Dialog'
import {CENTER_COLUMN_OFFSET, SCROLLBAR_OFFSET} from '#/components/Layout/const' import {CENTER_COLUMN_OFFSET, SCROLLBAR_OFFSET} from '#/components/Layout/const'
import {ScrollbarOffsetContext} from '#/components/Layout/context' import {ScrollbarOffsetContext} from '#/components/Layout/context'
import {IS_WEB} from '#/env'
export * from '#/components/Layout/const' export * from '#/components/Layout/const'
export * as Header from '#/components/Layout/Header' export * as Header from '#/components/Layout/Header'
@@ -43,7 +43,7 @@ export const Screen = memo(function Screen({
const {top} = useSafeAreaInsets() const {top} = useSafeAreaInsets()
return ( return (
<> <>
{isWeb && <WebCenterBorders />} {IS_WEB && <WebCenterBorders />}
<View <View
style={[a.util_screen_outer, {paddingTop: noInsetTop ? 0 : top}, style]} style={[a.util_screen_outer, {paddingTop: noInsetTop ? 0 : top}, style]}
{...props} {...props}
@@ -98,7 +98,7 @@ export const Content = memo(
contentContainerStyle, contentContainerStyle,
]} ]}
{...props}> {...props}>
{isWeb ? ( {IS_WEB ? (
<Center ignoreTabletLayoutOffset={ignoreTabletLayoutOffset}> <Center ignoreTabletLayoutOffset={ignoreTabletLayoutOffset}>
{/* @ts-expect-error web only -esb */} {/* @ts-expect-error web only -esb */}
{children} {children}
@@ -145,7 +145,7 @@ export const KeyboardAwareContent = memo(function LayoutKeyboardAwareContent({
]} ]}
keyboardShouldPersistTaps="handled" keyboardShouldPersistTaps="handled"
{...props}> {...props}>
{isWeb ? <Center>{children}</Center> : children} {IS_WEB ? <Center>{children}</Center> : children}
</KeyboardAwareScrollView> </KeyboardAwareScrollView>
) )
}) })
+11 -11
View File
@@ -18,12 +18,12 @@ import {
isExternalUrl, isExternalUrl,
linkRequiresWarning, linkRequiresWarning,
} from '#/lib/strings/url-helpers' } from '#/lib/strings/url-helpers'
import {isNative, isWeb} from '#/platform/detection'
import {useModalControls} from '#/state/modals' import {useModalControls} from '#/state/modals'
import {atoms as a, flatten, type TextStyleProp, useTheme, web} from '#/alf' import {atoms as a, flatten, type TextStyleProp, useTheme, web} from '#/alf'
import {Button, type ButtonProps} from '#/components/Button' import {Button, type ButtonProps} from '#/components/Button'
import {useInteractionState} from '#/components/hooks/useInteractionState' import {useInteractionState} from '#/components/hooks/useInteractionState'
import {Text, type TextProps} from '#/components/Typography' import {Text, type TextProps} from '#/components/Typography'
import {IS_NATIVE, IS_WEB} from '#/env'
import {router} from '#/routes' import {router} from '#/routes'
import {useGlobalDialogsControlContext} from './dialogs/Context' import {useGlobalDialogsControlContext} from './dialogs/Context'
@@ -130,7 +130,7 @@ export function useLink({
linkRequiresWarning(href, displayText), linkRequiresWarning(href, displayText),
) )
if (isWeb) { if (IS_WEB) {
e.preventDefault() e.preventDefault()
} }
@@ -162,7 +162,7 @@ export function useLink({
] ]
// does not apply to web's flat navigator // does not apply to web's flat navigator
if (isNative && screen !== 'NotFound') { if (IS_NATIVE && screen !== 'NotFound') {
const state = navigation.getState() const state = navigation.getState()
// if screen is not in the current navigator, it means it's // if screen is not in the current navigator, it means it's
// most likely a tab screen. note: state can be undefined // most likely a tab screen. note: state can be undefined
@@ -246,7 +246,7 @@ export function useLink({
(e: GestureResponderEvent) => { (e: GestureResponderEvent) => {
const exitEarlyIfFalse = outerOnLongPress?.(e) const exitEarlyIfFalse = outerOnLongPress?.(e)
if (exitEarlyIfFalse === false) return if (exitEarlyIfFalse === false) return
return isNative && shareOnLongPress ? handleLongPress() : undefined return IS_NATIVE && shareOnLongPress ? handleLongPress() : undefined
}, },
[outerOnLongPress, handleLongPress, shareOnLongPress], [outerOnLongPress, handleLongPress, shareOnLongPress],
) )
@@ -501,7 +501,7 @@ export function WebOnlyInlineLinkText({
onPress, onPress,
...props ...props
}: Omit<InlineLinkProps, 'onLongPress'>) { }: Omit<InlineLinkProps, 'onLongPress'>) {
return isWeb ? ( return IS_WEB ? (
<InlineLinkText {...props} to={to} onPress={onPress}> <InlineLinkText {...props} to={to} onPress={onPress}>
{children} {children}
</InlineLinkText> </InlineLinkText>
@@ -547,7 +547,7 @@ export function createStaticClickIfUnmodified(
): {onPress: Exclude<BaseLinkProps['onPress'], undefined>} { ): {onPress: Exclude<BaseLinkProps['onPress'], undefined>} {
return { return {
onPress(e: GestureResponderEvent) { onPress(e: GestureResponderEvent) {
if (!isWeb || !isModifiedClickEvent(e)) { if (!IS_WEB || !isModifiedClickEvent(e)) {
e.preventDefault() e.preventDefault()
onPressHandler(e) onPressHandler(e)
return false return false
@@ -561,7 +561,7 @@ export function createStaticClickIfUnmodified(
* intends to deviate from default behavior. * intends to deviate from default behavior.
*/ */
export function isClickEventWithMetaKey(e: GestureResponderEvent) { export function isClickEventWithMetaKey(e: GestureResponderEvent) {
if (!isWeb) return false if (!IS_WEB) return false
const event = e as unknown as MouseEvent const event = e as unknown as MouseEvent
return event.metaKey || event.altKey || event.ctrlKey || event.shiftKey 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` * Determines if the web click target is anything other than `_self`
*/ */
export function isClickTargetExternal(e: GestureResponderEvent) { export function isClickTargetExternal(e: GestureResponderEvent) {
if (!isWeb) return false if (!IS_WEB) return false
const event = e as unknown as MouseEvent const event = e as unknown as MouseEvent
const el = event.currentTarget as HTMLAnchorElement const el = event.currentTarget as HTMLAnchorElement
return el && el.target && el.target !== '_self' 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} * {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button}
*/ */
export function isModifiedClickEvent(e: GestureResponderEvent): boolean { export function isModifiedClickEvent(e: GestureResponderEvent): boolean {
if (!isWeb) return false if (!IS_WEB) return false
const event = e as unknown as MouseEvent const event = e as unknown as MouseEvent
const isPrimaryButton = event.button === 0 const isPrimaryButton = event.button === 0
return ( return (
@@ -596,8 +596,8 @@ export function isModifiedClickEvent(e: GestureResponderEvent): boolean {
* {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button} * {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button}
*/ */
export function shouldClickOpenNewTab(e: GestureResponderEvent) { export function shouldClickOpenNewTab(e: GestureResponderEvent) {
if (!isWeb) return false if (!IS_WEB) return false
const event = e as unknown as MouseEvent 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 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 {useLingui} from '@lingui/react'
import flattenReactChildren from 'react-keyed-flatten-children' import flattenReactChildren from 'react-keyed-flatten-children'
import {isAndroid, isIOS, isNative} from '#/platform/detection'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button' import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
@@ -30,6 +29,7 @@ import {
type TriggerProps, type TriggerProps,
} from '#/components/Menu/types' } from '#/components/Menu/types'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_ANDROID, IS_IOS, IS_NATIVE} from '#/env'
export { export {
type DialogControlProps as MenuControlProps, type DialogControlProps as MenuControlProps,
@@ -70,7 +70,7 @@ export function Trigger({
} = useInteractionState() } = useInteractionState()
return children({ return children({
isNative: true, IS_NATIVE: true,
control: context.control, control: context.control,
state: { state: {
hovered: false, hovered: false,
@@ -111,7 +111,7 @@ export function Outer({
<Dialog.ScrollableInner label={_(msg`Menu`)}> <Dialog.ScrollableInner label={_(msg`Menu`)}>
<View style={[a.gap_lg]}> <View style={[a.gap_lg]}>
{children} {children}
{isNative && showCancel && <Cancel />} {IS_NATIVE && showCancel && <Cancel />}
</View> </View>
</Dialog.ScrollableInner> </Dialog.ScrollableInner>
</Context.Provider> </Context.Provider>
@@ -137,13 +137,13 @@ export function Item({children, label, style, onPress, ...rest}: ItemProps) {
onFocus={onFocus} onFocus={onFocus}
onBlur={onBlur} onBlur={onBlur}
onPress={async e => { onPress={async e => {
if (isAndroid) { if (IS_ANDROID) {
/** /**
* Below fix for iOS doesn't work for Android, this does. * Below fix for iOS doesn't work for Android, this does.
*/ */
onPress?.(e) onPress?.(e)
context.control.close() context.control.close()
} else if (isIOS) { } else if (IS_IOS) {
/** /**
* Fixes a subtle bug on iOS * Fixes a subtle bug on iOS
* {@link https://github.com/bluesky-social/social-app/pull/5849/files#diff-de516ef5e7bd9840cd639213301df38cf03acfcad5bda85a1d63efd249ba79deL124-L127} * {@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> <RadixTriggerPassThrough>
{props => {props =>
children({ children({
isNative: false, IS_NATIVE: false,
control, control,
state: { state: {
hovered, hovered,
+2 -2
View File
@@ -43,7 +43,7 @@ export type TriggerProps = {
} }
export type TriggerChildProps = export type TriggerChildProps =
| { | {
isNative: true IS_NATIVE: true
control: Dialog.DialogOuterProps['control'] control: Dialog.DialogOuterProps['control']
state: { state: {
/** /**
@@ -73,7 +73,7 @@ export type TriggerChildProps =
} }
} }
| { | {
isNative: false IS_NATIVE: false
control: Dialog.DialogOuterProps['control'] control: Dialog.DialogOuterProps['control']
state: { state: {
hovered: boolean hovered: boolean
+2 -2
View File
@@ -8,7 +8,6 @@ import {differenceInSeconds} from 'date-fns'
import {HITSLOP_10} from '#/lib/constants' import {HITSLOP_10} from '#/lib/constants'
import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo' import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {isNative} from '#/platform/detection'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {atoms as a, useTheme, web} from '#/alf' import {atoms as a, useTheme, web} from '#/alf'
@@ -18,6 +17,7 @@ import {useDialogControl} from '#/components/Dialog'
import {Newskie} from '#/components/icons/Newskie' import {Newskie} from '#/components/icons/Newskie'
import * as StarterPackCard from '#/components/StarterPack/StarterPackCard' import * as StarterPackCard from '#/components/StarterPack/StarterPackCard'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
export function NewskieDialog({ export function NewskieDialog({
profile, profile,
@@ -162,7 +162,7 @@ function DialogInner({
</StarterPackCard.Link> </StarterPackCard.Link>
) : null} ) : null}
{isNative && ( {IS_NATIVE && (
<Button <Button
label={_(msg`Close`)} label={_(msg`Close`)}
color="secondary" color="secondary"
@@ -7,11 +7,11 @@ import {
import {LinearGradient} from 'expo-linear-gradient' import {LinearGradient} from 'expo-linear-gradient'
import {utils} from '@bsky.app/alf' import {utils} from '@bsky.app/alf'
import {isAndroid, isNative} from '#/platform/detection'
import {useA11y} from '#/state/a11y' import {useA11y} from '#/state/a11y'
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
import {FocusScope} from '#/components/FocusScope' import {FocusScope} from '#/components/FocusScope'
import {LockScroll} from '#/components/LockScroll' import {LockScroll} from '#/components/LockScroll'
import {IS_ANDROID, IS_NATIVE} from '#/env'
const GUTTER = 24 const GUTTER = 24
@@ -80,7 +80,7 @@ export function Overlay({
a.z_20, a.z_20,
a.align_center, a.align_center,
!gtPhone && [a.justify_end, {minHeight: frame.height}], !gtPhone && [a.justify_end, {minHeight: frame.height}],
isNative && [ IS_NATIVE && [
{ {
paddingBottom: Math.max(insets.bottom, a.p_2xl.padding), paddingBottom: Math.max(insets.bottom, a.p_2xl.padding),
}, },
@@ -109,7 +109,7 @@ export function Overlay({
<FocusScope> <FocusScope>
<View <View
accessible={isAndroid} accessible={IS_ANDROID}
role="dialog" role="dialog"
aria-role="dialog" aria-role="dialog"
aria-label={label} aria-label={label}
+2 -2
View File
@@ -1,12 +1,12 @@
import {useEffect} from 'react' import {useEffect} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {isIOS} from '#/platform/detection'
import {atoms as a} from '#/alf' import {atoms as a} from '#/alf'
import {FullWindowOverlay} from '#/components/FullWindowOverlay' import {FullWindowOverlay} from '#/components/FullWindowOverlay'
import {usePolicyUpdateContext} from '#/components/PolicyUpdateOverlay/context' import {usePolicyUpdateContext} from '#/components/PolicyUpdateOverlay/context'
import {Portal} from '#/components/PolicyUpdateOverlay/Portal' import {Portal} from '#/components/PolicyUpdateOverlay/Portal'
import {Content} from '#/components/PolicyUpdateOverlay/updates/202508' import {Content} from '#/components/PolicyUpdateOverlay/updates/202508'
import {IS_IOS} from '#/env'
export {Provider} from '#/components/PolicyUpdateOverlay/context' export {Provider} from '#/components/PolicyUpdateOverlay/context'
export {usePolicyUpdateContext} 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 // setting a zIndex when using FullWindowOverlay on iOS
// means the taps pass straight through to the underlying content (???) // means the taps pass straight through to the underlying content (???)
// so don't set it on iOS. FullWindowOverlay already does the job. // so don't set it on iOS. FullWindowOverlay already does the job.
!isIOS && {zIndex: 9999}, !IS_IOS && {zIndex: 9999},
]}> ]}>
<Content state={state} /> <Content state={state} />
</View> </View>
@@ -3,7 +3,6 @@ import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {isAndroid} from '#/platform/detection'
import {useA11y} from '#/state/a11y' import {useA11y} from '#/state/a11y'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button' import {Button, ButtonText} from '#/components/Button'
@@ -12,6 +11,7 @@ import {Badge} from '#/components/PolicyUpdateOverlay/Badge'
import {Overlay} from '#/components/PolicyUpdateOverlay/Overlay' import {Overlay} from '#/components/PolicyUpdateOverlay/Overlay'
import {type PolicyUpdateState} from '#/components/PolicyUpdateOverlay/usePolicyUpdateState' import {type PolicyUpdateState} from '#/components/PolicyUpdateOverlay/usePolicyUpdateState'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_ANDROID} from '#/env'
export function Content({state}: {state: PolicyUpdateState}) { export function Content({state}: {state: PolicyUpdateState}) {
const t = useTheme() const t = useTheme()
@@ -56,7 +56,7 @@ export function Content({state}: {state: PolicyUpdateState}) {
size: 'small', size: 'small',
} as const } 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.`, 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 {useLingui} from '@lingui/react'
import {type EmbedPlayerParams} from '#/lib/strings/embed-player' import {type EmbedPlayerParams} from '#/lib/strings/embed-player'
import {isIOS, isNative, isWeb} from '#/platform/detection'
import {useExternalEmbedsPrefs} from '#/state/preferences' import {useExternalEmbedsPrefs} from '#/state/preferences'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {useDialogControl} from '#/components/Dialog' import {useDialogControl} from '#/components/Dialog'
import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent' import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent'
import {Fill} from '#/components/Fill' import {Fill} from '#/components/Fill'
import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' import {PlayButtonIcon} from '#/components/video/PlayButtonIcon'
import {IS_IOS, IS_NATIVE, IS_WEB} from '#/env'
export function ExternalGif({ export function ExternalGif({
link, link,
@@ -66,12 +66,12 @@ export function ExternalGif({
// Control animation on native // Control animation on native
setIsAnimating(prev => { setIsAnimating(prev => {
if (prev) { if (prev) {
if (isNative) { if (IS_NATIVE) {
imageRef.current?.stopAnimating() imageRef.current?.stopAnimating()
} }
return false return false
} else { } else {
if (isNative) { if (IS_NATIVE) {
imageRef.current?.startAnimating() imageRef.current?.startAnimating()
} }
return true return true
@@ -112,7 +112,7 @@ export function ExternalGif({
<Image <Image
source={{ source={{
uri: uri:
!isPrefetched || (isWeb && !isAnimating) !isPrefetched || (IS_WEB && !isAnimating)
? link.thumb ? link.thumb
: params.playerUri, : params.playerUri,
}} // Web uses the thumb to control playback }} // Web uses the thumb to control playback
@@ -123,7 +123,7 @@ export function ExternalGif({
accessibilityIgnoresInvertColors accessibilityIgnoresInvertColors
accessibilityLabel={link.title} accessibilityLabel={link.title}
accessibilityHint={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) && ( {(!isPrefetched || !isAnimating) && (
@@ -26,7 +26,6 @@ import {
type EmbedPlayerParams, type EmbedPlayerParams,
getPlayerAspect, getPlayerAspect,
} from '#/lib/strings/embed-player' } from '#/lib/strings/embed-player'
import {isNative} from '#/platform/detection'
import {useExternalEmbedsPrefs} from '#/state/preferences' import {useExternalEmbedsPrefs} from '#/state/preferences'
import {EventStopper} from '#/view/com/util/EventStopper' import {EventStopper} from '#/view/com/util/EventStopper'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
@@ -34,6 +33,7 @@ import {useDialogControl} from '#/components/Dialog'
import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent' import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent'
import {Fill} from '#/components/Fill' import {Fill} from '#/components/Fill'
import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' import {PlayButtonIcon} from '#/components/video/PlayButtonIcon'
import {IS_NATIVE} from '#/env'
interface ShouldStartLoadRequest { interface ShouldStartLoadRequest {
url: string url: string
@@ -148,7 +148,7 @@ export function ExternalPlayer({
const {height: winHeight, width: winWidth} = windowDims const {height: winHeight, width: winWidth} = windowDims
// Get the proper screen height depending on what is going on // 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
? winHeight ? winHeight
: winWidth : winWidth
@@ -13,7 +13,6 @@ import {useLingui} from '@lingui/react'
import {HITSLOP_20} from '#/lib/constants' import {HITSLOP_20} from '#/lib/constants'
import {clamp} from '#/lib/numbers' import {clamp} from '#/lib/numbers'
import {type EmbedPlayerParams} from '#/lib/strings/embed-player' import {type EmbedPlayerParams} from '#/lib/strings/embed-player'
import {isWeb} from '#/platform/detection'
import {useAutoplayDisabled} from '#/state/preferences' import {useAutoplayDisabled} from '#/state/preferences'
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
@@ -22,6 +21,7 @@ import {Loader} from '#/components/Loader'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' import {PlayButtonIcon} from '#/components/video/PlayButtonIcon'
import {IS_WEB} from '#/env'
import {GifView} from '../../../../../modules/expo-bluesky-gif-view' import {GifView} from '../../../../../modules/expo-bluesky-gif-view'
import {type GifViewStateChangeEvent} from '../../../../../modules/expo-bluesky-gif-view/src/GifView.types' import {type GifViewStateChangeEvent} from '../../../../../modules/expo-bluesky-gif-view/src/GifView.types'
@@ -218,18 +218,18 @@ const styles = StyleSheet.create({
altContainer: { altContainer: {
backgroundColor: 'rgba(0, 0, 0, 0.75)', backgroundColor: 'rgba(0, 0, 0, 0.75)',
borderRadius: 6, borderRadius: 6,
paddingHorizontal: isWeb ? 8 : 6, paddingHorizontal: IS_WEB ? 8 : 6,
paddingVertical: isWeb ? 6 : 3, paddingVertical: IS_WEB ? 6 : 3,
position: 'absolute', position: 'absolute',
// Related to margin/gap hack. This keeps the alt label in the same position // Related to margin/gap hack. This keeps the alt label in the same position
// on all platforms // on all platforms
right: isWeb ? 8 : 5, right: IS_WEB ? 8 : 5,
bottom: isWeb ? 8 : 5, bottom: IS_WEB ? 8 : 5,
zIndex: 2, zIndex: 2,
}, },
alt: { alt: {
color: 'white', color: 'white',
fontSize: isWeb ? 10 : 7, fontSize: IS_WEB ? 10 : 7,
fontWeight: '600', fontWeight: '600',
}, },
}) })
@@ -10,13 +10,13 @@ import {useHaptics} from '#/lib/haptics'
import {shareUrl} from '#/lib/sharing' import {shareUrl} from '#/lib/sharing'
import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player' import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player'
import {toNiceDomain} from '#/lib/strings/url-helpers' import {toNiceDomain} from '#/lib/strings/url-helpers'
import {isNative} from '#/platform/detection'
import {useExternalEmbedsPrefs} from '#/state/preferences' import {useExternalEmbedsPrefs} from '#/state/preferences'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Divider} from '#/components/Divider' import {Divider} from '#/components/Divider'
import {Earth_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' import {Earth_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe'
import {Link} from '#/components/Link' import {Link} from '#/components/Link'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
import {ExternalGif} from './ExternalGif' import {ExternalGif} from './ExternalGif'
import {ExternalPlayer} from './ExternalPlayer' import {ExternalPlayer} from './ExternalPlayer'
import {GifEmbed} from './Gif' import {GifEmbed} from './Gif'
@@ -53,7 +53,7 @@ export const ExternalEmbed = ({
}, [playHaptic, onOpen]) }, [playHaptic, onOpen])
const onShareExternal = useCallback(() => { const onShareExternal = useCallback(() => {
if (link.uri && isNative) { if (link.uri && IS_NATIVE) {
playHaptic('Heavy') playHaptic('Heavy')
shareUrl(link.uri) shareUrl(link.uri)
} }
@@ -8,7 +8,7 @@ import React, {
} from 'react' } from 'react'
import {useWindowDimensions} from 'react-native' import {useWindowDimensions} from 'react-native'
import {isNative, isWeb} from '#/platform/detection' import {IS_NATIVE, IS_WEB} from '#/env'
const Context = React.createContext<{ const Context = React.createContext<{
activeViewId: string | null activeViewId: string | null
@@ -18,7 +18,7 @@ const Context = React.createContext<{
Context.displayName = 'ActiveVideoWebContext' Context.displayName = 'ActiveVideoWebContext'
export function Provider({children}: {children: React.ReactNode}) { export function Provider({children}: {children: React.ReactNode}) {
if (!isWeb) { if (!IS_WEB) {
throw new Error('ActiveVideoWebContext may only be used on 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( const sendViewPosition = useCallback(
(viewId: string, y: number) => { (viewId: string, y: number) => {
if (isNative) return if (IS_NATIVE) return
if (viewId === activeViewIdRef.current) { if (viewId === activeViewIdRef.current) {
activeViewLocationRef.current = y activeViewLocationRef.current = y
@@ -6,7 +6,6 @@ import type Hls from 'hls.js'
import {isTouchDevice} from '#/lib/browser' import {isTouchDevice} from '#/lib/browser'
import {clamp} from '#/lib/numbers' import {clamp} from '#/lib/numbers'
import {isIPhoneWeb} from '#/platform/detection'
import { import {
useAutoplayDisabled, useAutoplayDisabled,
useSetSubtitlesEnabled, 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 {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB_MOBILE_IOS} from '#/env'
import {TimeIndicator} from '../TimeIndicator' import {TimeIndicator} from '../TimeIndicator'
import {ControlButton} from './ControlButton' import {ControlButton} from './ControlButton'
import {Scrubber} from './Scrubber' import {Scrubber} from './Scrubber'
@@ -400,7 +400,7 @@ export function Controls({
onEndHover={onVolumeEndHover} onEndHover={onVolumeEndHover}
drawFocus={drawFocus} drawFocus={drawFocus}
/> />
{!isIPhoneWeb && ( {!IS_WEB_MOBILE_IOS && (
<ControlButton <ControlButton
active={isFullscreen} active={isFullscreen}
activeLabel={_(msg`Exit fullscreen`)} activeLabel={_(msg`Exit fullscreen`)}
@@ -10,7 +10,6 @@ import {type NavigationProp} from '#/lib/routes/types'
import {shareText, shareUrl} from '#/lib/sharing' import {shareText, shareUrl} from '#/lib/sharing'
import {toShareUrl} from '#/lib/strings/url-helpers' import {toShareUrl} from '#/lib/strings/url-helpers'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isIOS} from '#/platform/detection'
import {useProfileShadow} from '#/state/cache/profile-shadow' import {useProfileShadow} from '#/state/cache/profile-shadow'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import * as Toast from '#/view/com/util/Toast' 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 {PaperPlane_Stroke2_Corner0_Rounded as PaperPlaneIcon} from '#/components/icons/PaperPlane'
import * as Menu from '#/components/Menu' import * as Menu from '#/components/Menu'
import {useAgeAssurance} from '#/ageAssurance' import {useAgeAssurance} from '#/ageAssurance'
import {IS_IOS} from '#/env'
import {useDevMode} from '#/storage/hooks/dev-mode' import {useDevMode} from '#/storage/hooks/dev-mode'
import {RecentChats} from './RecentChats' import {RecentChats} from './RecentChats'
import {type ShareMenuItemsProps} from './ShareMenuItems.types' import {type ShareMenuItemsProps} from './ShareMenuItems.types'
@@ -63,7 +63,7 @@ let ShareMenuItems = ({
const onCopyLink = async () => { const onCopyLink = async () => {
logger.metric('share:press:copyLink', {}, {statsig: true}) logger.metric('share:press:copyLink', {}, {statsig: true})
const url = toShareUrl(href) const url = toShareUrl(href)
if (isIOS) { if (IS_IOS) {
// iOS only // iOS only
await ExpoClipboard.setUrlAsync(url) await ExpoClipboard.setUrlAsync(url)
} else { } else {
@@ -9,7 +9,6 @@ import {type NavigationProp} from '#/lib/routes/types'
import {shareText, shareUrl} from '#/lib/sharing' import {shareText, shareUrl} from '#/lib/sharing'
import {toShareUrl} from '#/lib/strings/url-helpers' import {toShareUrl} from '#/lib/strings/url-helpers'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {useProfileShadow} from '#/state/cache/profile-shadow' import {useProfileShadow} from '#/state/cache/profile-shadow'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {useBreakpoints} from '#/alf' 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 {PaperPlane_Stroke2_Corner0_Rounded as Send} from '#/components/icons/PaperPlane'
import * as Menu from '#/components/Menu' import * as Menu from '#/components/Menu'
import {useAgeAssurance} from '#/ageAssurance' import {useAgeAssurance} from '#/ageAssurance'
import {IS_WEB} from '#/env'
import {useDevMode} from '#/storage/hooks/dev-mode' import {useDevMode} from '#/storage/hooks/dev-mode'
import {type ShareMenuItemsProps} from './ShareMenuItems.types' 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 = () => { const onShareATURI = () => {
shareText(postUri) shareText(postUri)
@@ -12,7 +12,6 @@ import {useLingui} from '@lingui/react'
import {popularInterests, useInterestsDisplayNames} from '#/lib/interests' import {popularInterests, useInterestsDisplayNames} from '#/lib/interests'
import {logEvent} from '#/lib/statsig/statsig' import {logEvent} from '#/lib/statsig/statsig'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useActorSearch} from '#/state/queries/actor-search' import {useActorSearch} from '#/state/queries/actor-search'
import {usePreferencesQuery} from '#/state/queries/preferences' 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 {boostInterests, InterestTabs} from '#/components/InterestTabs'
import * as ProfileCard from '#/components/ProfileCard' import * as ProfileCard from '#/components/ProfileCard'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import type * as bsky from '#/types/bsky' import type * as bsky from '#/types/bsky'
import {ProgressGuideTask} from './Task' import {ProgressGuideTask} from './Task'
@@ -431,7 +431,7 @@ function HeaderTop({guide}: {guide?: Follow10ProgressGuide}) {
<Trans>Find people to follow</Trans> <Trans>Find people to follow</Trans>
</Text> </Text>
{guide && ( {guide && (
<View style={isWeb && {paddingRight: 36}}> <View style={IS_WEB && {paddingRight: 36}}>
<ProgressGuideTask <ProgressGuideTask
current={guide.numFollows + 1} current={guide.numFollows + 1}
total={10 + 1} total={10 + 1}
@@ -440,12 +440,12 @@ function HeaderTop({guide}: {guide?: Follow10ProgressGuide}) {
/> />
</View> </View>
)} )}
{isWeb ? ( {IS_WEB ? (
<Button <Button
label={_(msg`Close`)} label={_(msg`Close`)}
size="small" size="small"
shape="round" shape="round"
variant={isWeb ? 'ghost' : 'solid'} variant={IS_WEB ? 'ghost' : 'solid'}
color="secondary" color="secondary"
style={[ style={[
a.absolute, a.absolute,
@@ -579,7 +579,7 @@ function FollowProfileCardInner({
<ProfileCard.Outer> <ProfileCard.Outer>
<ProfileCard.Header> <ProfileCard.Header>
<ProfileCard.Avatar <ProfileCard.Avatar
disabledPreview={!isWeb} disabledPreview={!IS_WEB}
profile={profile} profile={profile}
moderationOpts={moderationOpts} moderationOpts={moderationOpts}
/> />
+3 -3
View File
@@ -11,9 +11,9 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {isWeb} from '#/platform/detection'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Portal} from '#/components/Portal' import {Portal} from '#/components/Portal'
import {IS_WEB} from '#/env'
import {AnimatedCheck, type AnimatedCheckRef} from '../anim/AnimatedCheck' import {AnimatedCheck, type AnimatedCheckRef} from '../anim/AnimatedCheck'
import {Text} from '../Typography' import {Text} from '../Typography'
@@ -108,11 +108,11 @@ export const ProgressGuideToast = React.forwardRef<
const containerStyle = React.useMemo(() => { const containerStyle = React.useMemo(() => {
let left = 10 let left = 10
let right = 10 let right = 10
if (isWeb && winDim.width > 400) { if (IS_WEB && winDim.width > 400) {
left = right = (winDim.width - 380) / 2 left = right = (winDim.width - 380) / 2
} }
return { return {
position: isWeb ? 'fixed' : 'absolute', position: IS_WEB ? 'fixed' : 'absolute',
top: 0, top: 0,
left, left,
right, right,
+5 -5
View File
@@ -6,7 +6,6 @@ import {useNavigation} from '@react-navigation/native'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {isInvalidHandle} from '#/lib/strings/handles' import {isInvalidHandle} from '#/lib/strings/handles'
import {isNative, isWeb} from '#/platform/detection'
import { import {
usePreferencesQuery, usePreferencesQuery,
useRemoveMutedWordsMutation, useRemoveMutedWordsMutation,
@@ -22,6 +21,7 @@ import {
} from '#/components/Link' } from '#/components/Link'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import * as Menu from '#/components/Menu' import * as Menu from '#/components/Menu'
import {IS_NATIVE, IS_WEB} from '#/env'
export function RichTextTag({ export function RichTextTag({
tag, tag,
@@ -50,7 +50,7 @@ export function RichTextTag({
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
const isCashtag = tag.startsWith('$') const isCashtag = tag.startsWith('$')
const label = isCashtag ? _(msg`Cashtag ${tag}`) : _(msg`Hashtag ${tag}`) 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`Long press to open tag menu for ${isCashtag ? tag : `#${tag}`}`)
: _(msg`Click 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} {...menuProps}
onPress={e => { onPress={e => {
if (isWeb) { if (IS_WEB) {
return createStaticClickIfUnmodified(() => { return createStaticClickIfUnmodified(() => {
if (!isNative) { if (!IS_NATIVE) {
menuProps.onPress() menuProps.onPress()
} }
}).onPress(e) }).onPress(e)
@@ -99,7 +99,7 @@ export function RichTextTag({
label={label} label={label}
style={textStyle} style={textStyle}
emoji> emoji>
{isNative ? ( {IS_NATIVE ? (
display display
) : ( ) : (
<RNText ref={menuProps.ref}>{display}</RNText> <RNText ref={menuProps.ref}>{display}</RNText>
+3 -3
View File
@@ -8,7 +8,7 @@ import Animated, {
} from 'react-native-reanimated' } from 'react-native-reanimated'
import type React from 'react' import type React from 'react'
import {isWeb} from '#/platform/detection' import {IS_WEB} from '#/env'
export function ScreenTransition({ export function ScreenTransition({
direction, direction,
@@ -31,8 +31,8 @@ export function ScreenTransition({
return ( return (
<Animated.View <Animated.View
entering={isWeb ? webEntering : entering} entering={IS_WEB ? webEntering : entering}
exiting={isWeb ? webExiting : exiting} exiting={IS_WEB ? webExiting : exiting}
style={style}> style={style}>
{children} {children}
</Animated.View> </Animated.View>
+1 -1
View File
@@ -82,7 +82,7 @@ export function Trigger({children, label}: TriggerProps) {
if (typeof children === 'function') { if (typeof children === 'function') {
return children({ return children({
isNative: true, IS_NATIVE: true,
control, control,
state: { state: {
hovered: false, hovered: false,
+1 -1
View File
@@ -68,7 +68,7 @@ export function Trigger({children, label}: TriggerProps) {
<RadixTriggerPassThrough> <RadixTriggerPassThrough>
{props => {props =>
children({ children({
isNative: false, IS_NATIVE: false,
state: { state: {
hovered, hovered,
focused, focused,
+2 -2
View File
@@ -65,7 +65,7 @@ export type TriggerProps = {
export type TriggerChildProps = export type TriggerChildProps =
| { | {
isNative: true IS_NATIVE: true
control: DialogControlProps control: DialogControlProps
state: { state: {
/** /**
@@ -92,7 +92,7 @@ export type TriggerChildProps =
} }
} }
| { | {
isNative: false IS_NATIVE: false
state: { state: {
hovered: boolean hovered: boolean
focused: boolean focused: boolean
@@ -3,11 +3,11 @@ import {type ListRenderItemInfo, View} from 'react-native'
import {type AppBskyFeedDefs} from '@atproto/api' import {type AppBskyFeedDefs} from '@atproto/api'
import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset' import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset'
import {isNative, isWeb} from '#/platform/detection'
import {List, type ListRef} from '#/view/com/util/List' import {List, type ListRef} from '#/view/com/util/List'
import {type SectionRef} from '#/screens/Profile/Sections/types' import {type SectionRef} from '#/screens/Profile/Sections/types'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import * as FeedCard from '#/components/FeedCard' import * as FeedCard from '#/components/FeedCard'
import {IS_NATIVE, IS_WEB} from '#/env'
function keyExtractor(item: AppBskyFeedDefs.GeneratorView) { function keyExtractor(item: AppBskyFeedDefs.GeneratorView) {
return item.uri return item.uri
@@ -27,7 +27,7 @@ export const FeedsList = React.forwardRef<SectionRef, ProfilesListProps>(
const onScrollToTop = useCallback(() => { const onScrollToTop = useCallback(() => {
scrollElRef.current?.scrollToOffset({ scrollElRef.current?.scrollToOffset({
animated: isNative, animated: IS_NATIVE,
offset: -headerHeight, offset: -headerHeight,
}) })
}, [scrollElRef, headerHeight]) }, [scrollElRef, headerHeight])
@@ -44,7 +44,7 @@ export const FeedsList = React.forwardRef<SectionRef, ProfilesListProps>(
<View <View
style={[ style={[
a.p_lg, a.p_lg,
(isWeb || index !== 0) && a.border_t, (IS_WEB || index !== 0) && a.border_t,
t.atoms.border_contrast_low, t.atoms.border_contrast_low,
]}> ]}>
<FeedCard.Default view={item} /> <FeedCard.Default view={item} />
@@ -3,13 +3,13 @@ import {View} from 'react-native'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {isNative} from '#/platform/detection'
import {type FeedDescriptor} from '#/state/queries/post-feed' import {type FeedDescriptor} from '#/state/queries/post-feed'
import {PostFeed} from '#/view/com/posts/PostFeed' import {PostFeed} from '#/view/com/posts/PostFeed'
import {EmptyState} from '#/view/com/util/EmptyState' import {EmptyState} from '#/view/com/util/EmptyState'
import {type ListRef} from '#/view/com/util/List' import {type ListRef} from '#/view/com/util/List'
import {type SectionRef} from '#/screens/Profile/Sections/types' import {type SectionRef} from '#/screens/Profile/Sections/types'
import {HashtagWide_Stroke1_Corner0_Rounded as HashtagWideIcon} from '#/components/icons/Hashtag' import {HashtagWide_Stroke1_Corner0_Rounded as HashtagWideIcon} from '#/components/icons/Hashtag'
import {IS_NATIVE} from '#/env'
interface ProfilesListProps { interface ProfilesListProps {
listUri: string listUri: string
@@ -24,7 +24,7 @@ export const PostsList = React.forwardRef<SectionRef, ProfilesListProps>(
const onScrollToTop = useCallback(() => { const onScrollToTop = useCallback(() => {
scrollElRef.current?.scrollToOffset({ scrollElRef.current?.scrollToOffset({
animated: isNative, animated: IS_NATIVE,
offset: -headerHeight, offset: -headerHeight,
}) })
}, [scrollElRef, headerHeight]) }, [scrollElRef, headerHeight])
@@ -14,7 +14,6 @@ import {
import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset' import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted' import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted'
import {isNative, isWeb} from '#/platform/detection'
import {useAllListMembersQuery} from '#/state/queries/list-members' import {useAllListMembersQuery} from '#/state/queries/list-members'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {List, type ListRef} from '#/view/com/util/List' 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 {atoms as a, useTheme} from '#/alf'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists' import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
import {Default as ProfileCard} from '#/components/ProfileCard' import {Default as ProfileCard} from '#/components/ProfileCard'
import {IS_NATIVE, IS_WEB} from '#/env'
function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic, index: number) { function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic, index: number) {
return `${item.did}-${index}` return `${item.did}-${index}`
@@ -75,7 +75,7 @@ export const ProfilesList = React.forwardRef<SectionRef, ProfilesListProps>(
} }
const onScrollToTop = useCallback(() => { const onScrollToTop = useCallback(() => {
scrollElRef.current?.scrollToOffset({ scrollElRef.current?.scrollToOffset({
animated: isNative, animated: IS_NATIVE,
offset: -headerHeight, offset: -headerHeight,
}) })
}, [scrollElRef, headerHeight]) }, [scrollElRef, headerHeight])
@@ -93,7 +93,7 @@ export const ProfilesList = React.forwardRef<SectionRef, ProfilesListProps>(
style={[ style={[
a.p_lg, a.p_lg,
t.atoms.border_contrast_low, t.atoms.border_contrast_low,
(isWeb || index !== 0) && a.border_t, (IS_WEB || index !== 0) && a.border_t,
]}> ]}>
<ProfileCard <ProfileCard
profile={item} profile={item}
@@ -19,7 +19,6 @@ import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {parseStarterPackUri} from '#/lib/strings/starter-pack' import {parseStarterPackUri} from '#/lib/strings/starter-pack'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isIOS} from '#/platform/detection'
import {useActorStarterPacksQuery} from '#/state/queries/actor-starter-packs' import {useActorStarterPacksQuery} from '#/state/queries/actor-starter-packs'
import { import {
EmptyState, EmptyState,
@@ -36,6 +35,7 @@ import {Loader} from '#/components/Loader'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import {Default as StarterPackCard} from '#/components/StarterPack/StarterPackCard' import {Default as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_IOS} from '#/env'
interface SectionRef { interface SectionRef {
scrollToTop: () => void scrollToTop: () => void
@@ -136,7 +136,7 @@ export function ProfileStarterPacks({
}, [isFetchingNextPage, hasNextPage, isError, fetchNextPage]) }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])
useEffect(() => { useEffect(() => {
if (isIOS && enabled && scrollElRef.current) { if (IS_IOS && enabled && scrollElRef.current) {
const nativeTag = findNodeHandle(scrollElRef.current) const nativeTag = findNodeHandle(scrollElRef.current)
setScrollViewTag(nativeTag) 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 {type AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
import {Trans} from '@lingui/macro' import {Trans} from '@lingui/macro'
import {isWeb} from '#/platform/detection'
import {Logo} from '#/view/icons/Logo' import {Logo} from '#/view/icons/Logo'
import {Logotype} from '#/view/icons/Logotype' import {Logotype} from '#/view/icons/Logotype'
import {useTheme} from '#/alf' import {useTheme} from '#/alf'
import {atoms as a} from '#/alf' import {atoms as a} from '#/alf'
import {LinearGradientBackground} from '#/components/LinearGradientBackground' import {LinearGradientBackground} from '#/components/LinearGradientBackground'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
const LazyViewShot = lazy( const LazyViewShot = lazy(
@@ -121,7 +121,7 @@ export function QrCodeInner({link}: {link: string}) {
return ( return (
<View style={{position: 'relative'}}> <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. */} {/* 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 <View
style={{ style={{
position: 'absolute', position: 'absolute',
@@ -139,9 +139,9 @@ export function QrCodeInner({link}: {link: string}) {
a.rounded_sm, a.rounded_sm,
{height: 225, width: 225, backgroundColor: '#f3f3f3'}, {height: 225, width: 225, backgroundColor: '#f3f3f3'},
]} ]}
pieceSize={isWeb ? 8 : 6} pieceSize={IS_WEB ? 8 : 6}
padding={20} padding={20}
pieceBorderRadius={isWeb ? 4.5 : 3.5} pieceBorderRadius={IS_WEB ? 4.5 : 3.5}
outerEyesOptions={{ outerEyesOptions={{
topLeft: { topLeft: {
borderRadius: [12, 12, 0, 12], borderRadius: [12, 12, 0, 12],
@@ -159,11 +159,11 @@ export function QrCodeInner({link}: {link: string}) {
innerEyesOptions={{borderRadius: 3}} innerEyesOptions={{borderRadius: 3}}
logo={{ logo={{
href: require('../../../assets/logo.png'), href: require('../../../assets/logo.png'),
...(isWeb && { ...(IS_WEB && {
onChange: onLogoAreaChange, onChange: onLogoAreaChange,
padding: 28, padding: 28,
}), }),
...(!isWeb && { ...(!IS_WEB && {
padding: 2, padding: 2,
scale: 0.95, scale: 0.95,
}), }),
+6 -6
View File
@@ -9,7 +9,6 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isNative, isWeb} from '#/platform/detection'
import {atoms as a, useBreakpoints} from '#/alf' import {atoms as a, useBreakpoints} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog' 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 {Loader} from '#/components/Loader'
import {QrCode} from '#/components/StarterPack/QrCode' import {QrCode} from '#/components/StarterPack/QrCode'
import * as Toast from '#/components/Toast' import * as Toast from '#/components/Toast'
import {IS_NATIVE, IS_WEB} from '#/env'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
export function QrCodeDialog({ export function QrCodeDialog({
@@ -56,7 +56,7 @@ export function QrCodeDialog({
const onSavePress = async () => { const onSavePress = async () => {
ref.current?.capture?.().then(async (uri: string) => { ref.current?.capture?.().then(async (uri: string) => {
if (isNative) { if (IS_NATIVE) {
const res = await requestMediaLibraryPermissionsAsync() const res = await requestMediaLibraryPermissionsAsync()
if (!res.granted) { if (!res.granted) {
@@ -111,7 +111,7 @@ export function QrCodeDialog({
}) })
setIsSaveProcessing(false) setIsSaveProcessing(false)
Toast.show( Toast.show(
isWeb IS_WEB
? _(msg`QR code has been downloaded!`) ? _(msg`QR code has been downloaded!`)
: _(msg`QR code saved to your camera roll!`), : _(msg`QR code saved to your camera roll!`),
) )
@@ -178,18 +178,18 @@ export function QrCodeDialog({
label={_(msg`Copy QR code`)} label={_(msg`Copy QR code`)}
color="primary_subtle" color="primary_subtle"
size="large" size="large"
onPress={isWeb ? onCopyPress : onSharePress}> onPress={IS_WEB ? onCopyPress : onSharePress}>
<ButtonIcon <ButtonIcon
icon={ icon={
isCopyProcessing isCopyProcessing
? Loader ? Loader
: isWeb : IS_WEB
? ChainLinkIcon ? ChainLinkIcon
: ShareIcon : ShareIcon
} }
/> />
<ButtonText> <ButtonText>
{isWeb ? <Trans>Copy</Trans> : <Trans>Share</Trans>} {IS_WEB ? <Trans>Copy</Trans> : <Trans>Share</Trans>}
</ButtonText> </ButtonText>
</Button> </Button>
<Button <Button
+8 -4
View File
@@ -8,7 +8,6 @@ import {useSaveImageToMediaLibrary} from '#/lib/media/save-image'
import {shareUrl} from '#/lib/sharing' import {shareUrl} from '#/lib/sharing'
import {getStarterPackOgCard} from '#/lib/strings/starter-pack' import {getStarterPackOgCard} from '#/lib/strings/starter-pack'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isNative, isWeb} from '#/platform/detection'
import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {type DialogControlProps} from '#/components/Dialog' 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 {QrCode_Stroke2_Corner0_Rounded as QrCodeIcon} from '#/components/icons/QrCode'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE, IS_WEB} from '#/env'
interface Props { interface Props {
starterPack: AppBskyGraphDefs.StarterPackView starterPack: AppBskyGraphDefs.StarterPackView
@@ -110,13 +110,17 @@ function ShareDialogInner({
], ],
]}> ]}>
<Button <Button
label={isWeb ? _(msg`Copy link`) : _(msg`Share link`)} label={IS_WEB ? _(msg`Copy link`) : _(msg`Share link`)}
color="primary_subtle" color="primary_subtle"
size="large" size="large"
onPress={onShareLink}> onPress={onShareLink}>
<ButtonIcon icon={ChainLinkIcon} /> <ButtonIcon icon={ChainLinkIcon} />
<ButtonText> <ButtonText>
{isWeb ? <Trans>Copy Link</Trans> : <Trans>Share link</Trans>} {IS_WEB ? (
<Trans>Copy Link</Trans>
) : (
<Trans>Share link</Trans>
)}
</ButtonText> </ButtonText>
</Button> </Button>
<Button <Button
@@ -133,7 +137,7 @@ function ShareDialogInner({
<Trans>Share QR code</Trans> <Trans>Share QR code</Trans>
</ButtonText> </ButtonText>
</Button> </Button>
{isNative && ( {IS_NATIVE && (
<Button <Button
label={_(msg`Save image`)} label={_(msg`Save image`)}
color="secondary" color="secondary"
@@ -10,7 +10,6 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {isWeb} from '#/platform/detection'
import {type ListMethods} from '#/view/com/util/List' import {type ListMethods} from '#/view/com/util/List'
import { import {
type WizardAction, type WizardAction,
@@ -24,6 +23,7 @@ import {
WizardProfileCard, WizardProfileCard,
} from '#/components/StarterPack/Wizard/WizardListCard' } from '#/components/StarterPack/Wizard/WizardListCard'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
function keyExtractor( function keyExtractor(
item: AppBskyActorDefs.ProfileViewBasic | AppBskyFeedDefs.GeneratorView, item: AppBskyActorDefs.ProfileViewBasic | AppBskyFeedDefs.GeneratorView,
@@ -95,7 +95,7 @@ export function WizardEditListDialog({
a.mb_sm, a.mb_sm,
t.atoms.bg, t.atoms.bg,
t.atoms.border_contrast_medium, t.atoms.border_contrast_medium,
isWeb IS_WEB
? [ ? [
a.align_center, a.align_center,
{ {
@@ -113,7 +113,7 @@ export function WizardEditListDialog({
)} )}
</Text> </Text>
<View style={{width: 60}}> <View style={{width: 60}}>
{isWeb && ( {IS_WEB && (
<Button <Button
label={_(msg`Close`)} label={_(msg`Close`)}
variant="ghost" variant="ghost"
+3 -3
View File
@@ -1,8 +1,8 @@
import {View} from 'react-native' import {View} from 'react-native'
import {isTouchDevice} from '#/lib/browser' import {isTouchDevice} from '#/lib/browser'
import {isNative, isWeb} from '#/platform/detection'
import {atoms as a, useTheme, type ViewStyleProp} from '#/alf' import {atoms as a, useTheme, type ViewStyleProp} from '#/alf'
import {IS_NATIVE, IS_WEB} from '#/env'
export function SubtleHover({ export function SubtleHover({
style, style,
@@ -39,9 +39,9 @@ export function SubtleHover({
/> />
) )
if (isWeb && web) { if (IS_WEB && web) {
return isTouchDevice ? null : el return isTouchDevice ? null : el
} else if (isNative && native) { } else if (IS_NATIVE && native) {
return el return el
} }
+3 -3
View File
@@ -18,7 +18,6 @@ import {useLingui} from '@lingui/react'
import {HITSLOP_10} from '#/lib/constants' import {HITSLOP_10} from '#/lib/constants'
import {makeListLink, makeProfileLink} from '#/lib/routes/links' import {makeListLink, makeProfileLink} from '#/lib/routes/links'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isNative} from '#/platform/detection'
import { import {
type ThreadgateAllowUISetting, type ThreadgateAllowUISetting,
threadgateViewToAllowUISetting, 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 {Group3_Stroke2_Corner0_Rounded as GroupIcon} from '#/components/icons/Group'
import {InlineLinkText} from '#/components/Link' import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
interface WhoCanReplyProps { interface WhoCanReplyProps {
@@ -86,7 +86,7 @@ export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
: _(msg`Some people can reply`) : _(msg`Some people can reply`)
const onPressOpen = () => { const onPressOpen = () => {
if (isNative && Keyboard.isVisible()) { if (IS_NATIVE && Keyboard.isVisible()) {
Keyboard.dismiss() Keyboard.dismiss()
} }
if (isThreadAuthor) { if (isThreadAuthor) {
@@ -229,7 +229,7 @@ function WhoCanReplyDialog({
embeddingDisabled={embeddingDisabled} embeddingDisabled={embeddingDisabled}
/> />
</View> </View>
{isNative && ( {IS_NATIVE && (
<Button <Button
label={_(msg`Close`)} label={_(msg`Close`)}
onPress={() => control.close()} onPress={() => control.close()}
@@ -18,7 +18,6 @@ import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-disp
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {updateProfileShadow} from '#/state/cache/profile-shadow' import {updateProfileShadow} from '#/state/cache/profile-shadow'
import {RQKEY_getActivitySubscriptions} from '#/state/queries/activity-subscriptions' import {RQKEY_getActivitySubscriptions} from '#/state/queries/activity-subscriptions'
import {useAgent} from '#/state/session' import {useAgent} from '#/state/session'
@@ -37,6 +36,7 @@ import * as Toggle from '#/components/forms/Toggle'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import * as ProfileCard from '#/components/ProfileCard' import * as ProfileCard from '#/components/ProfileCard'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import type * as bsky from '#/types/bsky' import type * as bsky from '#/types/bsky'
export function SubscribeProfileDialog({ export function SubscribeProfileDialog({
@@ -195,7 +195,7 @@ function DialogInner({
} }
} else { } else {
// on web, a disabled save button feels more natural than a massive close button // on web, a disabled save button feels more natural than a massive close button
if (isWeb) { if (IS_WEB) {
return { return {
label: _(msg`Save changes`), label: _(msg`Save changes`),
color: 'secondary', color: 'secondary',
@@ -3,7 +3,6 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo' import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
import {isNative} from '#/platform/detection'
import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf' import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf'
import {Admonition} from '#/components/Admonition' import {Admonition} from '#/components/Admonition'
import {AgeAssuranceAppealDialog} from '#/components/ageAssurance/AgeAssuranceAppealDialog' import {AgeAssuranceAppealDialog} from '#/components/ageAssurance/AgeAssuranceAppealDialog'
@@ -23,6 +22,7 @@ import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {logger, useAgeAssurance} from '#/ageAssurance' import {logger, useAgeAssurance} from '#/ageAssurance'
import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess' import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess'
import {IS_NATIVE} from '#/env'
import {useDeviceGeolocationApi} from '#/geolocation' import {useDeviceGeolocationApi} from '#/geolocation'
export function AgeAssuranceAccountCard({style}: ViewStyleProp & {}) { export function AgeAssuranceAccountCard({style}: ViewStyleProp & {}) {
@@ -86,7 +86,7 @@ function Inner({style}: ViewStyleProp & {}) {
<View style={[a.pb_md, a.gap_xs]}> <View style={[a.pb_md, a.gap_xs]}>
<Text style={[a.text_sm, a.leading_snug]}>{copy.notice}</Text> <Text style={[a.text_sm, a.leading_snug]}>{copy.notice}</Text>
{isNative && ( {IS_NATIVE && (
<> <>
<Text style={[a.text_sm, a.leading_snug]}> <Text style={[a.text_sm, a.leading_snug]}>
<Trans> <Trans>
@@ -5,7 +5,6 @@ import {useLingui} from '@lingui/react'
import {retry} from '#/lib/async/retry' import {retry} from '#/lib/async/retry'
import {wait} from '#/lib/async/wait' import {wait} from '#/lib/async/wait'
import {isNative} from '#/platform/detection'
import {useAgent} from '#/state/session' import {useAgent} from '#/state/session'
import {atoms as a, useTheme, web} from '#/alf' import {atoms as a, useTheme, web} from '#/alf'
import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge' import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge'
@@ -18,6 +17,7 @@ import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {refetchAgeAssuranceServerState} from '#/ageAssurance' import {refetchAgeAssuranceServerState} from '#/ageAssurance'
import {logger} from '#/ageAssurance' import {logger} from '#/ageAssurance'
import {IS_NATIVE} from '#/env'
export type AgeAssuranceRedirectDialogState = { export type AgeAssuranceRedirectDialogState = {
result: 'success' | 'unknown' result: 'success' | 'unknown'
@@ -166,7 +166,7 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
</Trans> </Trans>
</Text> </Text>
{isNative && ( {IS_NATIVE && (
<View style={[a.w_full, a.pt_lg]}> <View style={[a.w_full, a.pt_lg]}>
<Button <Button
label={_(msg`Close`)} label={_(msg`Close`)}
@@ -225,7 +225,7 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
)} )}
</Text> </Text>
{error && isNative && ( {error && IS_NATIVE && (
<View style={[a.w_full, a.pt_lg]}> <View style={[a.w_full, a.pt_lg]}>
<Button <Button
label={_(msg`Close`)} label={_(msg`Close`)}
@@ -8,12 +8,12 @@ import {useLingui} from '@lingui/react'
import {HITSLOP_10} from '#/lib/constants' import {HITSLOP_10} from '#/lib/constants'
import {useGate} from '#/lib/statsig/statsig' import {useGate} from '#/lib/statsig/statsig'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs' import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button' import {Button} from '#/components/Button'
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times' import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import {Link} from '../Link' import {Link} from '../Link'
import {useIsFindContactsFeatureEnabledBasedOnGeolocation} from './country-allowlist' import {useIsFindContactsFeatureEnabledBasedOnGeolocation} from './country-allowlist'
@@ -92,7 +92,7 @@ function useInternalState() {
const gate = useGate() const gate = useGate()
const visible = useMemo(() => { const visible = useMemo(() => {
if (isWeb) return false if (IS_WEB) return false
if (hidden) return false if (hidden) return false
if (nux && nux.completed) return false if (nux && nux.completed) return false
if (!isFeatureEnabled) return false if (!isFeatureEnabled) return false
@@ -9,10 +9,10 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {mergeRefs} from '#/lib/merge-refs' import {mergeRefs} from '#/lib/merge-refs'
import {isAndroid, isIOS} from '#/platform/detection'
import {atoms as a, ios, platform, useTheme} from '#/alf' import {atoms as a, ios, platform, useTheme} from '#/alf'
import {useInteractionState} from '#/components/hooks/useInteractionState' import {useInteractionState} from '#/components/hooks/useInteractionState'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_ANDROID, IS_IOS} from '#/env'
export function OTPInput({ export function OTPInput({
label, label,
@@ -95,7 +95,7 @@ export function OTPInput({
<TextInput <TextInput
// SMS autofill is borked on iOS if you open the keyboard immediately -sfn // SMS autofill is borked on iOS if you open the keyboard immediately -sfn
onLayout={ios(() => setTimeout(() => innerRef.current?.focus(), 100))} onLayout={ios(() => setTimeout(() => innerRef.current?.focus(), 100))}
autoFocus={isAndroid} autoFocus={IS_ANDROID}
accessible accessible
accessibilityLabel={label} accessibilityLabel={label}
accessibilityHint="" accessibilityHint=""
@@ -135,7 +135,7 @@ export function OTPInput({
android: {opacity: 0}, android: {opacity: 0},
}), }),
]} ]}
caretHidden={isIOS} caretHidden={IS_IOS}
clearTextOnFocus clearTextOnFocus
/> />
</Pressable> </Pressable>
+3 -3
View File
@@ -7,7 +7,6 @@ import {useCleanError} from '#/lib/hooks/useCleanError'
import {isAppPassword} from '#/lib/jwt' import {isAppPassword} from '#/lib/jwt'
import {getAge, getDateAgo} from '#/lib/strings/time' import {getAge, getDateAgo} from '#/lib/strings/time'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isIOS, isWeb} from '#/platform/detection'
import { import {
useBirthdateMutation, useBirthdateMutation,
useIsBirthdateUpdateAllowed, useIsBirthdateUpdateAllowed,
@@ -26,6 +25,7 @@ import {DateField} from '#/components/forms/DateField'
import {SimpleInlineLinkText} from '#/components/Link' import {SimpleInlineLinkText} from '#/components/Link'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Span, Text} from '#/components/Typography' import {Span, Text} from '#/components/Typography'
import {IS_IOS, IS_WEB} from '#/env'
export function BirthDateSettingsDialog({ export function BirthDateSettingsDialog({
control, control,
@@ -154,7 +154,7 @@ function BirthdayInner({
return ( return (
<View style={a.gap_lg} testID="birthDateSettingsDialog"> <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 <DateField
testID="birthdayInput" testID="birthdayInput"
value={date} value={date}
@@ -191,7 +191,7 @@ function BirthdayInner({
<ErrorMessage message={errorMessage} style={[a.rounded_sm]} /> <ErrorMessage message={errorMessage} style={[a.rounded_sm]} />
) : undefined} ) : undefined}
<View style={isWeb && [a.flex_row, a.justify_end]}> <View style={IS_WEB && [a.flex_row, a.justify_end]}>
<Button <Button
label={hasChanged ? _(msg`Save birthdate`) : _(msg`Done`)} label={hasChanged ? _(msg`Save birthdate`) : _(msg`Done`)}
size="large" size="large"
@@ -6,7 +6,6 @@ import {useLingui} from '@lingui/react'
import {wait} from '#/lib/async/wait' import {wait} from '#/lib/async/wait'
import {isNetworkError, useCleanError} from '#/lib/hooks/useCleanError' import {isNetworkError, useCleanError} from '#/lib/hooks/useCleanError'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {atoms as a, useTheme, web} from '#/alf' import {atoms as a, useTheme, web} from '#/alf'
import {Admonition} from '#/components/Admonition' import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button' 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 {PinLocation_Stroke2_Corner0_Rounded as LocationIcon} from '#/components/icons/PinLocation'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import {type Geolocation, useRequestDeviceGeolocation} from '#/geolocation' import {type Geolocation, useRequestDeviceGeolocation} from '#/geolocation'
export type Props = { export type Props = {
@@ -138,7 +138,7 @@ function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) {
disabled={isRequesting} disabled={isRequesting}
label={_(msg`Allow location access`)} label={_(msg`Allow location access`)}
onPress={onPressConfirm} onPress={onPressConfirm}
size={isWeb ? 'small' : 'large'} size={IS_WEB ? 'small' : 'large'}
color="primary"> color="primary">
<ButtonIcon icon={isRequesting ? Loader : LocationIcon} /> <ButtonIcon icon={isRequesting ? Loader : LocationIcon} />
<ButtonText> <ButtonText>
@@ -147,11 +147,11 @@ function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) {
</Button> </Button>
)} )}
{!isWeb && ( {!IS_WEB && (
<Button <Button
label={_(msg`Cancel`)} label={_(msg`Cancel`)}
onPress={() => close()} onPress={() => close()}
size={isWeb ? 'small' : 'large'} size={IS_WEB ? 'small' : 'large'}
color="secondary"> color="secondary">
<ButtonText> <ButtonText>
<Trans>Cancel</Trans> <Trans>Cancel</Trans>
+3 -3
View File
@@ -13,7 +13,6 @@ import {useLingui} from '@lingui/react'
import {logEvent} from '#/lib/statsig/statsig' import {logEvent} from '#/lib/statsig/statsig'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {isWeb} from '#/platform/detection'
import { import {
type Gif, type Gif,
tenorUrlToBskyGifUrl, tenorUrlToBskyGifUrl,
@@ -31,6 +30,7 @@ import {useThrottledValue} from '#/components/hooks/useThrottledValue'
import {ArrowLeft_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arrow' import {ArrowLeft_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arrow'
import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass' import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists' import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
import {IS_WEB} from '#/env'
export function GifSelectDialog({ export function GifSelectDialog({
controlRef, controlRef,
@@ -149,7 +149,7 @@ function GifList({
a.pb_sm, a.pb_sm,
t.atoms.bg, t.atoms.bg,
]}> ]}>
{!gtMobile && isWeb && ( {!gtMobile && IS_WEB && (
<Button <Button
size="small" size="small"
variant="ghost" variant="ghost"
@@ -161,7 +161,7 @@ function GifList({
</Button> </Button>
)} )}
<TextField.Root style={[!gtMobile && isWeb && a.flex_1]}> <TextField.Root style={[!gtMobile && IS_WEB && a.flex_1]}>
<TextField.Icon icon={Search} /> <TextField.Icon icon={Search} />
<TextField.Input <TextField.Input
label={_(msg`Search GIFs`)} label={_(msg`Search GIFs`)}
@@ -4,19 +4,19 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useOpenLink} from '#/lib/hooks/useOpenLink' import {useOpenLink} from '#/lib/hooks/useOpenLink'
import {isWeb} from '#/platform/detection'
import {useSetInAppBrowser} from '#/state/preferences/in-app-browser' import {useSetInAppBrowser} from '#/state/preferences/in-app-browser'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
import {SquareArrowTopRight_Stroke2_Corner0_Rounded as External} from '#/components/icons/SquareArrowTopRight' import {SquareArrowTopRight_Stroke2_Corner0_Rounded as External} from '#/components/icons/SquareArrowTopRight'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import {useGlobalDialogsControlContext} from './Context' import {useGlobalDialogsControlContext} from './Context'
export function InAppBrowserConsentDialog() { export function InAppBrowserConsentDialog() {
const {inAppBrowserConsentControl} = useGlobalDialogsControlContext() const {inAppBrowserConsentControl} = useGlobalDialogsControlContext()
if (isWeb) return null if (IS_WEB) return null
return ( return (
<Dialog.Outer <Dialog.Outer
+2 -2
View File
@@ -5,7 +5,6 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isNative} from '#/platform/detection'
import { import {
usePreferencesQuery, usePreferencesQuery,
useRemoveMutedWordMutation, useRemoveMutedWordMutation,
@@ -32,6 +31,7 @@ import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
const ONE_DAY = 24 * 60 * 60 * 1000 const ONE_DAY = 24 * 60 * 60 * 1000
@@ -406,7 +406,7 @@ function MutedWordsInner() {
)} )}
</View> </View>
{isNative && <View style={{height: 20}} />} {IS_NATIVE && <View style={{height: 20}} />}
</View> </View>
<Dialog.Close /> <Dialog.Close />
@@ -12,7 +12,6 @@ import {useQueryClient} from '@tanstack/react-query'
import {useHaptics} from '#/lib/haptics' import {useHaptics} from '#/lib/haptics'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isIOS} from '#/platform/detection'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
import {useMyListsQuery} from '#/state/queries/my-lists' import {useMyListsQuery} from '#/state/queries/my-lists'
import {useGetPost} from '#/state/queries/post' 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 {CloseQuote_Stroke2_Corner1_Rounded as QuoteIcon} from '#/components/icons/Quote'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_IOS} from '#/env'
export type PostInteractionSettingsFormProps = { export type PostInteractionSettingsFormProps = {
canSave?: boolean canSave?: boolean
@@ -531,7 +531,7 @@ export function PostInteractionSettingsForm({
hitSlop={0} hitSlop={0}
onPress={() => { onPress={() => {
playHaptic('Light') playHaptic('Light')
if (isIOS && !showLists) { if (IS_IOS && !showLists) {
LayoutAnimation.configureNext({ LayoutAnimation.configureNext({
...LayoutAnimation.Presets.linear, ...LayoutAnimation.Presets.linear,
duration: 175, duration: 175,
@@ -13,7 +13,6 @@ import {useLingui} from '@lingui/react'
import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
import {isWeb} from '#/platform/detection'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete' import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
import {useListConvosQuery} from '#/state/queries/messages/list-conversations' 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 {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import * as ProfileCard from '#/components/ProfileCard' import * as ProfileCard from '#/components/ProfileCard'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import type * as bsky from '#/types/bsky' import type * as bsky from '#/types/bsky'
export type ProfileItem = { export type ProfileItem = {
@@ -254,7 +254,7 @@ export function SearchablePeopleList({
) )
useLayoutEffect(() => { useLayoutEffect(() => {
if (isWeb) { if (IS_WEB) {
setImmediate(() => { setImmediate(() => {
inputRef?.current?.focus() inputRef?.current?.focus()
}) })
@@ -290,12 +290,12 @@ export function SearchablePeopleList({
]}> ]}>
{title} {title}
</Text> </Text>
{isWeb ? ( {IS_WEB ? (
<Button <Button
label={_(msg`Close`)} label={_(msg`Close`)}
size="small" size="small"
shape="round" shape="round"
variant={isWeb ? 'ghost' : 'solid'} variant={IS_WEB ? 'ghost' : 'solid'}
color="secondary" color="secondary"
style={[ style={[
a.absolute, a.absolute,
+3 -3
View File
@@ -3,7 +3,6 @@ import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {isNative} from '#/platform/detection'
import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import {useCloseAllActiveElements} from '#/state/util' import {useCloseAllActiveElements} from '#/state/util'
import {Logo} from '#/view/icons/Logo' import {Logo} from '#/view/icons/Logo'
@@ -13,6 +12,7 @@ import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
export function SigninDialog() { export function SigninDialog() {
const {signinDialogControl: control} = useGlobalDialogsControlContext() const {signinDialogControl: control} = useGlobalDialogsControlContext()
@@ -45,7 +45,7 @@ function SigninDialogInner({}: {control: Dialog.DialogOuterProps['control']}) {
<Dialog.ScrollableInner <Dialog.ScrollableInner
label={_(msg`Sign in to Bluesky or create a new account`)} label={_(msg`Sign in to Bluesky or create a new account`)}
style={[gtMobile ? {width: 'auto', maxWidth: 420} : a.w_full]}> style={[gtMobile ? {width: 'auto', maxWidth: 420} : a.w_full]}>
<View style={[!isNative && a.p_2xl]}> <View style={[!IS_NATIVE && a.p_2xl]}>
<View <View
style={[ style={[
a.flex_row, a.flex_row,
@@ -101,7 +101,7 @@ function SigninDialogInner({}: {control: Dialog.DialogOuterProps['control']}) {
</Button> </Button>
</View> </View>
{isNative && <View style={{height: 10}} />} {IS_NATIVE && <View style={{height: 10}} />}
</View> </View>
<Dialog.Close /> <Dialog.Close />
+4 -4
View File
@@ -12,7 +12,6 @@ import {useQueryClient} from '@tanstack/react-query'
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import { import {
invalidateActorStarterPacksWithMembershipQuery, invalidateActorStarterPacksWithMembershipQuery,
useActorStarterPacksWithMembershipsQuery, useActorStarterPacksWithMembershipsQuery,
@@ -32,6 +31,7 @@ import {StarterPack} from '#/components/icons/StarterPack'
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times' import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
type StarterPackWithMembership = type StarterPackWithMembership =
@@ -91,7 +91,7 @@ function Empty({onStartWizard}: {onStartWizard: () => void}) {
const t = useTheme() const t = useTheme()
return ( 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]}> <View style={[a.gap_xs, a.align_center]}>
<StarterPack <StarterPack
width={48} width={48}
@@ -169,7 +169,7 @@ function StarterPackList({
<View <View
style={[ style={[
{justifyContent: 'space-between', flexDirection: 'row'}, {justifyContent: 'space-between', flexDirection: 'row'},
isWeb ? a.mb_2xl : a.my_lg, IS_WEB ? a.mb_2xl : a.my_lg,
a.align_center, a.align_center,
]}> ]}>
<Text style={[a.text_lg, a.font_semi_bold]}> <Text style={[a.text_lg, a.font_semi_bold]}>
@@ -232,7 +232,7 @@ function StarterPackList({
onEndReachedThreshold={0.1} onEndReachedThreshold={0.1}
ListHeaderComponent={listHeader} ListHeaderComponent={listHeader}
ListEmptyComponent={<Empty onStartWizard={onStartWizard} />} 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 {richTextToString} from '#/lib/strings/rich-text-helpers'
import {shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip' import {shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {type ImageMeta} from '#/state/gallery' import {type ImageMeta} from '#/state/gallery'
import { import {
useListCreateMutation, useListCreateMutation,
@@ -26,6 +25,7 @@ import * as TextField from '#/components/forms/TextField'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
const DISPLAY_NAME_MAX_GRAPHEMES = 64 const DISPLAY_NAME_MAX_GRAPHEMES = 64
const DESCRIPTION_MAX_GRAPHEMES = 300 const DESCRIPTION_MAX_GRAPHEMES = 300
@@ -48,7 +48,7 @@ export function CreateOrEditListDialog({
// 'You might lose unsaved changes' warning // 'You might lose unsaved changes' warning
useEffect(() => { useEffect(() => {
if (isWeb && dirty) { if (IS_WEB && dirty) {
const abortController = new AbortController() const abortController = new AbortController()
const {signal} = abortController const {signal} = abortController
window.addEventListener('beforeunload', evt => evt.preventDefault(), { window.addEventListener('beforeunload', evt => evt.preventDefault(), {
@@ -4,13 +4,13 @@ import {Image} from 'expo-image'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {isWeb} from '#/platform/detection'
import {atoms as a, useTheme, web} from '#/alf' import {atoms as a, useTheme, web} from '#/alf'
import {Button, ButtonText} from '#/components/Button' import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
import {useNuxDialogContext} from '#/components/dialogs/nuxs' import {useNuxDialogContext} from '#/components/dialogs/nuxs'
import {Sparkle_Stroke2_Corner0_Rounded as SparkleIcon} from '#/components/icons/Sparkle' import {Sparkle_Stroke2_Corner0_Rounded as SparkleIcon} from '#/components/icons/Sparkle'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
export function ActivitySubscriptionsNUX() { export function ActivitySubscriptionsNUX() {
const t = useTheme() const t = useTheme()
@@ -44,8 +44,8 @@ export function ActivitySubscriptionsNUX() {
a.overflow_hidden, a.overflow_hidden,
t.atoms.bg_contrast_25, t.atoms.bg_contrast_25,
{ {
gap: isWeb ? 16 : 24, gap: IS_WEB ? 16 : 24,
paddingTop: isWeb ? 24 : 48, paddingTop: IS_WEB ? 24 : 48,
borderTopLeftRadius: a.rounded_md.borderRadius, borderTopLeftRadius: a.rounded_md.borderRadius,
borderTopRightRadius: a.rounded_md.borderRadius, borderTopRightRadius: a.rounded_md.borderRadius,
}, },
@@ -120,7 +120,7 @@ export function ActivitySubscriptionsNUX() {
style={[ style={[
a.align_center, a.align_center,
a.px_xl, 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]}> <View style={[a.gap_md, a.align_center]}>
<Text <Text
@@ -130,7 +130,7 @@ export function ActivitySubscriptionsNUX() {
a.font_bold, a.font_bold,
a.text_center, a.text_center,
{ {
fontSize: isWeb ? 28 : 32, fontSize: IS_WEB ? 28 : 32,
maxWidth: 300, maxWidth: 300,
}, },
]}> ]}>
@@ -153,7 +153,7 @@ export function ActivitySubscriptionsNUX() {
</Text> </Text>
</View> </View>
{!isWeb && ( {!IS_WEB && (
<Button <Button
label={_(msg`Close`)} label={_(msg`Close`)}
size="large" size="large"
@@ -5,7 +5,6 @@ import {LinearGradient} from 'expo-linear-gradient'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {isWeb} from '#/platform/detection'
import {atoms as a, useTheme, web} from '#/alf' import {atoms as a, useTheme, web} from '#/alf'
import {transparentifyColor} from '#/alf/util/colorGeneration' import {transparentifyColor} from '#/alf/util/colorGeneration'
import {Button, ButtonText} from '#/components/Button' import {Button, ButtonText} from '#/components/Button'
@@ -13,6 +12,7 @@ import * as Dialog from '#/components/Dialog'
import {useNuxDialogContext} from '#/components/dialogs/nuxs' import {useNuxDialogContext} from '#/components/dialogs/nuxs'
import {Sparkle_Stroke2_Corner0_Rounded as SparkleIcon} from '#/components/icons/Sparkle' import {Sparkle_Stroke2_Corner0_Rounded as SparkleIcon} from '#/components/icons/Sparkle'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
export function BookmarksAnnouncement() { export function BookmarksAnnouncement() {
const t = useTheme() const t = useTheme()
@@ -49,7 +49,7 @@ export function BookmarksAnnouncement() {
a.overflow_hidden, a.overflow_hidden,
{ {
gap: 16, gap: 16,
paddingTop: isWeb ? 24 : 40, paddingTop: IS_WEB ? 24 : 40,
borderTopLeftRadius: a.rounded_md.borderRadius, borderTopLeftRadius: a.rounded_md.borderRadius,
borderTopRightRadius: a.rounded_md.borderRadius, borderTopRightRadius: a.rounded_md.borderRadius,
}, },
@@ -90,7 +90,7 @@ export function BookmarksAnnouncement() {
borderRadius: 24, borderRadius: 24,
aspectRatio: 333 / 104, aspectRatio: 333 / 104,
}, },
isWeb IS_WEB
? [ ? [
{ {
boxShadow: `0px 10px 15px -3px ${transparentifyColor(t.palette.black, 0.2)}`, boxShadow: `0px 10px 15px -3px ${transparentifyColor(t.palette.black, 0.2)}`,
@@ -136,7 +136,7 @@ export function BookmarksAnnouncement() {
a.font_bold, a.font_bold,
a.text_center, a.text_center,
{ {
fontSize: isWeb ? 28 : 32, fontSize: IS_WEB ? 28 : 32,
maxWidth: 300, maxWidth: 300,
}, },
]}> ]}>
@@ -158,7 +158,7 @@ export function BookmarksAnnouncement() {
</Text> </Text>
</View> </View>
{!isWeb && ( {!IS_WEB && (
<Button <Button
label={_(msg`Close`)} label={_(msg`Close`)}
size="large" size="large"
@@ -6,7 +6,6 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isNative, isWeb} from '#/platform/detection'
import {atoms as a, useTheme, web} from '#/alf' import {atoms as a, useTheme, web} from '#/alf'
import {Button, ButtonText} from '#/components/Button' import {Button, ButtonText} from '#/components/Button'
import {isFindContactsFeatureEnabled} from '#/components/contacts/country-allowlist' import {isFindContactsFeatureEnabled} from '#/components/contacts/country-allowlist'
@@ -17,13 +16,14 @@ import {
isExistingUserAsOf, isExistingUserAsOf,
} from '#/components/dialogs/nuxs/utils' } from '#/components/dialogs/nuxs/utils'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE, IS_WEB} from '#/env'
import {IS_E2E} from '#/env' import {IS_E2E} from '#/env'
import {navigate} from '#/Navigation' import {navigate} from '#/Navigation'
export const enabled = createIsEnabledCheck(props => { export const enabled = createIsEnabledCheck(props => {
return ( return (
!IS_E2E && !IS_E2E &&
isNative && IS_NATIVE &&
isExistingUserAsOf( isExistingUserAsOf(
'2025-12-16T00:00:00.000Z', '2025-12-16T00:00:00.000Z',
props.currentProfile.createdAt, props.currentProfile.createdAt,
@@ -89,7 +89,7 @@ export function FindContactsAnnouncement() {
a.font_bold, a.font_bold,
a.text_center, a.text_center,
{ {
fontSize: isWeb ? 28 : 32, fontSize: IS_WEB ? 28 : 32,
maxWidth: 300, maxWidth: 300,
}, },
]}> ]}>
@@ -6,7 +6,6 @@ import {useLingui} from '@lingui/react'
import {urls} from '#/lib/constants' import {urls} from '#/lib/constants'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isNative} from '#/platform/detection'
import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button' import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog' 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 {VerifierCheck} from '#/components/icons/VerifierCheck'
import {Link} from '#/components/Link' import {Link} from '#/components/Link'
import {Span, Text} from '#/components/Typography' import {Span, Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
export function InitialVerificationAnnouncement() { export function InitialVerificationAnnouncement() {
const t = useTheme() const t = useTheme()
@@ -173,7 +173,7 @@ export function InitialVerificationAnnouncement() {
<Trans>Read blog post</Trans> <Trans>Read blog post</Trans>
</ButtonText> </ButtonText>
</Link> </Link>
{isNative && ( {IS_NATIVE && (
<Button <Button
label={_(msg`Close`)} label={_(msg`Close`)}
size="small" size="small"
@@ -5,7 +5,6 @@ import {LinearGradient} from 'expo-linear-gradient'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {isWeb} from '#/platform/detection'
import {atoms as a, select, useTheme, utils, web} from '#/alf' import {atoms as a, select, useTheme, utils, web} from '#/alf'
import {Button, ButtonText} from '#/components/Button' import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
@@ -16,6 +15,7 @@ import {
} from '#/components/dialogs/nuxs/utils' } from '#/components/dialogs/nuxs/utils'
import {Beaker_Stroke2_Corner2_Rounded as BeakerIcon} from '#/components/icons/Beaker' import {Beaker_Stroke2_Corner2_Rounded as BeakerIcon} from '#/components/icons/Beaker'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import {IS_E2E} from '#/env' import {IS_E2E} from '#/env'
export const enabled = createIsEnabledCheck(props => { export const enabled = createIsEnabledCheck(props => {
@@ -72,7 +72,7 @@ export function LiveNowBetaDialog() {
a.overflow_hidden, a.overflow_hidden,
{ {
gap: 16, gap: 16,
paddingTop: isWeb ? 24 : 40, paddingTop: IS_WEB ? 24 : 40,
borderTopLeftRadius: a.rounded_md.borderRadius, borderTopLeftRadius: a.rounded_md.borderRadius,
borderTopRightRadius: a.rounded_md.borderRadius, borderTopRightRadius: a.rounded_md.borderRadius,
}, },
@@ -116,7 +116,7 @@ export function LiveNowBetaDialog() {
borderRadius: 24, borderRadius: 24,
aspectRatio: 652 / 211, aspectRatio: 652 / 211,
}, },
isWeb IS_WEB
? [ ? [
{ {
boxShadow: `0px 10px 15px -3px ${shadowColor}`, boxShadow: `0px 10px 15px -3px ${shadowColor}`,
@@ -163,7 +163,7 @@ export function LiveNowBetaDialog() {
a.font_bold, a.font_bold,
a.text_center, a.text_center,
{ {
fontSize: isWeb ? 28 : 32, fontSize: IS_WEB ? 28 : 32,
maxWidth: 360, maxWidth: 360,
}, },
]}> ]}>
@@ -186,7 +186,7 @@ export function LiveNowBetaDialog() {
</Text> </Text>
</View> </View>
{!isWeb && ( {!IS_WEB && (
<Button <Button
label={_(msg`Close`)} label={_(msg`Close`)}
size="large" size="large"
+1 -1
View File
@@ -21,7 +21,7 @@ export function ActionsWrapper({
<MessageContextMenu message={message}> <MessageContextMenu message={message}>
{trigger => {trigger =>
// will always be true, since this file is platform split // will always be true, since this file is platform split
trigger.isNative && ( trigger.IS_NATIVE && (
<View style={[a.flex_1, a.relative]}> <View style={[a.flex_1, a.relative]}>
<View <View
style={[ style={[
+4 -4
View File
@@ -89,9 +89,9 @@ export function ActionsWrapper({
: [a.ml_xs, {marginRight: 'auto'}], : [a.ml_xs, {marginRight: 'auto'}],
]}> ]}>
<EmojiReactionPicker message={message} onEmojiSelect={onEmojiSelect}> <EmojiReactionPicker message={message} onEmojiSelect={onEmojiSelect}>
{({props, state, isNative, control}) => { {({props, state, IS_NATIVE, control}) => {
// always false, file is platform split // always false, file is platform split
if (isNative) return null if (IS_NATIVE) return null
const showMenuTrigger = showActions || control.isOpen ? 1 : 0 const showMenuTrigger = showActions || control.isOpen ? 1 : 0
return ( return (
<Pressable <Pressable
@@ -111,9 +111,9 @@ export function ActionsWrapper({
}} }}
</EmojiReactionPicker> </EmojiReactionPicker>
<MessageContextMenu message={message}> <MessageContextMenu message={message}>
{({props, state, isNative, control}) => { {({props, state, IS_NATIVE, control}) => {
// always false, file is platform split // always false, file is platform split
if (isNative) return null if (IS_NATIVE) return null
const showMenuTrigger = showActions || control.isOpen ? 1 : 0 const showMenuTrigger = showActions || control.isOpen ? 1 : 0
return ( return (
<Pressable <Pressable
+2 -2
View File
@@ -7,7 +7,6 @@ import {StackActions, useNavigation} from '@react-navigation/native'
import type React from 'react' import type React from 'react'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {isNative} from '#/platform/detection'
import {useProfileShadow} from '#/state/cache/profile-shadow' import {useProfileShadow} from '#/state/cache/profile-shadow'
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
import { import {
@@ -21,6 +20,7 @@ import * as Dialog from '#/components/Dialog'
import * as Toggle from '#/components/forms/Toggle' import * as Toggle from '#/components/forms/Toggle'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
type ReportDialogParams = { type ReportDialogParams = {
convoId: string convoId: string
@@ -130,7 +130,7 @@ function DoneStep({
onMutate: () => { onMutate: () => {
if (currentScreen === 'conversation') { if (currentScreen === 'conversation') {
navigation.dispatch( 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 {ScaleAndFadeIn} from '#/lib/custom-animations/ScaleAndFade'
import {ShrinkAndPop} from '#/lib/custom-animations/ShrinkAndPop' import {ShrinkAndPop} from '#/lib/custom-animations/ShrinkAndPop'
import {useHaptics} from '#/lib/haptics' import {useHaptics} from '#/lib/haptics'
import {isWeb} from '#/platform/detection'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
const AnimatedPressable = Animated.createAnimatedComponent(Pressable) const AnimatedPressable = Animated.createAnimatedComponent(Pressable)
@@ -41,12 +41,12 @@ export function ChatEmptyPill() {
}, [_]) }, [_])
const onPressIn = React.useCallback(() => { const onPressIn = React.useCallback(() => {
if (isWeb) return if (IS_WEB) return
scale.set(() => withTiming(1.075, {duration: 100})) scale.set(() => withTiming(1.075, {duration: 100}))
}, [scale]) }, [scale])
const onPressOut = React.useCallback(() => { const onPressOut = React.useCallback(() => {
if (isWeb) return if (IS_WEB) return
scale.set(() => withTiming(1, {duration: 100})) scale.set(() => withTiming(1, {duration: 100}))
}, [scale]) }, [scale])
+2 -2
View File
@@ -3,11 +3,11 @@ import {useLingui} from '@lingui/react'
import {StackActions, useNavigation} from '@react-navigation/native' import {StackActions, useNavigation} from '@react-navigation/native'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {isNative} from '#/platform/detection'
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {type DialogOuterProps} from '#/components/Dialog' import {type DialogOuterProps} from '#/components/Dialog'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import {IS_NATIVE} from '#/env'
export function LeaveConvoPrompt({ export function LeaveConvoPrompt({
control, control,
@@ -27,7 +27,7 @@ export function LeaveConvoPrompt({
onMutate: () => { onMutate: () => {
if (currentScreen === 'conversation') { if (currentScreen === 'conversation') {
navigation.dispatch( 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 {useTranslate} from '#/lib/hooks/useTranslate'
import {richTextToString} from '#/lib/strings/rich-text-helpers' import {richTextToString} from '#/lib/strings/rich-text-helpers'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isNative} from '#/platform/detection'
import {useConvoActive} from '#/state/messages/convo' import {useConvoActive} from '#/state/messages/convo'
import {useLanguagePrefs} from '#/state/preferences' import {useLanguagePrefs} from '#/state/preferences'
import {useSession} from '#/state/session' 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 {ReportDialog} from '#/components/moderation/ReportDialog'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import {usePromptControl} from '#/components/Prompt' import {usePromptControl} from '#/components/Prompt'
import {IS_NATIVE} from '#/env'
import {EmojiReactionPicker} from './EmojiReactionPicker' import {EmojiReactionPicker} from './EmojiReactionPicker'
import {hasReachedReactionLimit} from './util' import {hasReachedReactionLimit} from './util'
@@ -112,7 +112,7 @@ export let MessageContextMenu = ({
return ( return (
<> <>
<ContextMenu.Root> <ContextMenu.Root>
{isNative && ( {IS_NATIVE && (
<ContextMenu.AuxiliaryView align={isFromSelf ? 'right' : 'left'}> <ContextMenu.AuxiliaryView align={isFromSelf ? 'right' : 'left'}>
<EmojiReactionPicker <EmojiReactionPicker
message={message} message={message}
+3 -3
View File
@@ -21,7 +21,6 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {isNative} from '#/platform/detection'
import {useConvoActive} from '#/state/messages/convo' import {useConvoActive} from '#/state/messages/convo'
import {type ConvoItem} from '#/state/messages/convo/types' import {type ConvoItem} from '#/state/messages/convo/types'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
@@ -32,6 +31,7 @@ import {ActionsWrapper} from '#/components/dms/ActionsWrapper'
import {InlineLinkText} from '#/components/Link' import {InlineLinkText} from '#/components/Link'
import {RichText} from '#/components/RichText' import {RichText} from '#/components/RichText'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
import {DateDivider} from './DateDivider' import {DateDivider} from './DateDivider'
import {MessageItemEmbed} from './MessageItemEmbed' import {MessageItemEmbed} from './MessageItemEmbed'
import {localDateString} from './util' import {localDateString} from './util'
@@ -218,10 +218,10 @@ let MessageItem = ({
</View> </View>
)} )}
{isNative && appliedReactions} {IS_NATIVE && appliedReactions}
</ActionsWrapper> </ActionsWrapper>
{!isNative && appliedReactions} {!IS_NATIVE && appliedReactions}
{isLastInGroup && ( {isLastInGroup && (
<MessageItemMetadata <MessageItemMetadata
+2 -2
View File
@@ -10,7 +10,6 @@ import {useLingui} from '@lingui/react'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {isWeb} from '#/platform/detection'
import {type Shadow} from '#/state/cache/profile-shadow' import {type Shadow} from '#/state/cache/profile-shadow'
import {isConvoActive, useConvo} from '#/state/messages/convo' import {isConvoActive, useConvo} from '#/state/messages/convo'
import {type ConvoItem} from '#/state/messages/convo/types' import {type ConvoItem} from '#/state/messages/convo/types'
@@ -24,8 +23,9 @@ import {PostAlerts} from '#/components/moderation/PostAlerts'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {useSimpleVerificationState} from '#/components/verification' import {useSimpleVerificationState} from '#/components/verification'
import {VerificationCheck} from '#/components/verification/VerificationCheck' 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({ export function MessagesListHeader({
profile, profile,
+5 -5
View File
@@ -14,9 +14,9 @@ import {
ScaleAndFadeOut, ScaleAndFadeOut,
} from '#/lib/custom-animations/ScaleAndFade' } from '#/lib/custom-animations/ScaleAndFade'
import {useHaptics} from '#/lib/haptics' import {useHaptics} from '#/lib/haptics'
import {isAndroid, isIOS, isWeb} from '#/platform/detection'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_ANDROID, IS_IOS, IS_WEB} from '#/env'
const AnimatedPressable = Animated.createAnimatedComponent(Pressable) const AnimatedPressable = Animated.createAnimatedComponent(Pressable)
@@ -28,18 +28,18 @@ export function NewMessagesPill({
const t = useTheme() const t = useTheme()
const playHaptic = useHaptics() const playHaptic = useHaptics()
const {bottom: bottomInset} = useSafeAreaInsets() const {bottom: bottomInset} = useSafeAreaInsets()
const bottomBarHeight = isIOS ? 42 : isAndroid ? 60 : 0 const bottomBarHeight = IS_IOS ? 42 : IS_ANDROID ? 60 : 0
const bottomOffset = isWeb ? 0 : bottomInset + bottomBarHeight const bottomOffset = IS_WEB ? 0 : bottomInset + bottomBarHeight
const scale = useSharedValue(1) const scale = useSharedValue(1)
const onPressIn = React.useCallback(() => { const onPressIn = React.useCallback(() => {
if (isWeb) return if (IS_WEB) return
scale.set(() => withTiming(1.075, {duration: 100})) scale.set(() => withTiming(1.075, {duration: 100}))
}, [scale]) }, [scale])
const onPressOut = React.useCallback(() => { const onPressOut = React.useCallback(() => {
if (isWeb) return if (IS_WEB) return
scale.set(() => withTiming(1, {duration: 100})) scale.set(() => withTiming(1, {duration: 100}))
}, [scale]) }, [scale])
+2 -2
View File
@@ -4,12 +4,12 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {HITSLOP_10} from '#/lib/constants' import {HITSLOP_10} from '#/lib/constants'
import {isNative} from '#/platform/detection'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button' import {Button, ButtonIcon} from '#/components/Button'
import * as TextField from '#/components/forms/TextField' import * as TextField from '#/components/forms/TextField'
import {MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlassIcon} from '#/components/icons/MagnifyingGlass' import {MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlassIcon} from '#/components/icons/MagnifyingGlass'
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import {IS_NATIVE} from '#/env'
type SearchInputProps = Omit<TextField.InputProps, 'label'> & { type SearchInputProps = Omit<TextField.InputProps, 'label'> & {
label?: TextField.InputProps['label'] label?: TextField.InputProps['label']
@@ -36,7 +36,7 @@ export const SearchInput = React.forwardRef<TextInput, SearchInputProps>(
placeholder={_(msg`Search`)} placeholder={_(msg`Search`)}
returnKeyType="search" returnKeyType="search"
keyboardAppearance={t.scheme} keyboardAppearance={t.scheme}
selectTextOnFocus={isNative} selectTextOnFocus={IS_NATIVE}
autoFocus={false} autoFocus={false}
accessibilityRole="search" accessibilityRole="search"
autoCorrect={false} 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 {HITSLOP_10} from '#/lib/constants'
import {useHaptics} from '#/lib/haptics' import {useHaptics} from '#/lib/haptics'
import {isNative} from '#/platform/detection'
import { import {
atoms as a, atoms as a,
native, native,
@@ -22,6 +21,7 @@ import {
import {useInteractionState} from '#/components/hooks/useInteractionState' import {useInteractionState} from '#/components/hooks/useInteractionState'
import {CheckThick_Stroke2_Corner0_Rounded as Checkmark} from '#/components/icons/Check' import {CheckThick_Stroke2_Corner0_Rounded as Checkmark} from '#/components/icons/Check'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
export * from './Panel' 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' } from 'react'
import {isFirefox, isSafari} from '#/lib/browser' import {isFirefox, isSafari} from '#/lib/browser'
import {isWeb} from '#/platform/detection' import {IS_WEB} from '#/env'
function fullscreenSubscribe(onChange: () => void) { function fullscreenSubscribe(onChange: () => void) {
document.addEventListener('fullscreenchange', onChange) document.addEventListener('fullscreenchange', onChange)
@@ -15,7 +15,7 @@ function fullscreenSubscribe(onChange: () => void) {
} }
export function useFullscreen(ref?: React.RefObject<HTMLElement | null>) { 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, () => const isFullscreen = useSyncExternalStore(fullscreenSubscribe, () =>
Boolean(document.fullscreenElement), Boolean(document.fullscreenElement),
) )
@@ -4,9 +4,9 @@ import {
createStarterPackLinkFromAndroidReferrer, createStarterPackLinkFromAndroidReferrer,
httpStarterPackUriToAtUri, httpStarterPackUriToAtUri,
} from '#/lib/strings/starter-pack' } from '#/lib/strings/starter-pack'
import {isAndroid} from '#/platform/detection'
import {useHasCheckedForStarterPack} from '#/state/preferences/used-starter-packs' import {useHasCheckedForStarterPack} from '#/state/preferences/used-starter-packs'
import {useSetActiveStarterPack} from '#/state/shell/starter-pack' import {useSetActiveStarterPack} from '#/state/shell/starter-pack'
import {IS_ANDROID} from '#/env'
import {Referrer, SharedPrefs} from '../../../modules/expo-bluesky-swiss-army' import {Referrer, SharedPrefs} from '../../../modules/expo-bluesky-swiss-army'
export function useStarterPackEntry() { export function useStarterPackEntry() {
@@ -32,7 +32,7 @@ export function useStarterPackEntry() {
;(async () => { ;(async () => {
let uri: string | null | undefined let uri: string | null | undefined
if (isAndroid) { if (IS_ANDROID) {
const res = await Referrer.getGooglePlayReferrerInfoAsync() const res = await Referrer.getGooglePlayReferrerInfoAsync()
if (res && res.installReferrer) { if (res && res.installReferrer) {
+2 -2
View File
@@ -1,7 +1,7 @@
import {useEffect, useState} from 'react' import {useEffect, useState} from 'react'
import {isWeb} from '#/platform/detection'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {IS_WEB} from '#/env'
export function useWelcomeModal() { export function useWelcomeModal() {
const {hasSession} = useSession() const {hasSession} = useSession()
@@ -22,7 +22,7 @@ export function useWelcomeModal() {
// 2. We're on the web (this is a web-only feature) // 2. We're on the web (this is a web-only feature)
// 3. We're on the homepage (path is '/' or '/home') // 3. We're on the homepage (path is '/' or '/home')
// 4. User hasn't actively closed the modal in this session // 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 currentPath = window.location.pathname
const isHomePage = currentPath === '/' const isHomePage = currentPath === '/'
const hasUserClosedModal = const hasUserClosedModal =
+2 -2
View File
@@ -11,12 +11,12 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {type Dimensions} from '#/lib/media/types' import {type Dimensions} from '#/lib/media/types'
import {isNative} from '#/platform/detection'
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {ArrowsDiagonalOut_Stroke2_Corner0_Rounded as Fullscreen} from '#/components/icons/ArrowsDiagonal' import {ArrowsDiagonalOut_Stroke2_Corner0_Rounded as Fullscreen} from '#/components/icons/ArrowsDiagonal'
import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {MediaInsetBorder} from '#/components/MediaInsetBorder'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
export function ConstrainedImage({ export function ConstrainedImage({
aspectRatio, aspectRatio,
@@ -35,7 +35,7 @@ export function ConstrainedImage({
* the height of the image. * the height of the image.
*/ */
const outerAspectRatio = useMemo<DimensionValue>(() => { 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, minMobileAspectRatio ?? 16 / 9) // 9:16 bounding box
: Math.min(1 / aspectRatio, 1) // 1:1 bounding box : Math.min(1 / aspectRatio, 1) // 1:1 bounding box
return `${ratio * 100}%` return `${ratio * 100}%`
@@ -3,7 +3,6 @@ import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {isNative} from '#/platform/detection'
import {useAgent, useSession} from '#/state/session' import {useAgent, useSession} from '#/state/session'
import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button' 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 {useIntentDialogs} from '#/components/intents/IntentDialogs'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
export function VerifyEmailIntentDialog() { export function VerifyEmailIntentDialog() {
const {verifyEmailDialogControl: control} = useIntentDialogs() const {verifyEmailDialogControl: control} = useIntentDialogs()
@@ -68,7 +68,7 @@ function Inner({}: {control: DialogControlProps}) {
<Loader size="xl" fill={t.atoms.text_contrast_low.color} /> <Loader size="xl" fill={t.atoms.text_contrast_low.color} />
</View> </View>
) : status === 'success' ? ( ) : 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]}> <Text style={[a.font_bold, a.text_2xl]}>
<Trans>Email Verified</Trans> <Trans>Email Verified</Trans>
</Text> </Text>
@@ -93,7 +93,7 @@ function Inner({}: {control: DialogControlProps}) {
</Text> </Text>
</View> </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]}> <Text style={[a.font_bold, a.text_2xl]}>
<Trans>Email Resent</Trans> <Trans>Email Resent</Trans>
</Text> </Text>
@@ -12,7 +12,6 @@ import {useLabelInfo} from '#/lib/moderation/useLabelInfo'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isAndroid} from '#/platform/detection'
import {useAgent, useSession} from '#/state/session' import {useAgent, useSession} from '#/state/session'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {atoms as a, useBreakpoints, useTheme} from '#/alf' 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 * as Dialog from '#/components/Dialog'
import {InlineLinkText} from '#/components/Link' import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_ANDROID} from '#/env'
import {Admonition} from '../Admonition' import {Admonition} from '../Admonition'
import {Divider} from '../Divider' import {Divider} from '../Divider'
import {Loader} from '../Loader' import {Loader} from '../Loader'
@@ -344,7 +344,7 @@ function AppealForm({
{isPending && <ButtonIcon icon={Loader} />} {isPending && <ButtonIcon icon={Loader} />}
</Button> </Button>
</View> </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 {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {listUriToHref} from '#/lib/strings/url-helpers' import {listUriToHref} from '#/lib/strings/url-helpers'
import {isNative} from '#/platform/detection'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {atoms as a, useGutters, useTheme} from '#/alf' import {atoms as a, useGutters, useTheme} from '#/alf'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
import {InlineLinkText} from '#/components/Link' import {InlineLinkText} from '#/components/Link'
import {type AppModerationCause} from '#/components/Pills' import {type AppModerationCause} from '#/components/Pills'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
export {useDialogControl as useModerationDetailsDialogControl} from '#/components/Dialog' export {useDialogControl as useModerationDetailsDialogControl} from '#/components/Dialog'
@@ -158,7 +158,7 @@ function ModerationDetailsDialogInner({
xGutters, xGutters,
a.py_md, a.py_md,
a.border_t, a.border_t,
!isNative && t.atoms.bg_contrast_25, !IS_NATIVE && t.atoms.bg_contrast_25,
t.atoms.border_contrast_low, t.atoms.border_contrast_low,
{ {
borderBottomLeftRadius: a.rounded_md.borderRadius, borderBottomLeftRadius: a.rounded_md.borderRadius,
@@ -219,7 +219,7 @@ function ModerationDetailsDialogInner({
</View> </View>
)} )}
{isNative && <View style={{height: 40}} />} {IS_NATIVE && <View style={{height: 40}} />}
<Dialog.Close /> <Dialog.Close />
</Dialog.ScrollableInner> </Dialog.ScrollableInner>
@@ -8,7 +8,6 @@ import {wait} from '#/lib/async/wait'
import {getLabelingServiceTitle} from '#/lib/moderation' import {getLabelingServiceTitle} from '#/lib/moderation'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
import {Logger} from '#/logger' import {Logger} from '#/logger'
import {isNative} from '#/platform/detection'
import {useMyLabelersQuery} from '#/state/queries/preferences' import {useMyLabelersQuery} from '#/state/queries/preferences'
import {CharProgress} from '#/view/com/composer/char-progress/CharProgress' import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
import {UserAvatar} from '#/view/com/util/UserAvatar' 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 {createStaticClick, InlineLinkText, Link} from '#/components/Link'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
import {useSubmitReportMutation} from './action' import {useSubmitReportMutation} from './action'
import { import {
BSKY_LABELER_ONLY_REPORT_REASONS, BSKY_LABELER_ONLY_REPORT_REASONS,
@@ -253,7 +253,7 @@ function Inner(props: ReportDialogProps) {
label={_(msg`Report dialog`)} label={_(msg`Report dialog`)}
ref={ref} ref={ref}
style={[a.w_full, {maxWidth: 500}]}> 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> <StepOuter>
<StepTitle <StepTitle
index={1} index={1}
+11
View File
@@ -1,3 +1,4 @@
import {Platform} from 'react-native'
import {nativeBuildVersion} from 'expo-application' import {nativeBuildVersion} from 'expo-application'
import {BUNDLE_IDENTIFIER, IS_TESTFLIGHT, RELEASE_VERSION} from '#/env/common' 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)} (${ export const APP_METADATA = `${BUNDLE_IDENTIFIER.slice(0, 7)} (${
__DEV__ ? 'dev' : IS_TESTFLIGHT ? 'tf' : 'prod' __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. * The short commit hash and environment of the current bundle.
*/ */
export const APP_METADATA = `${BUNDLE_IDENTIFIER.slice(0, 7)} (${__DEV__ ? 'dev' : 'prod'})` 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 {useLingui} from '@lingui/react'
import {useCleanError} from '#/lib/hooks/useCleanError' import {useCleanError} from '#/lib/hooks/useCleanError'
import {isNative} from '#/platform/detection'
import {atoms as a, web} from '#/alf' import {atoms as a, web} from '#/alf'
import {Admonition} from '#/components/Admonition' import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {Button, ButtonIcon, ButtonText} from '#/components/Button'
@@ -11,6 +10,7 @@ import * as Dialog from '#/components/Dialog'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import * as Toast from '#/components/Toast' import * as Toast from '#/components/Toast'
import {Span, Text} from '#/components/Typography' import {Span, Text} from '#/components/Typography'
import {IS_NATIVE} from '#/env'
import {useUpdateLiveEventPreferences} from '#/features/liveEvents/preferences' import {useUpdateLiveEventPreferences} from '#/features/liveEvents/preferences'
import { import {
type LiveEventFeed, type LiveEventFeed,
@@ -146,7 +146,7 @@ function Inner({
</ButtonText> </ButtonText>
{isHidingAllFeeds && <ButtonIcon icon={Loader} />} {isHidingAllFeeds && <ButtonIcon icon={Loader} />}
</Button> </Button>
{isNative && ( {IS_NATIVE && (
<Button <Button
label={_(msg`Cancel`)} label={_(msg`Cancel`)}
size="large" 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 {useMutation, useQueryClient} from '@tanstack/react-query'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import { import {
preferencesQueryKey, preferencesQueryKey,
usePreferencesQuery, usePreferencesQuery,
} from '#/state/queries/preferences' } from '#/state/queries/preferences'
import {useAgent} from '#/state/session' import {useAgent} from '#/state/session'
import {IS_WEB} from '#/env'
import * as env from '#/env' import * as env from '#/env'
import { import {
type LiveEventFeed, type LiveEventFeed,
@@ -41,7 +41,7 @@ function useWebOnlyDebugLiveEventPreferences() {
const agent = useAgent() const agent = useAgent()
useEffect(() => { useEffect(() => {
if (env.IS_DEV && isWeb && typeof window !== 'undefined') { if (env.IS_DEV && IS_WEB && typeof window !== 'undefined') {
// @ts-ignore // @ts-ignore
window.__updateLiveEventPreferences = async ( window.__updateLiveEventPreferences = async (
action: LiveEventPreferencesAction, action: LiveEventPreferencesAction,
+2 -2
View File
@@ -3,7 +3,7 @@ import {Platform} from 'react-native'
import * as Location from 'expo-location' import * as Location from 'expo-location'
import {createPermissionHook} from 'expo-modules-core' import {createPermissionHook} from 'expo-modules-core'
import {isNative} from '#/platform/detection' import {IS_NATIVE} from '#/env'
import * as debug from '#/geolocation/debug' import * as debug from '#/geolocation/debug'
import {logger} from '#/geolocation/logger' import {logger} from '#/geolocation/logger'
import {type Geolocation} from '#/geolocation/types' import {type Geolocation} from '#/geolocation/types'
@@ -118,7 +118,7 @@ export function useSyncDeviceGeolocationOnStartup(
const synced = useRef(false) const synced = useRef(false)
const [status] = useForegroundPermissions() const [status] = useForegroundPermissions()
useEffect(() => { useEffect(() => {
if (!isNative) return if (!IS_NATIVE) return
async function get() { async function get() {
// no need to set this more than once per session // 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 {type LocationGeocodedAddress} from 'expo-location'
import {isAndroid} from '#/platform/detection' import {IS_ANDROID} from '#/env'
import {logger} from '#/geolocation/logger' import {logger} from '#/geolocation/logger'
import {type Geolocation} from '#/geolocation/types' 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 * Android doesn't give us ISO 3166-2 short codes. We need these for US
*/ */
if (isAndroid) { if (IS_ANDROID) {
if (region && isoCountryCode === 'US') { if (region && isoCountryCode === 'US') {
/* /*
* We need short codes for US states. If we can't remap it, just drop it * 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