Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/expo/app.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
"light": "./assets/billion-logo.png",
"dark": "./assets/billion-logo.png"
},
"infoPlist": { "ITSAppUsesNonExemptEncryption": false, "LSApplicationCategoryType": "public.app-category.news" }
"infoPlist": {
"ITSAppUsesNonExemptEncryption": false,
"LSApplicationCategoryType": "public.app-category.news"
}
},
"android": {
"package": "app.billionnews.billion",
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/expo-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/// <reference types="expo/types" />

// NOTE: This file should not be edited and should be in your git ignore
// NOTE: This file should not be edited and should be in your git ignore
4 changes: 1 addition & 3 deletions apps/expo/src/app/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ export default function TabLayout() {
name="index"
options={{
title: "Browse",
tabBarIcon: ({ color }) => (
<TabBarIcon name="search" color={color} />
),
tabBarIcon: ({ color }) => <TabBarIcon name="search" color={color} />,
headerShown: false,
}}
/>
Expand Down
5 changes: 1 addition & 4 deletions apps/expo/src/app/(tabs)/feed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,7 @@ export default function FeedScreen() {
activeOpacity={0.85}
>
<Text
style={[
styles.readButtonText,
{ color: theme.primaryForeground },
]}
style={[styles.readButtonText, { color: theme.primaryForeground }]}
>
Read Full Article
</Text>
Expand Down
26 changes: 14 additions & 12 deletions apps/expo/src/app/article-detail.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { RenderRules } from "@ronradtke/react-native-markdown-display";
import { useState } from "react";
import {
ActivityIndicator,
Expand All @@ -6,14 +7,12 @@ import {
StyleSheet,
TouchableOpacity,
} from "react-native";
import Markdown, {
type RenderRules,
} from "@ronradtke/react-native-markdown-display";
import { SafeAreaView } from "react-native-safe-area-context";
import { Image } from "expo-image";
import { Stack, useLocalSearchParams, useRouter } from "expo-router";
import { Ionicons } from "@expo/vector-icons";
import Markdown from "@ronradtke/react-native-markdown-display";
import { useQuery } from "@tanstack/react-query";
import { Image } from "expo-image";

import { AIDisclaimerBanner } from "~/components/AIDisclaimerBanner";
import { Text, View } from "~/components/Themed";
Expand Down Expand Up @@ -162,17 +161,16 @@ export default function ArticleDetailScreen() {
allowedImageHandlers,
defaultImageHandler,
) => {
const { src, alt } = node.attributes;
const show = allowedImageHandlers.some((value) =>
/* eslint-disable */
const src = String(node.attributes.src ?? "");
const alt = node.attributes.alt ? String(node.attributes.alt) : undefined;
const show = allowedImageHandlers.some((value: string) =>
src.toLowerCase().startsWith(value.toLowerCase()),
);

if (!show && defaultImageHandler === null) {
return null;
}

const imageUri = show ? src : `${defaultImageHandler}${src}`;

return (
<Image
key={node.key}
Expand All @@ -184,6 +182,7 @@ export default function ArticleDetailScreen() {
accessibilityLabel={alt}
/>
);
/* eslint-enable */
},
};

Expand All @@ -201,16 +200,19 @@ export default function ArticleDetailScreen() {
};

const activeContent =
selectedTab === "article" ? content.articleContent : content.originalContent;
selectedTab === "article"
? content.articleContent
: content.originalContent;
const looksLikeMarkdown =
/^#{1,6}\s/m.test(activeContent) ||
/\[[^\]]+\]\((https?:\/\/|\/)/.test(activeContent) ||
/(^|\n)([-*+]|\d+\.)\s/m.test(activeContent) ||
/(^|\n)>\s/m.test(activeContent) ||
/!\[[^\]]*\]\(/.test(activeContent) ||
/```/.test(activeContent);
activeContent.includes("```");
const shouldRenderMarkdown =
activeContent.length <= 20000 && (content.isAIGenerated || looksLikeMarkdown);
activeContent.length <= 20000 &&
(content.isAIGenerated || looksLikeMarkdown);

return (
<>
Expand Down
70 changes: 57 additions & 13 deletions apps/expo/src/app/settings/terms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { SafeAreaView } from "react-native-safe-area-context";
import { useRouter } from "expo-router";
import { Ionicons } from "@expo/vector-icons";

import type { Theme } from "~/styles";
import { Text, View } from "~/components/Themed";
import { colors, fonts, sp, useTheme, type Theme } from "~/styles";
import { colors, fonts, sp, useTheme } from "~/styles";

const LAST_UPDATED = "April 5, 2026";

Expand Down Expand Up @@ -98,11 +99,27 @@ const PRIVACY_SECTIONS = [
},
];

function DocSection({ title, body, theme }: { title: string; body: string; theme: Theme }) {
function DocSection({
title,
body,
theme,
}: {
title: string;
body: string;
theme: Theme;
}) {
return (
<View style={styles.section} lightColor="transparent" darkColor="transparent">
<Text style={[styles.sectionTitle, { color: theme.foreground }]}>{title}</Text>
<Text style={[styles.sectionBody, { color: theme.textSecondary }]}>{body}</Text>
<View
style={styles.section}
lightColor="transparent"
darkColor="transparent"
>
<Text style={[styles.sectionTitle, { color: theme.foreground }]}>
{title}
</Text>
<Text style={[styles.sectionBody, { color: theme.textSecondary }]}>
{body}
</Text>
</View>
);
}
Expand All @@ -112,9 +129,18 @@ export default function TermsScreen() {
const { theme } = useTheme();

return (
<SafeAreaView style={[styles.container, { backgroundColor: theme.background }]} edges={["top"]}>
<SafeAreaView
style={[styles.container, { backgroundColor: theme.background }]}
edges={["top"]}
>
<View
style={[styles.header, { borderBottomColor: theme.border, backgroundColor: theme.background }]}
style={[
styles.header,
{
borderBottomColor: theme.border,
backgroundColor: theme.background,
},
]}
>
<TouchableOpacity
onPress={() => router.back()}
Expand All @@ -123,28 +149,46 @@ export default function TermsScreen() {
>
<Ionicons name="chevron-back" size={22} color={colors.white} />
</TouchableOpacity>
<Text style={[styles.title, { color: theme.foreground }]}>Terms & Privacy</Text>
<View style={{ width: 44 }} lightColor="transparent" darkColor="transparent" />
<Text style={[styles.title, { color: theme.foreground }]}>
Terms & Privacy
</Text>
<View
style={{ width: 44 }}
lightColor="transparent"
darkColor="transparent"
/>
</View>

<ScrollView style={styles.scroll} showsVerticalScrollIndicator={false}>
<Text style={[styles.lastUpdated, { color: theme.mutedForeground }]}>
Last updated {LAST_UPDATED}
</Text>

<Text style={[styles.docTitle, { color: theme.foreground }]}>Terms of Service</Text>
<Text style={[styles.docTitle, { color: theme.foreground }]}>
Terms of Service
</Text>
{TERMS_SECTIONS.map((s) => (
<DocSection key={s.title} {...s} theme={theme} />
))}

<View style={[styles.divider, { backgroundColor: theme.border }]} lightColor={theme.border} darkColor={theme.border} />
<View
style={[styles.divider, { backgroundColor: theme.border }]}
lightColor={theme.border}
darkColor={theme.border}
/>

<Text style={[styles.docTitle, { color: theme.foreground }]}>Privacy Policy</Text>
<Text style={[styles.docTitle, { color: theme.foreground }]}>
Privacy Policy
</Text>
{PRIVACY_SECTIONS.map((s) => (
<DocSection key={s.title} {...s} theme={theme} />
))}

<View style={{ height: sp[10] }} lightColor="transparent" darkColor="transparent" />
<View
style={{ height: sp[10] }}
lightColor="transparent"
darkColor="transparent"
/>
</ScrollView>
</SafeAreaView>
);
Expand Down
21 changes: 18 additions & 3 deletions apps/nextjs/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -586,10 +586,25 @@ export default function LandingPage() {
</span>
<div
className="flex items-center gap-5 text-[13px]"
style={{ color: "rgba(255,255,255,0.25)", fontFamily: "var(--font-albert-sans)" }}
style={{
color: "rgba(255,255,255,0.25)",
fontFamily: "var(--font-albert-sans)",
}}
>
<Link href="/terms" className="transition-colors duration-150 hover:text-white" style={{ color: "rgba(255,255,255,0.4)", textDecoration: "none" }}>Terms</Link>
<Link href="/privacy" className="transition-colors duration-150 hover:text-white" style={{ color: "rgba(255,255,255,0.4)", textDecoration: "none" }}>Privacy</Link>
<Link
href="/terms"
className="transition-colors duration-150 hover:text-white"
style={{ color: "rgba(255,255,255,0.4)", textDecoration: "none" }}
>
Terms
</Link>
<Link
href="/privacy"
className="transition-colors duration-150 hover:text-white"
style={{ color: "rgba(255,255,255,0.4)", textDecoration: "none" }}
>
Privacy
</Link>
<span>© 2026 Billion. All rights reserved.</span>
</div>
</footer>
Expand Down
58 changes: 47 additions & 11 deletions apps/nextjs/src/app/privacy/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Link from "next/link";
import type { Metadata } from "next";
import Link from "next/link";

export const metadata: Metadata = {
title: "Privacy Policy — Billion",
Expand Down Expand Up @@ -58,28 +58,41 @@ const SECTIONS = [

export default function PrivacyPage() {
return (
<main className="min-h-screen" style={{ backgroundColor: C.deepNavy, color: "#fff" }}>
<main
className="min-h-screen"
style={{ backgroundColor: C.deepNavy, color: "#fff" }}
>
<nav
className="flex items-center justify-between px-6 py-5"
style={{ maxWidth: 1120, margin: "0 auto" }}
>
<Link
href="/"
className="text-[22px] font-bold tracking-[-0.02em] text-white"
style={{ fontFamily: "var(--font-ibm-plex-serif), Georgia, serif", textDecoration: "none" }}
style={{
fontFamily: "var(--font-ibm-plex-serif), Georgia, serif",
textDecoration: "none",
}}
>
Billion
</Link>
<Link
href="/terms"
className="text-[15px] font-medium transition-colors duration-150 hover:text-white"
style={{ color: "rgba(255,255,255,0.6)", fontFamily: "var(--font-albert-sans)", textDecoration: "none" }}
style={{
color: "rgba(255,255,255,0.6)",
fontFamily: "var(--font-albert-sans)",
textDecoration: "none",
}}
>
Terms of Service
</Link>
</nav>

<article className="mx-auto px-6 py-12 md:py-16" style={{ maxWidth: 720 }}>
<article
className="mx-auto px-6 py-12 md:py-16"
style={{ maxWidth: 720 }}
>
<p
className="mb-2 text-[12px] font-medium tracking-[0.1em] uppercase"
style={{ color: C.general, fontFamily: "var(--font-albert-sans)" }}
Expand All @@ -88,7 +101,10 @@ export default function PrivacyPage() {
</p>
<h1
className="mb-10 leading-[1.15] font-bold tracking-[-0.02em] text-white"
style={{ fontFamily: "var(--font-ibm-plex-serif), Georgia, serif", fontSize: "clamp(2rem, 4vw, 3rem)" }}
style={{
fontFamily: "var(--font-ibm-plex-serif), Georgia, serif",
fontSize: "clamp(2rem, 4vw, 3rem)",
}}
>
Privacy Policy
</h1>
Expand All @@ -104,7 +120,10 @@ export default function PrivacyPage() {
</h2>
<p
className="m-0 text-[16px] leading-[1.7]"
style={{ color: "rgba(255,255,255,0.7)", fontFamily: "var(--font-albert-sans)" }}
style={{
color: "rgba(255,255,255,0.7)",
fontFamily: "var(--font-albert-sans)",
}}
>
{s.body}
</p>
Expand All @@ -119,17 +138,34 @@ export default function PrivacyPage() {
>
<span
className="text-[18px] font-bold"
style={{ color: "rgba(255,255,255,0.4)", fontFamily: "var(--font-ibm-plex-serif), Georgia, serif" }}
style={{
color: "rgba(255,255,255,0.4)",
fontFamily: "var(--font-ibm-plex-serif), Georgia, serif",
}}
>
Billion
</span>
<div
className="flex items-center gap-5 text-[13px]"
style={{ fontFamily: "var(--font-albert-sans)" }}
>
<Link href="/terms" className="hover:text-white transition-colors duration-150" style={{ color: "rgba(255,255,255,0.4)", textDecoration: "none" }}>Terms</Link>
<Link href="/privacy" className="hover:text-white transition-colors duration-150" style={{ color: "rgba(255,255,255,0.4)", textDecoration: "none" }}>Privacy</Link>
<span style={{ color: "rgba(255,255,255,0.25)" }}>© 2026 Billion. All rights reserved.</span>
<Link
href="/terms"
className="transition-colors duration-150 hover:text-white"
style={{ color: "rgba(255,255,255,0.4)", textDecoration: "none" }}
>
Terms
</Link>
<Link
href="/privacy"
className="transition-colors duration-150 hover:text-white"
style={{ color: "rgba(255,255,255,0.4)", textDecoration: "none" }}
>
Privacy
</Link>
<span style={{ color: "rgba(255,255,255,0.25)" }}>
© 2026 Billion. All rights reserved.
</span>
</div>
</footer>
</main>
Expand Down
Loading
Loading