Merge pull request #8494 from BlueWallet/fix-carousel
Some checks are pending
Build Release and Upload to TestFlight (iOS) / build (push) Waiting to run
Build Release and Upload to TestFlight (iOS) / testflight-upload (push) Blocked by required conditions
BuildReleaseApk / buildReleaseApk (push) Waiting to run
BuildReleaseApk / browserstack (push) Blocked by required conditions

fix: carousel cards snap
This commit is contained in:
GLaDOS 2026-04-24 17:11:13 +01:00 committed by GitHub
commit 2e7785bc60
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 23 additions and 10 deletions

View File

@ -37,8 +37,9 @@ import { Transaction, TWallet } from '../class/wallets/types';
import { BlueSpacing10 } from './BlueSpacing';
import { useLocale } from '@react-navigation/native';
// Horizontal carousel shows a small peek of the next card; adjust overlap to control that spacing.
const CARD_OVERLAP = 24;
export const WALLET_CAROUSEL_HEADER_WIDTH = 16;
export const getWalletCarouselItemWidth = (screenWidth: number) => Math.round(screenWidth * 0.82 > 375 ? 375 : screenWidth * 0.82);
interface NewWalletPanelProps {
onPress: () => void;
@ -74,7 +75,7 @@ const nStyles = StyleSheet.create({
const NewWalletPanel: React.FC<NewWalletPanelProps> = ({ onPress }) => {
const { colors } = useTheme();
const { width } = useWindowDimensions();
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
const itemWidth = getWalletCarouselItemWidth(width);
const { isLarge } = useSizeClass();
const nStylesHooks = StyleSheet.create({
container: isLarge
@ -283,7 +284,7 @@ export const WalletCarouselItem: React.FC<WalletCarouselItemProps> = React.memo(
const { colors } = useTheme();
const { walletTransactionUpdateStatus } = useStorage();
const { width } = useWindowDimensions();
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
const itemWidth = getWalletCarouselItemWidth(width);
const { sizeClass } = useSizeClass();
const isCompact = sizeVariant === 'compact';
const { direction } = useLocale();
@ -517,7 +518,7 @@ type FlatListRefType = FlatList<any> & {
const styles = StyleSheet.create({
listHeaderSeparator: {
width: 16,
width: WALLET_CAROUSEL_HEADER_WIDTH,
height: 20,
},
});
@ -540,7 +541,14 @@ const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props
} = props;
const { width } = useWindowDimensions();
const itemWidth = React.useMemo(() => (width * 0.82 > 375 ? 375 : width * 0.82), [width]);
const itemWidth = React.useMemo(() => getWalletCarouselItemWidth(width), [width]);
const snapInterval = React.useMemo(() => itemWidth, [itemWidth]);
const snapOffsets = React.useMemo(() => {
if (!horizontal) return undefined;
const cardsCount = data.length + (onNewWalletPress ? 1 : 0);
// Keep every card aligned with the first card's resting position.
return Array.from({ length: cardsCount }, (_, index) => index * snapInterval);
}, [horizontal, data.length, onNewWalletPress, snapInterval]);
const layoutTransition = useMemo(() => LinearTransition.duration(240).easing(Easing.inOut(Easing.quad)), []);
const enteringTransition = useMemo(() => FadeIn.duration(180), []);
const exitingTransition = useMemo(() => FadeOut.duration(150), []);
@ -858,9 +866,11 @@ const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props
extraData={[data, animateChanges, newWalletsMap.current, selectedWallet, lastAddedWalletId.current]}
keyExtractor={keyExtractor}
showsVerticalScrollIndicator={false}
pagingEnabled={horizontal}
pagingEnabled={false}
disableIntervalMomentum={horizontal}
snapToInterval={horizontal ? itemWidth - CARD_OVERLAP : undefined}
snapToInterval={undefined}
snapToOffsets={snapOffsets}
snapToAlignment={horizontal ? 'start' : undefined}
decelerationRate="fast"
contentContainerStyle={cStyles.content}
directionalLockEnabled

View File

@ -11,7 +11,7 @@ import presentAlert from '../../components/Alert';
import { FButton, FContainer } from '../../components/FloatButtons';
import { useTheme } from '../../components/themes';
import { TransactionListItem } from '../../components/TransactionListItem';
import WalletsCarousel from '../../components/WalletsCarousel';
import WalletsCarousel, { getWalletCarouselItemWidth } from '../../components/WalletsCarousel';
import { useSizeClass, SizeClass } from '../../blue_modules/sizeClass';
import loc from '../../loc';
import ActionSheet from '../ActionSheet';
@ -237,10 +237,13 @@ const WalletsList: React.FC = () => {
if (!isFocused) return;
const contentOffset = e.nativeEvent.contentOffset;
const index = Math.ceil(contentOffset.x / width);
const cardWidth = getWalletCarouselItemWidth(width);
const snapStep = cardWidth; // keep in sync with WalletsCarousel snap interval
const index = Math.max(0, Math.round(contentOffset.x / snapStep));
if (currentWalletIndex.current !== index) {
console.debug('onSnapToItem', wallets.length === index ? 'NewWallet/Importing card' : index);
triggerHapticFeedback(HapticFeedbackTypes.Selection);
if (wallets[index] && (wallets[index].timeToRefreshBalance() || wallets[index].timeToRefreshTransaction())) {
refreshWallets(index, false, false);
}