diff --git a/apps/mobile/src/features/home/HomeScreen.tsx b/apps/mobile/src/features/home/HomeScreen.tsx index 389a1f0bf0d8..26230cd9422a 100644 --- a/apps/mobile/src/features/home/HomeScreen.tsx +++ b/apps/mobile/src/features/home/HomeScreen.tsx @@ -25,6 +25,7 @@ import { type NativeSyntheticEvent, } from "react-native"; import { ScrollViewMarker } from "react-native-screens"; +import { GestureDetector, useNativeGesture } from "react-native-gesture-handler"; import type { SwipeableMethods } from "react-native-gesture-handler/ReanimatedSwipeable"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -280,6 +281,8 @@ export function HomeScreen(props: HomeScreenProps) { const fullSwipeWidth = primaryColumn && columnMetrics ? columnMetrics.width - 20 : undefined; const queuedThreadKeys = useQueuedThreadKeys(); const openSwipeableRef = useRef(null); + // Let trackpad scrolling coexist with each row's horizontal swipe gesture. + const homeScrollGesture = useNativeGesture(); const insets = useSafeAreaInsets(); const { fabClearance } = useAndroidControlSizing(); const screenMetrics = useNativeLayoutMetrics(); @@ -807,6 +810,7 @@ export function HomeScreen(props: HomeScreenProps) { onMoveThread={handleMoveThread} onSwipeableClose={handleSwipeableClose} onSwipeableWillOpen={handleSwipeableWillOpen} + simultaneousSwipeGesture={homeScrollGesture} activationKey={item.key} /> ); @@ -826,6 +830,7 @@ export function HomeScreen(props: HomeScreenProps) { handleSwipeableWillOpen, handleUnsettleThread, handleSetThreadAutoSettle, + homeScrollGesture, autoSettleOptOutEnvironmentIds, pinningEnvironmentIds, autoSettleOptOutEnvironmentIds, @@ -1012,51 +1017,53 @@ export function HomeScreen(props: HomeScreenProps) { rebuilds and `itemsAreEqual` keeps a minute tick (or an unrelated shell update) from re-rendering untouched rows. */} - activateVisibleRows(threadListV2Items)} - onTouchStart={(event) => trackListTouches(event, true)} - onTouchEnd={(event) => trackListTouches(event, false)} - onTouchCancel={(event) => trackListTouches(event, false)} - renderScrollComponent={Platform.OS === "ios" ? renderHomeScrollView : undefined} - data={threadListV2Items} - renderItem={renderV2Item} - keyExtractor={v2KeyExtractor} - getItemType={(item) => item.type} - itemsAreEqual={threadListV2ListItemsAreEqual} - estimatedItemSize={ESTIMATED_THREAD_LIST_V2_ROW_HEIGHT} - drawDistance={THREAD_LIST_V2_DRAW_DISTANCE} - recycleItems - extraData={v2ExtraData} - ListHeaderComponent={v2ListHeader} - ListFooterComponent={ - settledShelfExpanded && threadListV2Layout.hiddenSettledCount > 0 ? ( - - ) : null - } - ListEmptyComponent={v2ListEmpty} - style={{ flex: 1 }} - automaticallyAdjustsScrollIndicatorInsets={Platform.OS === "ios"} - contentInsetAdjustmentBehavior="never" - contentInset={Platform.OS === "ios" ? { top: insets.top } : undefined} - contentInsetStartAdjustment={Platform.OS === "ios" ? insets.top : 0} - showsVerticalScrollIndicator={false} - keyboardDismissMode="on-drag" - keyboardShouldPersistTaps="handled" - {...scrollGateHandlers} - scrollEventThrottle={16} - contentContainerStyle={{ - paddingLeft: (contentSideInsets?.left ?? 0) + (primaryColumn ? 8 : 0), - paddingRight: (contentSideInsets?.right ?? 0) + (primaryColumn ? 8 : 0), - paddingBottom: - Platform.OS === "ios" - ? iosBottomClearance - : Math.max(insets.bottom, 16) + (Platform.OS === "android" ? fabClearance : 88), - }} - /> + + activateVisibleRows(threadListV2Items)} + onTouchStart={(event) => trackListTouches(event, true)} + onTouchEnd={(event) => trackListTouches(event, false)} + onTouchCancel={(event) => trackListTouches(event, false)} + renderScrollComponent={Platform.OS === "ios" ? renderHomeScrollView : undefined} + data={threadListV2Items} + renderItem={renderV2Item} + keyExtractor={v2KeyExtractor} + getItemType={(item) => item.type} + itemsAreEqual={threadListV2ListItemsAreEqual} + estimatedItemSize={ESTIMATED_THREAD_LIST_V2_ROW_HEIGHT} + drawDistance={THREAD_LIST_V2_DRAW_DISTANCE} + recycleItems + extraData={v2ExtraData} + ListHeaderComponent={v2ListHeader} + ListFooterComponent={ + settledShelfExpanded && threadListV2Layout.hiddenSettledCount > 0 ? ( + + ) : null + } + ListEmptyComponent={v2ListEmpty} + style={{ flex: 1 }} + automaticallyAdjustsScrollIndicatorInsets={Platform.OS === "ios"} + contentInsetAdjustmentBehavior="never" + contentInset={Platform.OS === "ios" ? { top: insets.top } : undefined} + contentInsetStartAdjustment={Platform.OS === "ios" ? insets.top : 0} + showsVerticalScrollIndicator={false} + keyboardDismissMode="on-drag" + keyboardShouldPersistTaps="handled" + {...scrollGateHandlers} + scrollEventThrottle={16} + contentContainerStyle={{ + paddingLeft: (contentSideInsets?.left ?? 0) + (primaryColumn ? 8 : 0), + paddingRight: (contentSideInsets?.right ?? 0) + (primaryColumn ? 8 : 0), + paddingBottom: + Platform.OS === "ios" + ? iosBottomClearance + : Math.max(insets.bottom, 16) + (Platform.OS === "android" ? fabClearance : 88), + }} + /> +