React Native New Arch (#10980)
This commit is contained in:
@@ -89,14 +89,12 @@ const SPRING_IN: WithSpringConfig = {
|
||||
mass: 0.75,
|
||||
damping: 300,
|
||||
stiffness: 1200,
|
||||
restDisplacementThreshold: 0.01,
|
||||
}
|
||||
|
||||
const SPRING_OUT: WithSpringConfig = {
|
||||
mass: IS_IOS ? 1.25 : 0.75,
|
||||
damping: 150,
|
||||
stiffness: 1000,
|
||||
restDisplacementThreshold: 0.01,
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@ import {Gesture, GestureDetector} from 'react-native-gesture-handler'
|
||||
import Animated, {
|
||||
type AnimatedRef,
|
||||
measure,
|
||||
Reanimated3DefaultSpringConfig,
|
||||
runOnJS,
|
||||
scrollTo,
|
||||
type SharedValue,
|
||||
@@ -237,8 +238,8 @@ function SortableItem<T>({
|
||||
itemKey: string
|
||||
itemCount: number
|
||||
itemHeight: number
|
||||
state: Animated.SharedValue<DragState>
|
||||
dragY: Animated.SharedValue<number>
|
||||
state: SharedValue<DragState>
|
||||
dragY: SharedValue<number>
|
||||
scrollCompensation: SharedValue<number>
|
||||
isGestureActive: SharedValue<boolean>
|
||||
measureDone: SharedValue<boolean>
|
||||
@@ -370,18 +371,18 @@ function SortableItem<T>({
|
||||
return {
|
||||
transform: [
|
||||
{translateY: s.dragStartSlot * itemHeight + dragY.get()},
|
||||
{scale: withSpring(1.03)},
|
||||
{scale: withSpring(1.03, Reanimated3DefaultSpringConfig)},
|
||||
],
|
||||
zIndex: 999,
|
||||
...(IS_IOS
|
||||
? {
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {width: 0, height: 1},
|
||||
shadowOpacity: withSpring(0.08),
|
||||
shadowRadius: withSpring(4),
|
||||
shadowOpacity: withSpring(0.08, Reanimated3DefaultSpringConfig),
|
||||
shadowRadius: withSpring(4, Reanimated3DefaultSpringConfig),
|
||||
}
|
||||
: {
|
||||
elevation: withSpring(3),
|
||||
elevation: withSpring(3, Reanimated3DefaultSpringConfig),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -391,11 +392,11 @@ function SortableItem<T>({
|
||||
const inactive = {
|
||||
...(IS_IOS
|
||||
? {
|
||||
shadowOpacity: withSpring(0),
|
||||
shadowRadius: withSpring(0),
|
||||
shadowOpacity: withSpring(0, Reanimated3DefaultSpringConfig),
|
||||
shadowRadius: withSpring(0, Reanimated3DefaultSpringConfig),
|
||||
}
|
||||
: {
|
||||
elevation: withSpring(0),
|
||||
elevation: withSpring(0, Reanimated3DefaultSpringConfig),
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -425,7 +426,7 @@ function SortableItem<T>({
|
||||
return {
|
||||
transform: [
|
||||
{translateY: withTiming(baseY + offset, {duration: 200})},
|
||||
{scale: withSpring(1)},
|
||||
{scale: withSpring(1, Reanimated3DefaultSpringConfig)},
|
||||
],
|
||||
zIndex: 0,
|
||||
...inactive,
|
||||
|
||||
@@ -15,6 +15,8 @@ export const IS_GLASS_AVAILABLE =
|
||||
* Liquid Glass View that uses `expo-glass-effect`
|
||||
*
|
||||
* If unavailable, falls back to a regular `View`. Use `fallbackStyle` to customize the fallback appearance.
|
||||
* Note: Setting opacity to 0 on Expo GlassView or any of its parent views causes the glass effect to not render at all. https://docs.expo.dev/versions/v56.0.0/sdk/glass-effect/#known-issues
|
||||
* If animating the opacity of a parent view, start from a non-zero opacity to avoid this issue.
|
||||
*/
|
||||
export const GlassView = IS_GLASS_AVAILABLE ? InnerGlassView : FallbackView
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {useLingui} from '@lingui/react'
|
||||
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {DraggableScrollView} from '#/view/com/pager/DraggableScrollView'
|
||||
import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture'
|
||||
import {atoms as a, tokens, useTheme, web} from '#/alf'
|
||||
import {transparentifyColor} from '#/alf/util/colorGeneration'
|
||||
import {Button, ButtonIcon} from '#/components/Button'
|
||||
@@ -200,42 +201,44 @@ export function InterestTabs({
|
||||
|
||||
return (
|
||||
<View style={[a.relative, a.flex_row]}>
|
||||
<DraggableScrollView
|
||||
ref={listRef}
|
||||
contentContainerStyle={[
|
||||
a.gap_sm,
|
||||
{paddingHorizontal: gutterWidth},
|
||||
contentContainerStyle,
|
||||
]}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
decelerationRate="fast"
|
||||
snapToOffsets={
|
||||
tabOffsets.filter(o => !!o).length === interests.length
|
||||
? tabOffsets.map(o => o.x - tokens.space.xl)
|
||||
: undefined
|
||||
}
|
||||
onLayout={evt => setTotalWidth(evt.nativeEvent.layout.width)}
|
||||
onContentSizeChange={width => setContentWidth(width)}
|
||||
onScroll={evt => {
|
||||
const newScrollX = evt.nativeEvent.contentOffset.x
|
||||
setScrollX(newScrollX)
|
||||
}}
|
||||
scrollEventThrottle={16}>
|
||||
{interests.map((interest, i) => {
|
||||
const active = interest === selectedInterest && !disabled
|
||||
return (
|
||||
<TabComponent
|
||||
key={interest}
|
||||
onSelectTab={handleSelectTab}
|
||||
active={active}
|
||||
index={i}
|
||||
interest={interest}
|
||||
interestsDisplayName={interestsDisplayNames[interest]}
|
||||
onLayout={handleTabLayout}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</DraggableScrollView>
|
||||
<BlockDrawerGesture>
|
||||
<DraggableScrollView
|
||||
ref={listRef}
|
||||
contentContainerStyle={[
|
||||
a.gap_sm,
|
||||
{paddingHorizontal: gutterWidth},
|
||||
contentContainerStyle,
|
||||
]}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
decelerationRate="fast"
|
||||
snapToOffsets={
|
||||
tabOffsets.filter(o => !!o).length === interests.length
|
||||
? tabOffsets.map(o => o.x - tokens.space.xl)
|
||||
: undefined
|
||||
}
|
||||
onLayout={evt => setTotalWidth(evt.nativeEvent.layout.width)}
|
||||
onContentSizeChange={width => setContentWidth(width)}
|
||||
onScroll={evt => {
|
||||
const newScrollX = evt.nativeEvent.contentOffset.x
|
||||
setScrollX(newScrollX)
|
||||
}}
|
||||
scrollEventThrottle={16}>
|
||||
{interests.map((interest, i) => {
|
||||
const active = interest === selectedInterest && !disabled
|
||||
return (
|
||||
<TabComponent
|
||||
key={interest}
|
||||
onSelectTab={handleSelectTab}
|
||||
active={active}
|
||||
index={i}
|
||||
interest={interest}
|
||||
interestsDisplayName={interestsDisplayNames[interest]}
|
||||
onLayout={handleTabLayout}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</DraggableScrollView>
|
||||
</BlockDrawerGesture>
|
||||
{IS_WEB && canScrollLeft && (
|
||||
<View
|
||||
style={[
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {forwardRef, memo, useContext, useMemo} from 'react'
|
||||
import {memo, useContext, useMemo} from 'react'
|
||||
import {
|
||||
type StyleProp,
|
||||
View,
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
type ViewStyle,
|
||||
} from 'react-native'
|
||||
import Animated, {
|
||||
type AnimatedRef,
|
||||
type AnimatedScrollViewProps,
|
||||
useAnimatedStyle,
|
||||
} from 'react-native-reanimated'
|
||||
@@ -73,67 +74,64 @@ export type ContentProps = AnimatedScrollViewProps & {
|
||||
style?: StyleProp<ViewStyle>
|
||||
contentContainerStyle?: StyleProp<ViewStyle>
|
||||
ignoreTabletLayoutOffset?: boolean
|
||||
ref?: AnimatedRef<Animated.ScrollView>
|
||||
}
|
||||
|
||||
/**
|
||||
* Default scroll view for simple pages
|
||||
*/
|
||||
export const Content = memo(
|
||||
forwardRef<Animated.ScrollView, ContentProps>(function Content(
|
||||
{
|
||||
children,
|
||||
style,
|
||||
contentContainerStyle,
|
||||
ignoreTabletLayoutOffset,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) {
|
||||
const t = useTheme()
|
||||
const {footerHeight} = useShellLayout()
|
||||
const {isWithinSplitView} = useIsWithinSplitView()
|
||||
export const Content = memo(function Content({
|
||||
children,
|
||||
style,
|
||||
contentContainerStyle,
|
||||
ignoreTabletLayoutOffset,
|
||||
ref,
|
||||
...props
|
||||
}: ContentProps) {
|
||||
const t = useTheme()
|
||||
const {footerHeight} = useShellLayout()
|
||||
const {isWithinSplitView} = useIsWithinSplitView()
|
||||
|
||||
// note - if we ever make the footer transparent in any way,
|
||||
// we'll need to change this to use contentInsets/scrollIndicatorInsets
|
||||
// on iOS and contentContainerStyle padding on Android -sfn
|
||||
const animatedStyle = useAnimatedStyle(() => {
|
||||
return {
|
||||
marginBottom: footerHeight.get(),
|
||||
}
|
||||
})
|
||||
// note - if we ever make the footer transparent in any way,
|
||||
// we'll need to change this to use contentInsets/scrollIndicatorInsets
|
||||
// on iOS and contentContainerStyle padding on Android -sfn
|
||||
const animatedStyle = useAnimatedStyle(() => {
|
||||
return {
|
||||
marginBottom: footerHeight.get(),
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<Animated.ScrollView
|
||||
ref={ref}
|
||||
id="content"
|
||||
automaticallyAdjustsScrollIndicatorInsets={false}
|
||||
indicatorStyle={t.scheme === 'dark' ? 'white' : 'black'}
|
||||
style={[
|
||||
a.w_full,
|
||||
animatedStyle,
|
||||
isWithinSplitView &&
|
||||
web({
|
||||
flex: 1,
|
||||
overflowY: 'scroll',
|
||||
scrollbarWidth: 'thin',
|
||||
scrollbarColor: `${t.palette.contrast_100} transparent`,
|
||||
}),
|
||||
style,
|
||||
]}
|
||||
contentContainerStyle={[contentContainerStyle]}
|
||||
{...props}>
|
||||
{IS_WEB ? (
|
||||
<Center ignoreTabletLayoutOffset={ignoreTabletLayoutOffset}>
|
||||
{/* @ts-expect-error web only -esb */}
|
||||
{children}
|
||||
</Center>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</Animated.ScrollView>
|
||||
)
|
||||
}),
|
||||
)
|
||||
return (
|
||||
<Animated.ScrollView
|
||||
ref={ref}
|
||||
id="content"
|
||||
automaticallyAdjustsScrollIndicatorInsets={false}
|
||||
indicatorStyle={t.scheme === 'dark' ? 'white' : 'black'}
|
||||
style={[
|
||||
a.w_full,
|
||||
animatedStyle,
|
||||
isWithinSplitView &&
|
||||
web({
|
||||
flex: 1,
|
||||
overflowY: 'scroll',
|
||||
scrollbarWidth: 'thin',
|
||||
scrollbarColor: `${t.palette.contrast_100} transparent`,
|
||||
}),
|
||||
style,
|
||||
]}
|
||||
contentContainerStyle={[contentContainerStyle]}
|
||||
{...props}>
|
||||
{IS_WEB ? (
|
||||
<Center ignoreTabletLayoutOffset={ignoreTabletLayoutOffset}>
|
||||
{/* @ts-expect-error web only -esb */}
|
||||
{children}
|
||||
</Center>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</Animated.ScrollView>
|
||||
)
|
||||
})
|
||||
|
||||
/**
|
||||
* Utility component to center content within the screen
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from 'react-native-gesture-handler'
|
||||
import Animated, {
|
||||
type AnimatableValue,
|
||||
Reanimated3DefaultSpringConfig,
|
||||
runOnJS,
|
||||
type SharedValue,
|
||||
useAnimatedReaction,
|
||||
@@ -463,7 +464,10 @@ function clampTranslation(
|
||||
|
||||
function withClampedSpring<T extends AnimatableValue>(value: T): T {
|
||||
'worklet'
|
||||
return withSpring(value, {overshootClamping: true})
|
||||
return withSpring(value, {
|
||||
...Reanimated3DefaultSpringConfig,
|
||||
overshootClamping: true,
|
||||
})
|
||||
}
|
||||
|
||||
export default memo(ImageItem)
|
||||
|
||||
@@ -170,8 +170,6 @@ const ImageItem = ({
|
||||
width: screenSize.width,
|
||||
maxHeight: screenSize.height,
|
||||
alignSelf: 'center',
|
||||
aspectRatio: imageAspect ?? 1 /* force onLoad */,
|
||||
opacity: imageAspect === undefined ? 0 : 1,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -180,11 +178,19 @@ const ImageItem = ({
|
||||
return {
|
||||
transform: cropContentTransform,
|
||||
width: '100%',
|
||||
aspectRatio: imageAspect ?? 1 /* force onLoad */,
|
||||
opacity: imageAspect === undefined ? 0 : 1,
|
||||
}
|
||||
})
|
||||
|
||||
/*
|
||||
* When the aspect ratio is unknown until onLoad fires, these layout props
|
||||
* change after mount. They must be applied via a React render rather than
|
||||
* useAnimatedStyle
|
||||
*/
|
||||
const imageLayoutStyle = {
|
||||
aspectRatio: imageAspect ?? 1 /* force onLoad */,
|
||||
opacity: imageAspect === undefined ? 0 : 1,
|
||||
}
|
||||
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
const [hasLoaded, setHasLoaded] = useState(false)
|
||||
useAnimatedReaction(
|
||||
@@ -225,8 +231,8 @@ const ImageItem = ({
|
||||
{showLoader && (
|
||||
<ActivityIndicator size="small" color="#FFF" style={styles.loading} />
|
||||
)}
|
||||
<Animated.View style={imageCropStyle}>
|
||||
<Animated.View style={imageStyle}>
|
||||
<Animated.View style={[imageCropStyle, imageLayoutStyle]}>
|
||||
<Animated.View style={[imageStyle, imageLayoutStyle]}>
|
||||
<Image
|
||||
contentFit="contain"
|
||||
source={{uri: imageSrc.uri}}
|
||||
|
||||
@@ -60,13 +60,11 @@ const SLOW_SPRING: WithSpringConfig = {
|
||||
mass: IS_IOS ? 1.25 : 0.75,
|
||||
damping: 300,
|
||||
stiffness: 800,
|
||||
restDisplacementThreshold: 0.001,
|
||||
}
|
||||
const FAST_SPRING: WithSpringConfig = {
|
||||
mass: IS_IOS ? 1.25 : 0.75,
|
||||
damping: 150,
|
||||
stiffness: 900,
|
||||
restDisplacementThreshold: 0.001,
|
||||
}
|
||||
|
||||
function canAnimate(lightbox: Lightbox): boolean {
|
||||
@@ -532,7 +530,7 @@ function LightboxImage({
|
||||
const dismissTranslateY =
|
||||
isActive && openProgressValue === 1 ? dismissSwipeTranslateY.get() : 0
|
||||
|
||||
if (openProgressValue === 0 && isFlyingAway.get()) {
|
||||
if (openProgressValue === 0) {
|
||||
return {
|
||||
isHidden: true,
|
||||
isResting: false,
|
||||
@@ -609,6 +607,7 @@ function LightboxImage({
|
||||
return withSpring(0, {
|
||||
stiffness: 700,
|
||||
damping: 50,
|
||||
mass: 1,
|
||||
reduceMotion: ReduceMotion.Never,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import {type Component} from 'react'
|
||||
import {type TransformsStyle} from 'react-native'
|
||||
import {
|
||||
type AnimatedRef,
|
||||
@@ -29,7 +28,7 @@ export type ImageSource = {
|
||||
thumbUri: string
|
||||
thumbDimensions: Dimensions | null
|
||||
thumbRect: MeasuredDimensions | null
|
||||
thumbRef?: AnimatedRef<Component> | null
|
||||
thumbRef?: AnimatedRef | null
|
||||
thumbBorderRadius?: number
|
||||
alt?: string
|
||||
type: 'image' | 'circle-avi' | 'rect-avi'
|
||||
|
||||
@@ -177,6 +177,7 @@ export function LabelBase({
|
||||
text,
|
||||
a.font_semi_bold,
|
||||
a.leading_tight,
|
||||
a.flex_shrink,
|
||||
t.atoms.text_contrast_medium,
|
||||
{paddingRight: 3},
|
||||
]}>
|
||||
|
||||
@@ -59,7 +59,7 @@ export function ImageEmbed({
|
||||
|
||||
// Captured from AutoSizedImage so the peek-commit handler can reuse the same
|
||||
// ref + dims that a tap would — keeps the lightbox's return animation intact.
|
||||
const singleContainerRef = useRef<AnimatedRef<React.Component> | null>(null)
|
||||
const singleContainerRef = useRef<AnimatedRef | null>(null)
|
||||
const singleDimsRef = useRef<Dimensions | null>(null)
|
||||
|
||||
if (images.length > 0) {
|
||||
@@ -71,7 +71,7 @@ export function ImageEmbed({
|
||||
}))
|
||||
const onPress = (
|
||||
index: number,
|
||||
refs: AnimatedRef<React.Component>[],
|
||||
refs: AnimatedRef[],
|
||||
fetchedDims: (Dimensions | null)[],
|
||||
) => {
|
||||
if (postContext) {
|
||||
|
||||
@@ -119,7 +119,8 @@ export const ProgressGuideToast = forwardRef<
|
||||
left = right = (winDim.width - 380) / 2
|
||||
}
|
||||
return {
|
||||
position: IS_WEB ? 'fixed' : 'absolute',
|
||||
// position: fixed is web only
|
||||
position: (IS_WEB ? 'fixed' : 'absolute') as 'absolute',
|
||||
top: 0,
|
||||
left,
|
||||
right,
|
||||
@@ -134,12 +135,7 @@ export const ProgressGuideToast = forwardRef<
|
||||
return (
|
||||
isOpen && (
|
||||
<Portal>
|
||||
<Animated.View
|
||||
style={[
|
||||
// @ts-ignore position: fixed is web only
|
||||
containerStyle,
|
||||
animatedStyle,
|
||||
]}>
|
||||
<Animated.View style={[containerStyle, animatedStyle]}>
|
||||
<Pressable
|
||||
style={[
|
||||
t.atoms.bg,
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
type ViewStyle,
|
||||
} from 'react-native'
|
||||
import Animated, {
|
||||
type AnimatedStyle,
|
||||
FadeIn,
|
||||
FadeOut,
|
||||
interpolateColor,
|
||||
@@ -662,7 +663,7 @@ function BlockedPlaceholder({
|
||||
style,
|
||||
}: {
|
||||
profile: Shadow<ChatBskyActorDefs.ProfileViewBasic>
|
||||
style?: StyleProp<ViewStyle>
|
||||
style?: AnimatedStyle<ViewStyle>
|
||||
}) {
|
||||
const {t: l} = useLingui()
|
||||
const t = useTheme()
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
import {HITSLOP_20} from '#/lib/constants'
|
||||
import {mergeRefs} from '#/lib/merge-refs'
|
||||
import {
|
||||
android,
|
||||
applyFonts,
|
||||
atoms as a,
|
||||
platform,
|
||||
@@ -223,10 +222,6 @@ export function createInput(Component: typeof TextInput) {
|
||||
paddingTop: 13,
|
||||
paddingBottom: 13,
|
||||
},
|
||||
android({
|
||||
paddingTop: 8,
|
||||
paddingBottom: 9,
|
||||
}),
|
||||
/*
|
||||
* Margins are needed here to avoid autofill background overlapping the
|
||||
* top and bottom borders - esb
|
||||
|
||||
Reference in New Issue
Block a user