diff --git a/client-app/app/(app)/feed/index.tsx b/client-app/app/(app)/feed/index.tsx index ccfd673..4815ab1 100644 --- a/client-app/app/(app)/feed/index.tsx +++ b/client-app/app/(app)/feed/index.tsx @@ -21,7 +21,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { router } from 'expo-router'; import { TabHeader } from '@/components/TabHeader'; -import { PostCard } from '@/components/feed/PostCard'; +import { PostCard, PostSeparator } from '@/components/feed/PostCard'; import { useStore } from '@/lib/store'; import { fetchTimeline, fetchForYou, fetchTrending, fetchStats, bumpView, @@ -218,6 +218,7 @@ export default function FeedScreen() { onDeleted={onDeleted} /> )} + ItemSeparatorComponent={PostSeparator} refreshControl={ - {/* Floating compose button — pinned to the bottom-right corner - with 14px side inset. Vertical offset clears the 5-icon NavBar - (which lives below this view in the same layer) by sitting - ~14px above its top edge. */} - router.push('/(app)/compose' as never)} - style={({ pressed }) => ({ + {/* Floating compose button. + * + * Wrapped in a StyleSheet.absoluteFill container with pointerEvents + * "box-none" so only the FAB captures touches — taps anywhere else + * pass through to the FlatList below. + * + * Inside the wrapper, alignSelf: 'flex-end' pins to the right; + * bottom inset leaves ~14px clearance above the NavBar (≈56px tall + * + safe-area-bottom). Explicit `right: 14` is belt-and-braces + * for RTL / platform quirks where alignSelf alone might not pin. */} + - - + router.push('/(app)/compose' as never)} + style={({ pressed }) => ({ + position: 'absolute', + right: 14, + bottom: Math.max(insets.bottom, 8) + 70, + width: 56, height: 56, + borderRadius: 28, + backgroundColor: pressed ? '#1a8cd8' : '#1d9bf0', + alignItems: 'center', justifyContent: 'center', + shadowColor: '#000', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.5, + shadowRadius: 6, + elevation: 8, + })} + > + + + ); } diff --git a/client-app/app/(app)/feed/tag/[tag].tsx b/client-app/app/(app)/feed/tag/[tag].tsx index a262582..a5bafb4 100644 --- a/client-app/app/(app)/feed/tag/[tag].tsx +++ b/client-app/app/(app)/feed/tag/[tag].tsx @@ -14,7 +14,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Header } from '@/components/Header'; import { IconButton } from '@/components/IconButton'; -import { PostCard } from '@/components/feed/PostCard'; +import { PostCard, PostSeparator } from '@/components/feed/PostCard'; import { useStore } from '@/lib/store'; import { fetchHashtag, fetchStats, type FeedPostItem } from '@/lib/feed'; @@ -94,6 +94,7 @@ export default function HashtagScreen() { onStatsChanged={onStatsChanged} /> )} + ItemSeparatorComponent={PostSeparator} refreshControl={ ({ flexDirection: 'row', paddingHorizontal: 16, - paddingTop: compact ? 12 : 16, - paddingBottom: compact ? 14 : 18, + paddingTop: compact ? 14 : 18, + paddingBottom: compact ? 16 : 20, backgroundColor: pressed ? '#080808' : 'transparent', - borderBottomWidth: 1, - borderBottomColor: '#222222', })} > {/* Avatar column */} @@ -321,6 +319,20 @@ const _imgKeep = Image; export const PostCard = React.memo(PostCardInner); +/** + * PostSeparator — visible divider line between post cards. Exported so + * every feed surface (timeline, author, hashtag, post detail) can pass + * it as ItemSeparatorComponent and get identical spacing / colour. + * + * The line colour (#2a2a2a) is the minimum grey that reads on OLED + * black under mobile-bright-mode gamma — go darker and the seam vanishes. + * Height 1 is one logical px (hairline on retina). No horizontal inset: + * Twitter runs the seam edge-to-edge and it looks cleaner than a gap. + */ +export function PostSeparator() { + return ; +} + // ── Inline helpers ────────────────────────────────────────────────────── /** ActionButton — small icon + optional label. */