diff --git a/src/app.tsx b/src/app.tsx
index 8207b33..165ebb4 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -49,6 +49,31 @@ import { ThreadScreen } from "@/screens/ThreadScreen";
const SPLASH_MIN_DISPLAY_MS = 500;
+/** Generates header subtitle based on view type and item count */
+function getHeaderSubtitle(
+ view: View,
+ count: number,
+ hasMore: boolean
+): string | undefined {
+ if (count <= 0) return undefined;
+
+ // Only show "+" if we have a full page (30+) and there are more to load
+ const suffix = hasMore && count >= 30 ? "+" : "";
+
+ if (view === "notifications") {
+ return `${count} notifications`;
+ }
+ if (view === "bookmarks") {
+ return `${count}${suffix} bookmarked`;
+ }
+ return `${count} posts`;
+}
+
+/** Capitalizes the first letter of a view name for display */
+function getViewTitle(view: View): string {
+ return view.charAt(0).toUpperCase() + view.slice(1);
+}
+
export type View =
| "timeline"
| "bookmarks"
@@ -782,16 +807,17 @@ function AppContent({ client, user }: AppProps) {
) : isMainView ? (
) : null}
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index dbf8c7c..368f5b2 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -1,35 +1,27 @@
-import type { View } from "@/app";
+import type { ReactNode } from "react";
import { colors } from "@/lib/colors";
interface HeaderProps {
- currentView: View;
- postCount?: number;
- hasMore?: boolean;
- unreadNotificationCount?: number;
-}
-
-function getCountLabel(view: View, count: number, hasMore: boolean): string {
- // Only show "+" if we have a full page (30+) and there are more to load
- // If count < 30, we know we have all items regardless of hasMore
- const suffix = hasMore && count >= 30 ? "+" : "";
- if (view === "notifications") {
- return `${count} notifications`;
- }
- if (view === "bookmarks") {
- return `${count}${suffix} bookmarked`;
- }
- return `${count} posts`;
+ /** The main title to display (e.g., "Timeline", "Bookmarks") */
+ title: string;
+ /** Optional subtitle shown in parentheses (e.g., "30+ bookmarked") */
+ subtitle?: string;
+ /** Optional badge count shown with bell icon (e.g., unread notifications) */
+ badge?: number;
+ /** Optional content for the left slot (defaults to "xfeed" brand) */
+ leftContent?: ReactNode;
+ /** Optional content for the right slot */
+ rightContent?: ReactNode;
}
export function Header({
- currentView,
- postCount,
- hasMore = false,
- unreadNotificationCount,
+ title,
+ subtitle,
+ badge,
+ leftContent,
+ rightContent,
}: HeaderProps) {
- const viewLabel = currentView.charAt(0).toUpperCase() + currentView.slice(1);
-
return (
- xfeed
- {unreadNotificationCount !== undefined && unreadNotificationCount > 0 && (
- 🔔 {unreadNotificationCount}
+ {leftContent ?? xfeed}
+ {badge !== undefined && badge > 0 && (
+ 🔔 {badge}
)}
|
- {viewLabel}
- {postCount !== undefined && postCount > 0 && (
-
- {" "}
- ({getCountLabel(currentView, postCount, hasMore)})
-
- )}
+ {title}
+ {subtitle && ({subtitle})}
+ {rightContent}
);
}