// Mosaic Mobile — app shell: header, feed, bottom tab bar, sources, sheet state // Depends on: SITE_STORIES, SOURCE_DIR, FAVS (site-data), CardReader/CardPulse (mobile-cards), // StorySheet (mobile-sheet) // Exports: MosaicApp const M_TABS = [ { k: "today", label: "Today", icon: "ph-newspaper", cat: null }, { k: "politics", label: "Politics", icon: "ph-bank", cat: "Politics" }, { k: "economy", label: "Economy", icon: "ph-trend-up", cat: "Economy" }, { k: "sources", label: "Sources", icon: "ph-list-bullets", sources: true }, ]; const SourcesScreen = () => { // One flat list — filter chips do the grouping, no section headings (parity with web). const all = [ ...SOURCE_DIR.outlets.map((r) => ({ ...r, g: "outlets" })), ...SOURCE_DIR.independent.map((r) => ({ ...r, g: "independent" })), ...SOURCE_DIR.youtube.map((r) => ({ ...r, g: "youtube" })), ...SOURCE_DIR.international.map((r) => ({ ...r, g: "international" })), ]; const [filter, setFilter] = React.useState("all"); const rows = filter === "all" ? all : all.filter((r) => r.g === filter); const chips = [["all", "All"], ["outlets", "Outlets"], ["independent", "Independent"], ["youtube", "YouTube"], ["international", "International"]]; return (
Verifiable ownership facts only. Where a source leans is shown per story, based on what it actually published.