← back to Nineoh Guide
App.tsx: add ShowInfo type + show info banner (chips, expandable desc, disclaimer) below show selector
5b12116c4c6c161fcd44e38841762c3c618a18a2 · 2026-08-06 04:17:18 -0700 · steve@designerwallcoverings.com
Files touched
Diff
commit 5b12116c4c6c161fcd44e38841762c3c618a18a2
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date: Thu Aug 6 04:17:18 2026 -0700
App.tsx: add ShowInfo type + show info banner (chips, expandable desc, disclaimer) below show selector
---
apps/mobile/App.tsx | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 117 insertions(+), 1 deletion(-)
diff --git a/apps/mobile/App.tsx b/apps/mobile/App.tsx
index 7cba875..27c5c57 100644
--- a/apps/mobile/App.tsx
+++ b/apps/mobile/App.tsx
@@ -50,7 +50,20 @@ const SELECTED_SHOW_KEY = "nineoh.selectedShow.v1";
// The two shows this guide covers. `canonical_title` matches the DB seed.
const ORIGINAL_CANONICAL = "beverly-hills-90210";
-type Show = { id: string; canonicalTitle: string; displayTitle: string };
+type ShowInfo = {
+ description: string;
+ seasonCount: number;
+ episodeCount: number;
+ premiereDate: string | null;
+ finaleDate: string | null;
+ disclaimerText: string;
+};
+type Show = {
+ id: string;
+ canonicalTitle: string;
+ displayTitle: string;
+ showInfo?: ShowInfo;
+};
// Short, friendly label for the show toggle. Derived from displayTitle but
// pinned to the copy the brief specifies for the two known shows.
@@ -254,6 +267,7 @@ export default function App() {
const [revealed, setRevealed] = useState<Set<string>>(new Set());
const [eggTaps, setEggTaps] = useState(0);
const [egg, setEgg] = useState(false);
+ const [showInfoExpanded, setShowInfoExpanded] = useState(false);
const [appleUser, setAppleUser] = useState<{ id: string; name: string | null } | null>(
null
);
@@ -594,6 +608,66 @@ export default function App() {
</View>
) : null}
+ {/* Show info banner — subtle strip below the selector showing original show details */}
+ {(() => {
+ const sel = shows.find((s) => s.id === selectedShowId);
+ const info = sel?.showInfo;
+ if (!info) return null;
+ // Split description into sentences; show first 2, rest behind "more"
+ const sentences = info.description.match(/[^.!?]+[.!?]+/g) ?? [info.description];
+ const preview = sentences.slice(0, 2).join(" ").trim();
+ const rest = sentences.slice(2).join(" ").trim();
+ const premiereYear = info.premiereDate ? info.premiereDate.slice(0, 4) : null;
+ const finaleYear = info.finaleDate ? info.finaleDate.slice(0, 4) : null;
+ const yearRange =
+ premiereYear && finaleYear ? `${premiereYear}–${finaleYear}` : premiereYear ?? null;
+ return (
+ <View style={styles.showInfoBanner}>
+ <View style={styles.showInfoChips}>
+ {info.seasonCount > 0 && (
+ <View style={styles.showInfoChip}>
+ <Text style={styles.showInfoChipText}>
+ 📺 {info.seasonCount} Season{info.seasonCount !== 1 ? "s" : ""}
+ </Text>
+ </View>
+ )}
+ {info.episodeCount > 0 && (
+ <View style={styles.showInfoChip}>
+ <Text style={styles.showInfoChipText}>🎬 {info.episodeCount} Episodes</Text>
+ </View>
+ )}
+ {yearRange && (
+ <View style={styles.showInfoChip}>
+ <Text style={styles.showInfoChipText}>📅 {yearRange}</Text>
+ </View>
+ )}
+ </View>
+ <Text style={styles.showInfoDesc}>
+ {preview}
+ {rest && !showInfoExpanded ? " " : ""}
+ {rest && !showInfoExpanded ? (
+ <Text
+ style={styles.showInfoMore}
+ onPress={() => setShowInfoExpanded(true)}
+ >
+ more
+ </Text>
+ ) : null}
+ {rest && showInfoExpanded ? ` ${rest}` : ""}
+ {rest && showInfoExpanded ? (
+ <Text
+ style={styles.showInfoMore}
+ onPress={() => setShowInfoExpanded(false)}
+ >
+ {" "}less
+ </Text>
+ ) : null}
+ </Text>
+ <Text style={styles.showInfoDisclaimer}>{info.disclaimerText}</Text>
+ </View>
+ );
+ })()}
+
{egg ? (
<Pressable style={styles.egg} onPress={() => setEgg(false)}>
<Text style={styles.eggEmoji}>📟✨</Text>
@@ -1066,4 +1140,46 @@ const styles = StyleSheet.create({
marginBottom: 10,
},
eggBody: { color: "#fff6ec", fontSize: 14, textAlign: "center", lineHeight: 21 },
+ // Show info banner (below the show selector)
+ showInfoBanner: {
+ paddingHorizontal: 14,
+ paddingTop: 6,
+ paddingBottom: 8,
+ backgroundColor: "rgba(123,63,110,0.06)",
+ borderBottomWidth: 1,
+ borderBottomColor: "rgba(123,63,110,0.10)",
+ },
+ showInfoChips: {
+ flexDirection: "row",
+ flexWrap: "wrap",
+ gap: 5,
+ marginBottom: 6,
+ },
+ showInfoChip: {
+ backgroundColor: "rgba(123,63,110,0.12)",
+ borderRadius: 10,
+ paddingHorizontal: 8,
+ paddingVertical: 3,
+ },
+ showInfoChipText: {
+ fontSize: 11,
+ fontWeight: "600",
+ color: "#5a3a5a",
+ },
+ showInfoDesc: {
+ fontSize: 12,
+ color: "#4a3a52",
+ lineHeight: 18,
+ marginBottom: 4,
+ },
+ showInfoMore: {
+ fontSize: 12,
+ color: "#7b3f6e",
+ fontWeight: "600",
+ },
+ showInfoDisclaimer: {
+ fontSize: 10,
+ color: "#999",
+ lineHeight: 14,
+ },
});
← f7cb8f0 api/shows: add showInfo field (description, seasonCount, epi
·
back to Nineoh Guide
·
Wire real AdMob banner: iosAppId ca-app-pub-5278231299883833 182ad70 →