diff --git a/mobile/src/changelog.ts b/mobile/src/changelog.ts index ea7790a..42dc153 100644 --- a/mobile/src/changelog.ts +++ b/mobile/src/changelog.ts @@ -29,6 +29,21 @@ export type ChangelogEntry = { }; export const CHANGELOG: ChangelogEntry[] = [ + { + id: '2026-09-07f', + date: 'September 2026', + items: [ + 'The bottom bar is back on screen whenever you return from a scene, movie or performer, even if it was tucked away before you left.', + ], + }, + { + id: '2026-09-07e', + date: 'September 2026', + items: [ + 'The bottom bar slides away while you scroll down and comes back as soon as you scroll up, so the grid gets the whole screen.', + 'Sites: the pinning hint disappears once you have pinned something.', + ], + }, { id: '2026-09-07d', date: 'September 2026', diff --git a/mobile/src/components/BugReportFAB.tsx b/mobile/src/components/BugReportFAB.tsx index 4f96488..830a6a0 100644 --- a/mobile/src/components/BugReportFAB.tsx +++ b/mobile/src/components/BugReportFAB.tsx @@ -17,6 +17,7 @@ import React, { useCallback, useState } from 'react'; import { ActivityIndicator, Alert, + Animated, Image, KeyboardAvoidingView, Modal, @@ -35,7 +36,7 @@ import { captureScreen } from 'react-native-view-shot'; import { GoonClient } from '../api'; import { theme } from '../theme'; -import { BOTTOM_BAR_GAP, BOTTOM_BAR_HEIGHT } from '../lib/bottomBar'; +import { BOTTOM_BAR_GAP, BOTTOM_BAR_HEIGHT, barHidden } from '../lib/bottomBar'; import { formatLastPlayback } from '../lib/lastPlayback'; import { getSxyprnDiag } from '../lib/sxyprnResolver'; @@ -123,6 +124,9 @@ export function BugReportFAB({ client, appVersion, navRef }: Props) { const insets = useSafeAreaInsets(); const liftAboveTabs = currentRoute === null || currentRoute === 'Scenes'; const fabBottom = 24 + (liftAboveTabs ? insets.bottom + BOTTOM_BAR_GAP + BOTTOM_BAR_HEIGHT : 0); + // Gdy pasek chowa sie na scroll, FAB zjezdza o tyle samo (ta sama wartosc Animated). + const fabShift = liftAboveTabs ? insets.bottom + BOTTOM_BAR_GAP + BOTTOM_BAR_HEIGHT + 6 : 0; + const fabTranslate = barHidden.interpolate({ inputRange: [0, 1], outputRange: [0, fabShift] }); const onPress = useCallback(async () => { let captured: string | null = null; @@ -244,14 +248,18 @@ export function BugReportFAB({ client, appVersion, navRef }: Props) { return ( <> {!hidden ? ( - - ? - {unseen > 0 ? ( - - {unseen > 9 ? '9+' : unseen} - - ) : null} - + + + ? + {unseen > 0 ? ( + + {unseen > 9 ? '9+' : unseen} + + ) : null} + + ) : null} { + setBarHidden(false); + }, []), + ); + return ( {/* Gornej belki nie ma (2026-09-07): nazwe sekcji mowi pasek na dole, kolo zebate diff --git a/mobile/src/screens/MoviesScreen.tsx b/mobile/src/screens/MoviesScreen.tsx index 9495e73..7a160de 100644 --- a/mobile/src/screens/MoviesScreen.tsx +++ b/mobile/src/screens/MoviesScreen.tsx @@ -20,7 +20,7 @@ import { MovieFilters, MovieFiltersSheet, } from '../components/MovieFiltersSheet'; -import { useBottomBarInset, useScrollTopListener } from '../lib/bottomBar'; +import { useAutoHideOnScroll, useBottomBarInset, useScrollTopListener } from '../lib/bottomBar'; import { usePersistedState } from '../lib/persistedState'; import { MoviePosterCard } from '../components/MoviePosterCard'; import type { MovieOut } from '../types'; @@ -51,6 +51,7 @@ export function MoviesScreen() { React.useCallback(() => listRef.current?.scrollToOffset({ offset: 0, animated: true }), []), ); const bottomInset = useBottomBarInset(); + const onListScroll = useAutoHideOnScroll(); // seenSince: ISO timestamp ostatniego "kompletnie obejrzanej listy". Movies // z created_at > seenSince mają NEW badge. Ładujemy z SecureStore raz // przy mount; aktualizujemy gdy app idzie do tła (user widzi NEW przez @@ -152,6 +153,8 @@ export function MoviesScreen() { ref={listRef} data={items} numColumns={NUM_COLS} + onScroll={onListScroll} + scrollEventThrottle={16} keyExtractor={(m) => m.id} // Android removeClippedSubviews=true blankuje plakaty po scrollu (expo-image // nie re-renderuje odpiętych), ten sam bug co "znikające miniaturki" scen. diff --git a/mobile/src/screens/ScenesScreen.tsx b/mobile/src/screens/ScenesScreen.tsx index efc9c56..7f83b3b 100644 --- a/mobile/src/screens/ScenesScreen.tsx +++ b/mobile/src/screens/ScenesScreen.tsx @@ -23,7 +23,7 @@ import { SettingsButton, Toolbar, } from '../ui'; -import { useBottomBarInset, useScrollTopListener } from '../lib/bottomBar'; +import { useAutoHideOnScroll, useBottomBarInset, useScrollTopListener } from '../lib/bottomBar'; import { SceneTile, sceneGridProps } from '../components/SceneTile'; import { SceneGridSkeleton } from '../components/SceneGridSkeleton'; import { Thumb } from '../components/Thumb'; @@ -75,6 +75,7 @@ export function ScenesScreen({ React.useCallback(() => listRef.current?.scrollToOffset({ offset: 0, animated: true }), []), ); const bottomInset = useBottomBarInset(); + const onListScroll = useAutoHideOnScroll(); React.useEffect(() => { const t = setTimeout(() => setDebouncedQ(q), 350); @@ -273,6 +274,8 @@ export function ScenesScreen({ ref={listRef} {...sceneGridProps(gridColumns)} data={items} + onScroll={onListScroll} + scrollEventThrottle={16} keyExtractor={(s) => s.id} // removeClippedSubviews + okno renderowania (windowSize itd.) idą z sceneGridProps // patrz SceneTile. Perf bug-report 5b7ca1e1 (jank/obciążenie przy długim scrollu). diff --git a/mobile/src/screens/SitesScreen.tsx b/mobile/src/screens/SitesScreen.tsx index 82710ea..182991b 100644 --- a/mobile/src/screens/SitesScreen.tsx +++ b/mobile/src/screens/SitesScreen.tsx @@ -23,7 +23,7 @@ import type { RootStackParamList } from '../navigation'; import { theme } from '../theme'; import { PillButton, SCREEN_PAD_H, SCREEN_PAD_TOP, SearchInput, SettingsButton, Toolbar } from '../ui'; import type { SourceOut, SourceRating } from '../types'; -import { useBottomBarInset, useScrollTopListener } from '../lib/bottomBar'; +import { useAutoHideOnScroll, useBottomBarInset, useScrollTopListener } from '../lib/bottomBar'; type Order = 'popular' | 'recent'; @@ -42,6 +42,7 @@ export function SitesScreen() { React.useCallback(() => listRef.current?.scrollToOffset({ offset: 0, animated: true }), []), ); const bottomInset = useBottomBarInset(); + const onListScroll = useAutoHideOnScroll(); React.useEffect(() => { const t = setTimeout(() => setDebouncedQ(q), 250); @@ -108,7 +109,9 @@ export function SitesScreen() { setOrder('popular')} /> setOrder('recent')} /> - long-press a site to pin it to the top + {favQuery.data && pinned.size === 0 ? ( + long-press a site to pin it to the top + ) : null} {isLoading && } {error instanceof Error && {error.message}} @@ -117,6 +120,8 @@ export function SitesScreen() { ref={listRef} data={items} keyExtractor={(s) => s.origin} + onScroll={onListScroll} + scrollEventThrottle={16} numColumns={2} columnWrapperStyle={styles.gridRow} renderItem={({ item }) => (