Co-authored-by: Eric Bailey <git@esb.lol>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-02-24 06:34:52 +00:00
committed by GitHub
parent 9b0f0a8d24
commit 73b096e443
23 changed files with 90 additions and 71 deletions
-6
View File
@@ -1,6 +0,0 @@
export default {
requestPermission: jest.fn(),
onForegroundEvent: jest.fn(),
setBadgeCount: jest.fn(),
displayNotification: jest.fn(),
}
@@ -1,9 +0,0 @@
export const CameraRoll = {
getPhotos: jest.fn().mockResolvedValue({
edges: [
{node: {image: {uri: 'path/to/image1.jpg'}}},
{node: {image: {uri: 'path/to/image2.jpg'}}},
{node: {image: {uri: 'path/to/image3.jpg'}}},
],
}),
}
@@ -1,4 +0,0 @@
export default {
configure: jest.fn().mockResolvedValue(0),
finish: jest.fn(),
}
-1
View File
@@ -1 +0,0 @@
export default {}
-10
View File
@@ -1,10 +0,0 @@
jest.mock('rn-fetch-blob', () => {
return {
__esModule: true,
default: {
fs: {
unlink: jest.fn(),
},
},
}
})
-2
View File
@@ -1,2 +0,0 @@
export const DropdownMenu = jest.fn().mockImplementation(() => {})
export const create = jest.fn().mockImplementation(() => {})
-1
View File
@@ -112,7 +112,6 @@ module.exports = function (_config) {
'zh-Hans',
'zh-Hant',
],
UIDesignRequiresCompatibility: true,
},
associatedDomains: ASSOCIATED_DOMAINS,
entitlements: {
@@ -27,6 +27,19 @@ class SheetViewController: UIViewController {
return
}
// On iOS 26, the floaty sheet presentation adds the device bottom safe area
// on top of the custom detent value, creating visible padding inside the pill.
// Subtract it so the pill height matches our actual content.
var bottomSafeAreaAdjustment: CGFloat = 0
if #available(iOS 26.0, *) {
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first {
bottomSafeAreaAdjustment = window.safeAreaInsets.bottom
}
}
let adjustedHeight = contentHeight - bottomSafeAreaAdjustment
if #available(iOS 16.0, *) {
if contentHeight > screenHeight - 100 {
sheet.detents = [
@@ -36,7 +49,7 @@ class SheetViewController: UIViewController {
} else {
sheet.detents = [
.custom { _ in
return contentHeight
return adjustedHeight
}
]
if !preventExpansion {
@@ -5,6 +5,7 @@ import {
type NativeSyntheticEvent,
Platform,
type StyleProp,
useWindowDimensions,
View,
type ViewStyle,
} from 'react-native'
@@ -21,8 +22,6 @@ import {
Context as PortalContext,
} from './BottomSheetPortal'
const screenHeight = Dimensions.get('screen').height
const NativeView: React.ComponentType<
BottomSheetViewProps & {
ref: React.RefObject<any>
@@ -94,6 +93,7 @@ export class BottomSheetNativeComponent extends React.Component<
let extraStyles
if (IS_IOS15 && this.state.viewHeight) {
const screenHeight = Dimensions.get('screen').height
const {viewHeight} = this.state
const cornerRadius = this.props.cornerRadius ?? 0
if (viewHeight < screenHeight / 2) {
@@ -154,6 +154,7 @@ function BottomSheetNativeComponentInner({
}) {
const insets = useSafeAreaInsets()
const cornerRadius = rest.cornerRadius ?? 0
const {height: screenHeight} = useWindowDimensions()
const sheetHeight = IS_IOS ? screenHeight - insets.top : screenHeight
+7
View File
@@ -16,6 +16,13 @@
"expo-image-picker"
]
}
},
"install": {
"exclude": [
"react-native-reanimated",
"@sentry/react-native",
"react-native-pager-view"
]
}
},
"scripts": {
+4
View File
@@ -10,6 +10,10 @@ const EXP_CURVE = 'cubic-bezier(0.16, 1, 0.3, 1)'
export const atoms = {
...baseAtoms,
rounded_sheet: {
borderRadius: 40,
},
h_full_vh: web({
height: '100vh',
}),
+11 -7
View File
@@ -38,7 +38,7 @@ import {
type DialogOuterProps,
} from '#/components/Dialog/types'
import {createInput} from '#/components/forms/TextField'
import {IS_ANDROID, IS_IOS} from '#/env'
import {IS_ANDROID, IS_IOS, IS_LIQUID_GLASS} from '#/env'
import {BottomSheet, BottomSheetSnapPoint} from '../../../modules/bottom-sheet'
import {
type BottomSheetSnapPointChangeEvent,
@@ -166,7 +166,8 @@ export function Outer({
return (
<BottomSheet
ref={ref}
cornerRadius={20}
// device-bezel radius when undefined
cornerRadius={IS_LIQUID_GLASS ? undefined : 20}
backgroundColor={t.atoms.bg.backgroundColor}
{...nativeOptions}
onSnapPointChange={onSnapPointChange}
@@ -181,6 +182,9 @@ export function Outer({
)
}
/**
* @deprecated use `Dialog.ScrollableInner` instead
*/
export function Inner({children, style, header}: DialogInnerProps) {
const insets = useSafeAreaInsets()
return (
@@ -190,9 +194,9 @@ export function Inner({children, style, header}: DialogInnerProps) {
style={[
a.pt_2xl,
a.px_xl,
{
paddingBottom: insets.bottom + insets.top,
},
IS_LIQUID_GLASS
? a.pb_2xl
: {paddingBottom: insets.bottom + insets.top},
style,
]}>
{children}
@@ -253,7 +257,7 @@ export const ScrollableInner = React.forwardRef<ScrollView, DialogInnerProps>(
<KeyboardAwareScrollView
contentContainerStyle={[
a.pt_2xl,
a.px_xl,
IS_LIQUID_GLASS ? a.px_2xl : a.px_xl,
{paddingBottom},
contentContainerStyle,
]}
@@ -342,7 +346,7 @@ export function FlatListFooter({children}: {children: React.ReactNode}) {
a.pt_md,
{
paddingBottom: platform({
ios: tokens.space.md + bottom,
ios: tokens.space.md + bottom + (IS_LIQUID_GLASS ? top : 0),
android: tokens.space.md + bottom + top,
}),
},
+8 -3
View File
@@ -8,6 +8,7 @@ import {
import {atoms as a, useTheme} from '#/alf'
import {Text} from '#/components/Typography'
import {IS_LIQUID_GLASS} from '#/env'
export function Header({
renderLeft,
@@ -35,7 +36,7 @@ export function Header({
a.flex_row,
a.justify_center,
a.align_center,
{minHeight: 50},
{minHeight: IS_LIQUID_GLASS ? 64 : 50},
a.border_b,
t.atoms.border_contrast_medium,
t.atoms.bg,
@@ -44,11 +45,15 @@ export function Header({
style,
]}>
{renderLeft && (
<View style={[a.absolute, {left: 6}]}>{renderLeft()}</View>
<View style={[a.absolute, {left: IS_LIQUID_GLASS ? 12 : 6}]}>
{renderLeft()}
</View>
)}
{children}
{renderRight && (
<View style={[a.absolute, {right: 6}]}>{renderRight()}</View>
<View style={[a.absolute, {right: IS_LIQUID_GLASS ? 12 : 6}]}>
{renderRight()}
</View>
)}
</View>
)
+2 -2
View File
@@ -1,7 +1,7 @@
import {useCallback} from 'react'
import {SystemBars} from 'react-native-edge-to-edge'
import {IS_IOS} from '#/env'
import {IS_IOS, IS_LIQUID_GLASS} from '#/env'
/**
* If we're calling a system API like the image picker that opens a sheet
@@ -9,7 +9,7 @@ import {IS_IOS} from '#/env'
*/
export function useSheetWrapper() {
return useCallback(async <T>(promise: Promise<T>): Promise<T> => {
if (IS_IOS) {
if (IS_IOS && !IS_LIQUID_GLASS) {
const entry = SystemBars.pushStackEntry({
style: {
statusBar: 'light',
+4 -2
View File
@@ -273,7 +273,8 @@ export function ContainerItem({
a.align_center,
a.gap_sm,
a.px_md,
a.rounded_md,
a.rounded_lg,
a.curve_continuous,
a.border,
t.atoms.bg_contrast_25,
t.atoms.border_contrast_low,
@@ -311,7 +312,8 @@ export function Group({children, style}: GroupProps) {
return (
<View
style={[
a.rounded_md,
a.rounded_lg,
a.curve_continuous,
a.overflow_hidden,
a.border,
t.atoms.border_contrast_low,
@@ -17,7 +17,7 @@ import {SearchInput} from '#/components/forms/SearchInput'
import * as Toggle from '#/components/forms/Toggle'
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
import {Text} from '#/components/Typography'
import {IS_NATIVE, IS_WEB} from '#/env'
import {IS_LIQUID_GLASS, IS_NATIVE, IS_WEB} from '#/env'
export function LanguageSelectDialog({
titleText,
@@ -52,7 +52,9 @@ export function LanguageSelectDialog({
return (
<Dialog.Outer
control={control}
nativeOptions={{minHeight: height - insets.top}}>
nativeOptions={{
minHeight: IS_LIQUID_GLASS ? height : height - insets.top,
}}>
<Dialog.Handle />
<ErrorBoundary renderError={renderErrorBoundary}>
<DialogInner
+8
View File
@@ -5,6 +5,12 @@ import {BUNDLE_IDENTIFIER, IS_TESTFLIGHT, RELEASE_VERSION} from '#/env/common'
export * from '#/env/common'
// for some reason Platform.OS === 'ios' AND Platform.Version is undefined in our CI unit tests -sfn
const iOSMajorVersion =
Platform.OS === 'ios' && typeof Platform.Version === 'string'
? parseInt(Platform.Version.split('.')[0], 10)
: 0
/**
* The semver version of the app, specified in our `package.json`.file. On
* iOs/Android, the native build version is appended to the semver version, so
@@ -41,3 +47,5 @@ export const IS_WEB_FIREFOX: boolean = false
* Misc
*/
export const IS_HIGH_DPI: boolean = true
// ideally we'd use isLiquidGlassAvailable() from expo-glass-effect but checking iOS version is good enough for now
export const IS_LIQUID_GLASS: boolean = iOSMajorVersion >= 26
+1
View File
@@ -47,3 +47,4 @@ export const IS_WEB_FIREFOX: boolean = /firefox|fxios/i.test(
export const IS_HIGH_DPI: boolean = window.matchMedia(
'(min-resolution: 2dppx)',
).matches
export const IS_LIQUID_GLASS: boolean = false
+4 -2
View File
@@ -14,7 +14,7 @@ import {atoms as a, native, useBreakpoints, useTheme, web} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {Loader} from '#/components/Loader'
import {P, Text} from '#/components/Typography'
import {IS_IOS, IS_WEB} from '#/env'
import {IS_IOS, IS_LIQUID_GLASS, IS_WEB} from '#/env'
const COL_WIDTH = 400
@@ -107,7 +107,9 @@ export function SignupQueued() {
animationType={native('slide')}
presentationStyle="formSheet"
style={[web(a.util_screen_outer)]}>
{IS_IOS && <SystemBars style={{statusBar: 'light'}} />}
{IS_IOS && !IS_LIQUID_GLASS && (
<SystemBars style={{statusBar: 'light'}} />
)}
<ScrollView
style={[a.flex_1, t.atoms.bg]}
contentContainerStyle={{borderWidth: 0}}
+8 -10
View File
@@ -133,7 +133,7 @@ import * as Prompt from '#/components/Prompt'
import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import {IS_ANDROID, IS_IOS, IS_NATIVE, IS_WEB} from '#/env'
import {IS_ANDROID, IS_IOS, IS_LIQUID_GLASS, IS_NATIVE, IS_WEB} from '#/env'
import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet'
import {
draftToComposerPosts,
@@ -1522,7 +1522,7 @@ function ComposerTopBar({
<Animated.View
style={topBarAnimatedStyle}
layout={native(LinearTransition)}>
<View style={styles.topbarInner}>
<View style={[a.flex_row, a.align_center, a.gap_xs, a.p_lg, a.pb_md]}>
<Button
label={_(msg`Cancel`)}
variant="ghost"
@@ -2138,10 +2138,15 @@ function useKeyboardVerticalOffset() {
return bottom * -1
}
// they ditched the gap behaviour on 26
if (IS_LIQUID_GLASS) {
return top
}
// iPhone SE
if (top === 20) return 40
// all other iPhones
// all other iPhones on <26
return top + 10
}
@@ -2186,13 +2191,6 @@ function useHideKeyboardOnBackground() {
}
const styles = StyleSheet.create({
topbarInner: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 8,
height: 54,
gap: 4,
},
postBtn: {
borderRadius: 20,
paddingHorizontal: 20,
+4 -2
View File
@@ -1,5 +1,5 @@
import {useState} from 'react'
import {TouchableOpacity, View} from 'react-native'
import {TouchableOpacity, useWindowDimensions, View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Plural, Trans} from '@lingui/react/macro'
@@ -69,6 +69,7 @@ export function GifAltTextDialogLoaded({
const {_} = useLingui()
const t = useTheme()
const [altTextDraft, setAltTextDraft] = useState(altText || vendorAltText)
const {height: minHeight} = useWindowDimensions()
return (
<>
<TouchableOpacity
@@ -108,7 +109,8 @@ export function GifAltTextDialogLoaded({
control={control}
onClose={() => {
onSubmit(altTextDraft)
}}>
}}
nativeOptions={{minHeight}}>
<Dialog.Handle />
<AltTextInner
vendorAltText={vendorAltText}
+5 -3
View File
@@ -6,6 +6,7 @@ import {useComposerState} from '#/state/shell/composer'
import {ComposePost, useComposerCancelRef} from '#/view/com/composer/Composer'
import {atoms as a, useTheme} from '#/alf'
import {SheetCompatProvider as TooltipSheetCompatProvider} from '#/components/Tooltip'
import {IS_LIQUID_GLASS} from '#/env'
export function Composer({}: {winHeight: number}) {
const {setFullyExpandedCount} = useDialogStateControlContext()
@@ -32,8 +33,10 @@ export function Composer({}: {winHeight: number}) {
visible={open}
presentationStyle="pageSheet"
animationType="slide"
onRequestClose={() => ref.current?.onPressCancel()}>
<View style={[t.atoms.bg, a.flex_1]}>
onRequestClose={() => ref.current?.onPressCancel()}
backdropColor="transparent"
style={[!IS_LIQUID_GLASS && a.rounded_sheet]}>
<View style={[a.flex_1, a.curve_continuous, t.atoms.bg]}>
<TooltipSheetCompatProvider>
<ComposePost
cancelRef={ref}
@@ -45,7 +48,6 @@ export function Composer({}: {winHeight: number}) {
text={state?.text}
imageUris={state?.imageUris}
videoUri={state?.videoUri}
openGallery={state?.openGallery}
/>
</TooltipSheetCompatProvider>
</View>
+3 -2
View File
@@ -43,7 +43,7 @@ import {useAgeAssurance} from '#/ageAssurance'
import {NoAccessScreen} from '#/ageAssurance/components/NoAccessScreen'
import {RedirectOverlay} from '#/ageAssurance/components/RedirectOverlay'
import {PassiveAnalytics} from '#/analytics/PassiveAnalytics'
import {IS_ANDROID, IS_IOS} from '#/env'
import {IS_ANDROID, IS_IOS, IS_LIQUID_GLASS} from '#/env'
import {RoutesContainer, TabsNavigator} from '#/Navigation'
import {BottomSheetOutlet} from '../../../modules/bottom-sheet'
import {updateActiveViewAsync} from '../../../modules/expo-bluesky-swiss-army/src/VisibilityView'
@@ -223,7 +223,8 @@ export function Shell() {
<SystemBars
style={{
statusBar:
t.name !== 'light' || (IS_IOS && fullyExpandedCount > 0)
t.name !== 'light' ||
(IS_IOS && !IS_LIQUID_GLASS && fullyExpandedCount > 0)
? 'light'
: 'dark',
navigationBar: t.name !== 'light' ? 'light' : 'dark',