fix(mobile): onboarding pager — measure page width so last slide shows "Start browsing"

scrollTo/onScroll used the full screen width, but the ScrollView viewport is narrower
(card margins + padding), so the computed index desynced from the visible slide — the
last slide kept showing "Next"/"Skip" instead of "Start browsing". Measure the real
viewport width via onLayout and use it for paging, scrollTo and index. Caught on the
emulator (uiautomator dump — FLAG_SECURE blocks screenshots).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jtrzupek 2026-06-22 10:21:19 +02:00
parent db23b63e46
commit 1875604c6d

View file

@ -10,7 +10,6 @@
*/
import React from 'react';
import {
Dimensions,
Modal,
NativeScrollEvent,
NativeSyntheticEvent,
@ -97,7 +96,11 @@ export function OnboardingModal() {
const [visible, setVisible] = React.useState(false);
const [index, setIndex] = React.useState(0);
const scrollRef = React.useRef<ScrollView>(null);
const width = Dimensions.get('window').width;
// Szerokość STRONY = realna szerokość viewportu ScrollView (mierzona onLayout),
// NIE szerokość ekranu — karta ma marginesy + padding, więc ekran ≠ strona.
// Użycie szerokości ekranu rozjeżdżało paging vs scrollTo vs index (ostatni
// slajd pokazywał "Next" zamiast "Start browsing").
const [pageW, setPageW] = React.useState(0);
// Pierwsze odpalenie: pokaż jeśli jeszcze nie widziany.
React.useEffect(() => {
@ -134,13 +137,14 @@ export function OnboardingModal() {
(i: number) => {
const clamped = Math.max(0, Math.min(SLIDES.length - 1, i));
setIndex(clamped);
scrollRef.current?.scrollTo({ x: clamped * width, animated: true });
if (pageW > 0) scrollRef.current?.scrollTo({ x: clamped * pageW, animated: true });
},
[width],
[pageW],
);
const onScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
const i = Math.round(e.nativeEvent.contentOffset.x / width);
if (pageW <= 0) return;
const i = Math.round(e.nativeEvent.contentOffset.x / pageW);
if (i !== index) setIndex(i);
};
@ -164,10 +168,11 @@ export function OnboardingModal() {
pagingEnabled
showsHorizontalScrollIndicator={false}
onMomentumScrollEnd={onScroll}
onLayout={(e) => setPageW(e.nativeEvent.layout.width)}
style={styles.pager}
>
{SLIDES.map((s, i) => (
<View key={i} style={[styles.slide, { width: width - 48 }]}>
<View key={i} style={[styles.slide, pageW > 0 && { width: pageW }]}>
<Text style={styles.icon}>{s.icon}</Text>
<Text style={styles.title}>{s.title}</Text>
<View style={styles.lines}>