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 }) => (