From 1875604c6d92ecd62edef9cb0f8cd004f80c46d3 Mon Sep 17 00:00:00 2001 From: jtrzupek Date: Mon, 22 Jun 2026 10:21:19 +0200 Subject: [PATCH] =?UTF-8?q?fix(mobile):=20onboarding=20pager=20=E2=80=94?= =?UTF-8?q?=20measure=20page=20width=20so=20last=20slide=20shows=20"Start?= =?UTF-8?q?=20browsing"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- mobile/src/components/OnboardingModal.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/mobile/src/components/OnboardingModal.tsx b/mobile/src/components/OnboardingModal.tsx index 804ae87..4cbc54f 100644 --- a/mobile/src/components/OnboardingModal.tsx +++ b/mobile/src/components/OnboardingModal.tsx @@ -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(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) => { - 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) => ( - + 0 && { width: pageW }]}> {s.icon} {s.title}