Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client

Move browser env into main env (#9718)

authored by

Eric Bailey and committed by
GitHub
62c0c06b 0589bd7d

+70 -55
+2 -2
src/components/MediaInsetBorder.tsx
··· 1 1 import {StyleSheet} from 'react-native' 2 2 import type React from 'react' 3 3 4 - import {isHighDPI} from '#/lib/browser' 5 4 import {atoms as a, platform, useTheme, type ViewStyleProp} from '#/alf' 6 5 import {Fill} from '#/components/Fill' 6 + import {IS_HIGH_DPI} from '#/env' 7 7 8 8 /** 9 9 * Applies and thin border within a bounding box. Used to contrast media from ··· 33 33 // while we generally use hairlineWidth (aka 1px), 34 34 // we make an exception here for high DPI screens 35 35 // as the 1px border is very noticeable -sfn 36 - web: isHighDPI ? 0.5 : StyleSheet.hairlineWidth, 36 + web: IS_HIGH_DPI ? 0.5 : StyleSheet.hairlineWidth, 37 37 }), 38 38 }, 39 39 opaque
+3 -3
src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/Scrubber.tsx
··· 3 3 import {msg} from '@lingui/macro' 4 4 import {useLingui} from '@lingui/react' 5 5 6 - import {isFirefox, isTouchDevice} from '#/lib/browser' 7 6 import {clamp} from '#/lib/numbers' 8 7 import {atoms as a, useTheme, web} from '#/alf' 9 8 import {useInteractionState} from '#/components/hooks/useInteractionState' 9 + import {IS_WEB_FIREFOX, IS_WEB_TOUCH_DEVICE} from '#/env' 10 10 import {formatTime} from './utils' 11 11 12 12 export function Scrubber({ ··· 100 100 // a pointerUp event is fired outside the element that captured the 101 101 // pointer. Firefox clicks on the element the mouse is over, so we have 102 102 // to make everything unclickable while seeking -sfn 103 - if (isFirefox && scrubberActive) { 103 + if (IS_WEB_FIREFOX && scrubberActive) { 104 104 document.body.classList.add('force-no-clicks') 105 105 106 106 return () => { ··· 151 151 <View 152 152 testID="scrubber" 153 153 style={[ 154 - {height: isTouchDevice ? 32 : 18, width: '100%'}, 154 + {height: IS_WEB_TOUCH_DEVICE ? 32 : 18, width: '100%'}, 155 155 a.flex_shrink_0, 156 156 a.px_xs, 157 157 ]}
+2 -3
src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx
··· 4 4 import {useLingui} from '@lingui/react' 5 5 import type Hls from 'hls.js' 6 6 7 - import {isTouchDevice} from '#/lib/browser' 8 7 import {clamp} from '#/lib/numbers' 9 8 import { 10 9 useAutoplayDisabled, ··· 27 26 import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play' 28 27 import {Loader} from '#/components/Loader' 29 28 import {Text} from '#/components/Typography' 30 - import {IS_WEB_MOBILE_IOS} from '#/env' 29 + import {IS_WEB_MOBILE_IOS, IS_WEB_TOUCH_DEVICE} from '#/env' 31 30 import {TimeIndicator} from '../TimeIndicator' 32 31 import {ControlButton} from './ControlButton' 33 32 import {Scrubber} from './Scrubber' ··· 342 341 {opacity: showControls ? 1 : 0}, 343 342 {transition: 'opacity 0.2s ease-in-out'}, 344 343 ]}> 345 - {(!volumeHovered || isTouchDevice) && ( 344 + {(!volumeHovered || IS_WEB_TOUCH_DEVICE) && ( 346 345 <Scrubber 347 346 duration={duration} 348 347 currentTime={currentTime}
+5 -3
src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx
··· 4 4 import {msg} from '@lingui/macro' 5 5 import {useLingui} from '@lingui/react' 6 6 7 - import {isSafari, isTouchDevice} from '#/lib/browser' 8 7 import {atoms as a} from '#/alf' 9 8 import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute' 10 9 import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' 11 10 import {useVideoVolumeState} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' 11 + import {IS_WEB_SAFARI, IS_WEB_TOUCH_DEVICE} from '#/env' 12 12 import {ControlButton} from './ControlButton' 13 13 14 14 export function VolumeControl({ ··· 57 57 onPointerEnter={onHover} 58 58 onPointerLeave={onEndHover} 59 59 style={[a.relative]}> 60 - {hovered && !isTouchDevice && ( 60 + {hovered && !IS_WEB_TOUCH_DEVICE && ( 61 61 <Animated.View 62 62 entering={FadeIn.duration(100)} 63 63 exiting={FadeOut.duration(100)} ··· 80 80 aria-label={_(msg`Volume`)} 81 81 style={ 82 82 // Ridiculous safari hack for old version of safari. Fixed in sonoma beta -h 83 - isSafari ? {height: 92, minHeight: '100%'} : {height: '100%'} 83 + IS_WEB_SAFARI 84 + ? {height: 92, minHeight: '100%'} 85 + : {height: '100%'} 84 86 } 85 87 onChange={onVolumeChange} 86 88 // @ts-expect-error for old versions of firefox, and then re-using it for targeting the CSS -sfn
+2 -2
src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/utils.tsx
··· 1 1 import {type RefObject, useCallback, useEffect, useRef, useState} from 'react' 2 2 3 - import {isSafari} from '#/lib/browser' 4 3 import {logger} from '#/logger' 5 4 import {useVideoVolumeState} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' 5 + import {IS_WEB_SAFARI} from '#/env' 6 6 7 7 export function useVideoElement(ref: RefObject<HTMLVideoElement | null>) { 8 8 const [playing, setPlaying] = useState(false) ··· 41 41 setCurrentTime(round(ref.current.currentTime) || 0) 42 42 // HACK: Safari randomly fires `stalled` events when changing between segments 43 43 // let's just clear the buffering state if the video is still progressing -sfn 44 - if (isSafari) { 44 + if (IS_WEB_SAFARI) { 45 45 if (bufferingTimeout) clearTimeout(bufferingTimeout) 46 46 setBuffering(false) 47 47 }
+3 -3
src/components/Post/Embed/VideoEmbed/index.web.tsx
··· 11 11 import {msg} from '@lingui/macro' 12 12 import {useLingui} from '@lingui/react' 13 13 14 - import {isFirefox} from '#/lib/browser' 15 14 import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' 16 15 import {atoms as a, useTheme} from '#/alf' 17 16 import {useIsWithinMessage} from '#/components/dms/MessageContext' ··· 23 22 VideoEmbedInnerWeb, 24 23 VideoNotFoundError, 25 24 } from '#/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb' 25 + import {IS_WEB_FIREFOX} from '#/env' 26 26 import {useActiveVideoWeb} from './ActiveVideoWebContext' 27 27 import * as VideoFallback from './VideoEmbedInner/VideoFallback' 28 28 ··· 37 37 38 38 useEffect(() => { 39 39 if (!ref.current) return 40 - if (isFullscreen && !isFirefox) return 40 + if (isFullscreen && !IS_WEB_FIREFOX) return 41 41 const observer = new IntersectionObserver( 42 42 entries => { 43 43 const entry = entries[0] ··· 150 150 // observing a div of 100vh height 151 151 useEffect(() => { 152 152 if (!ref.current) return 153 - if (isFullscreen && !isFirefox) return 153 + if (isFullscreen && !IS_WEB_FIREFOX) return 154 154 const observer = new IntersectionObserver( 155 155 entries => { 156 156 const entry = entries[0]
+2 -2
src/components/ProfileHoverCard/index.web.tsx
··· 11 11 import {useNavigation} from '@react-navigation/native' 12 12 13 13 import {useActorStatus} from '#/lib/actor-status' 14 - import {isTouchDevice} from '#/lib/browser' 15 14 import {getModerationCauseKey} from '#/lib/moderation' 16 15 import {makeProfileLink} from '#/lib/routes/links' 17 16 import {type NavigationProp} from '#/lib/routes/types' ··· 43 42 import {Text} from '#/components/Typography' 44 43 import {useSimpleVerificationState} from '#/components/verification' 45 44 import {VerificationCheck} from '#/components/verification/VerificationCheck' 45 + import {IS_WEB_TOUCH_DEVICE} from '#/env' 46 46 import {type ProfileHoverCardProps} from './types' 47 47 48 48 const floatingMiddlewares = [ ··· 70 70 } 71 71 } 72 72 73 - if (props.disable || isTouchDevice) { 73 + if (props.disable || IS_WEB_TOUCH_DEVICE) { 74 74 return props.children 75 75 } else { 76 76 return (
+2 -3
src/components/SubtleHover.tsx
··· 1 1 import {View} from 'react-native' 2 2 3 - import {isTouchDevice} from '#/lib/browser' 4 3 import {atoms as a, useTheme, type ViewStyleProp} from '#/alf' 5 - import {IS_NATIVE, IS_WEB} from '#/env' 4 + import {IS_NATIVE, IS_WEB, IS_WEB_TOUCH_DEVICE} from '#/env' 6 5 7 6 export function SubtleHover({ 8 7 style, ··· 40 39 ) 41 40 42 41 if (IS_WEB && web) { 43 - return isTouchDevice ? null : el 42 + return IS_WEB_TOUCH_DEVICE ? null : el 44 43 } else if (IS_NATIVE && native) { 45 44 return el 46 45 }
+2 -3
src/components/hooks/useFullscreen.ts
··· 6 6 useSyncExternalStore, 7 7 } from 'react' 8 8 9 - import {isFirefox, isSafari} from '#/lib/browser' 10 - import {IS_WEB} from '#/env' 9 + import {IS_WEB, IS_WEB_FIREFOX, IS_WEB_SAFARI} from '#/env' 11 10 12 11 function fullscreenSubscribe(onChange: () => void) { 13 12 document.addEventListener('fullscreenchange', onChange) ··· 39 38 40 39 // Chrome has an issue where it doesn't scroll back to the top after exiting fullscreen 41 40 // Let's play it safe and do it if not FF or Safari, since anything else will probably be chromium 42 - if (prevIsFullscreen && !isFirefox && !isSafari) { 41 + if (prevIsFullscreen && !IS_WEB_FIREFOX && !IS_WEB_SAFARI) { 43 42 setTimeout(() => { 44 43 if (scrollYRef.current !== null) { 45 44 window.scrollTo(0, scrollYRef.current)
+13
src/env/index.ts
··· 26 26 export const IS_ANDROID: boolean = Platform.OS === 'android' 27 27 export const IS_NATIVE: boolean = true 28 28 export const IS_WEB: boolean = false 29 + 30 + /** 31 + * Web-specific platform detection 32 + */ 33 + export const IS_WEB_TOUCH_DEVICE: boolean = true 29 34 export const IS_WEB_MOBILE: boolean = false 30 35 export const IS_WEB_MOBILE_IOS: boolean = false 36 + export const IS_WEB_MOBILE_ANDROID: boolean = false 37 + export const IS_WEB_SAFARI: boolean = false 38 + export const IS_WEB_FIREFOX: boolean = false 39 + 40 + /** 41 + * Misc 42 + */ 43 + export const IS_HIGH_DPI: boolean = true
+23 -2
src/env/index.web.ts
··· 21 21 export const IS_ANDROID: boolean = false 22 22 export const IS_NATIVE: boolean = false 23 23 export const IS_WEB: boolean = true 24 - // @ts-ignore we know window exists -prf 25 - export const IS_WEB_MOBILE: boolean = global.window.matchMedia( 24 + 25 + /** 26 + * Web-specific platform detection 27 + */ 28 + export const IS_WEB_TOUCH_DEVICE = 29 + window.matchMedia('(pointer: coarse)').matches 30 + export const IS_WEB_MOBILE: boolean = window.matchMedia( 26 31 'only screen and (max-width: 1300px)', 27 32 )?.matches 28 33 export const IS_WEB_MOBILE_IOS: boolean = /iPhone/.test(navigator.userAgent) 34 + export const IS_WEB_MOBILE_ANDROID: boolean = 35 + /android/i.test(navigator.userAgent) && IS_WEB_TOUCH_DEVICE 36 + export const IS_WEB_SAFARI: boolean = /^((?!chrome|android).)*safari/i.test( 37 + // https://stackoverflow.com/questions/7944460/detect-safari-browser 38 + navigator.userAgent, 39 + ) 40 + export const IS_WEB_FIREFOX: boolean = /firefox|fxios/i.test( 41 + navigator.userAgent, 42 + ) 43 + 44 + /** 45 + * Misc 46 + */ 47 + export const IS_HIGH_DPI: boolean = window.matchMedia( 48 + '(min-resolution: 2dppx)', 49 + ).matches
-5
src/lib/browser.native.ts
··· 1 - export const isSafari = false 2 - export const isFirefox = false 3 - export const isTouchDevice = true 4 - export const IS_ANDROIDWeb = false 5 - export const isHighDPI = true
-9
src/lib/browser.ts
··· 1 - // https://stackoverflow.com/questions/7944460/detect-safari-browser 2 - export const isSafari = /^((?!chrome|android).)*safari/i.test( 3 - navigator.userAgent, 4 - ) 5 - export const isFirefox = /firefox|fxios/i.test(navigator.userAgent) 6 - export const isTouchDevice = window.matchMedia('(pointer: coarse)').matches 7 - export const IS_ANDROIDWeb = 8 - /android/i.test(navigator.userAgent) && isTouchDevice 9 - export const isHighDPI = window.matchMedia('(min-resolution: 2dppx)').matches
+2 -3
src/lib/custom-animations/PressableScale.tsx
··· 12 12 withTiming, 13 13 } from 'react-native-reanimated' 14 14 15 - import {isTouchDevice} from '#/lib/browser' 16 - import {IS_NATIVE} from '#/env' 15 + import {IS_NATIVE, IS_WEB_TOUCH_DEVICE} from '#/env' 17 16 18 - const DEFAULT_TARGET_SCALE = IS_NATIVE || isTouchDevice ? 0.98 : 1 17 + const DEFAULT_TARGET_SCALE = IS_NATIVE || IS_WEB_TOUCH_DEVICE ? 0.98 : 1 19 18 20 19 const AnimatedPressable = Animated.createAnimatedComponent(Pressable) 21 20
+2 -3
src/lib/strings/embed-player.ts
··· 1 1 import {Dimensions} from 'react-native' 2 2 3 - import {isSafari} from '#/lib/browser' 4 - import {IS_WEB} from '#/env' 3 + import {IS_WEB, IS_WEB_SAFARI} from '#/env' 5 4 6 5 const {height: SCREEN_HEIGHT} = Dimensions.get('window') 7 6 ··· 560 559 } 561 560 562 561 if (IS_WEB) { 563 - if (isSafari) { 562 + if (IS_WEB_SAFARI) { 564 563 id = id.replace('AAAAC', 'AAAP1') 565 564 filename = filename.replace('.gif', '.mp4') 566 565 } else {
+3 -3
src/screens/Messages/components/MessageInput.web.tsx
··· 6 6 import TextareaAutosize from 'react-textarea-autosize' 7 7 import {countGraphemes} from 'unicode-segmenter/grapheme' 8 8 9 - import {isSafari, isTouchDevice} from '#/lib/browser' 10 9 import {MAX_DM_GRAPHEME_LENGTH} from '#/lib/constants' 11 10 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 12 11 import { ··· 24 23 import {useSharedInputStyles} from '#/components/forms/TextField' 25 24 import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji' 26 25 import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlane} from '#/components/icons/PaperPlane' 26 + import {IS_WEB_SAFARI, IS_WEB_TOUCH_DEVICE} from '#/env' 27 27 import {useExtractEmbedFromFacets} from './MessageInputEmbed' 28 28 29 29 export function MessageInput({ ··· 85 85 // far too long of a delay, and a subsequent enter press would often just end up doing nothing. A shorter time 86 86 // frame was also not great, since it was too short to be reliable (i.e. an older system might have a larger 87 87 // time gap between the two events firing. 88 - if (isSafari && e.key === 'Enter' && e.keyCode === 229) { 88 + if (IS_WEB_SAFARI && e.key === 'Enter' && e.keyCode === 229) { 89 89 return 90 90 } 91 91 ··· 228 228 onChange={onChange} 229 229 // On mobile web phones, we want to keep the same behavior as the native app. Do not submit the message 230 230 // in these cases. 231 - onKeyDown={isTouchDevice && isMobile ? undefined : onKeyDown} 231 + onKeyDown={IS_WEB_TOUCH_DEVICE && isMobile ? undefined : onKeyDown} 232 232 /> 233 233 <Pressable 234 234 accessibilityRole="button"
+2 -3
src/screens/StarterPack/StarterPackLandingScreen.tsx
··· 11 11 import {msg, Trans} from '@lingui/macro' 12 12 import {useLingui} from '@lingui/react' 13 13 14 - import {IS_ANDROIDWeb} from '#/lib/browser' 15 14 import {JOINED_THIS_WEEK} from '#/lib/constants' 16 15 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 17 16 import {logEvent} from '#/lib/statsig/statsig' ··· 37 36 import * as Prompt from '#/components/Prompt' 38 37 import {RichText} from '#/components/RichText' 39 38 import {Text} from '#/components/Typography' 40 - import {IS_WEB} from '#/env' 39 + import {IS_WEB, IS_WEB_MOBILE_ANDROID} from '#/env' 41 40 import * as bsky from '#/types/bsky' 42 41 43 42 const AnimatedPressable = Animated.createAnimatedComponent(Pressable) ··· 142 141 postAppClipMessage({ 143 142 action: 'present', 144 143 }) 145 - } else if (IS_ANDROIDWeb) { 144 + } else if (IS_WEB_MOBILE_ANDROID) { 146 145 androidDialogControl.open() 147 146 } else { 148 147 onContinue()
+2 -3
src/view/com/util/UserAvatar.tsx
··· 16 16 import {useQueryClient} from '@tanstack/react-query' 17 17 18 18 import {useActorStatus} from '#/lib/actor-status' 19 - import {isTouchDevice} from '#/lib/browser' 20 19 import {useHaptics} from '#/lib/haptics' 21 20 import { 22 21 useCameraPermission, ··· 53 52 import {MediaInsetBorder} from '#/components/MediaInsetBorder' 54 53 import * as Menu from '#/components/Menu' 55 54 import {ProfileHoverCard} from '#/components/ProfileHoverCard' 56 - import {IS_ANDROID, IS_NATIVE, IS_WEB} from '#/env' 55 + import {IS_ANDROID, IS_NATIVE, IS_WEB, IS_WEB_TOUCH_DEVICE} from '#/env' 57 56 import type * as bsky from '#/types/bsky' 58 57 59 58 export type UserAvatarType = 'user' | 'algo' | 'list' | 'labeler' ··· 571 570 <ProfileHoverCard did={profile.did} disable={disableHoverCard}> 572 571 {disableNavigation ? ( 573 572 avatarEl 574 - ) : status.isActive && (IS_NATIVE || isTouchDevice) ? ( 573 + ) : status.isActive && (IS_NATIVE || IS_WEB_TOUCH_DEVICE) ? ( 575 574 <> 576 575 <Button 577 576 label={_(