import React, { useMemo, useState } from 'react';
import { View, Text, Modal, ScrollView, TouchableOpacity, Linking } from 'react-native';
import { X, ChevronDown, ChevronRight, ExternalLink, Scale } from 'lucide-react-native';
import { CREDITS, LICENSE_TEXTS, CreditEntry } from '../utils/credits';
// About + open-source credits, reachable from the settings sheet. Third-party
// data is generated from node_modules (src/utils/credits.ts + THIRD_PARTY_NOTICES.md);
// each project row links to its homepage, each license group expands to the
// full license text embedded in the app.
const APP_NAME = 'recipescamera';
const APP_VERSION = '1.2'; // keep in sync with app.json
function Group({
license,
entries,
}: {
license: string;
entries: CreditEntry[];
}) {
const [open, setOpen] = useState(false);
const text = LICENSE_TEXTS[license];
return (
setOpen((v) => !v)}
className="flex-row items-center justify-between px-3 py-2.5"
activeOpacity={0.7}
>
{license}
{entries.length} {entries.length === 1 ? 'component' : 'components'}
{open ? (
) : (
)}
{open && (
{entries.map((e) => (
{e.name}
{e.url.startsWith('http') && (
Linking.openURL(e.url)}
accessibilityLabel={`Open ${e.name} project page`}
className="flex-row items-center"
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
>
)}
{e.copyright ? (
{e.copyright}
) : null}
{e.version}
))}
{text ? (
{text}
) : null}
)}
);
}
export default function CreditsModal({
visible,
onClose,
}: {
visible: boolean;
onClose: () => void;
}) {
const groups = useMemo(() => {
const byLicense = new Map();
for (const e of CREDITS) {
const arr = byLicense.get(e.license) ?? [];
arr.push(e);
byLicense.set(e.license, arr);
}
// stable order: licenses as declared in LICENSE_TEXTS, then rest
const order = Object.keys(LICENSE_TEXTS);
const sorted = [...byLicense.keys()].sort(
(a, b) => order.indexOf(a) - order.indexOf(b)
);
return sorted
.filter((k) => byLicense.has(k))
.map((k) => ({ license: k, entries: byLicense.get(k)! }));
}, []);
return (
ABOUT & CREDITS
{APP_NAME}
version {APP_VERSION} · film-recipe camera (experimental)
Author: Lộc Phạm
Linking.openURL('mailto:tranloclqd@gmail.com')}
accessibilityLabel="Email author"
className="flex-row items-center ml-3"
hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}
>
tranloclqd@gmail.com
© 2026 Lộc Phạm · Built with React Native + Expo; icons by Lucide;
on-screen graphics rendered with Skia.
{groups.map((g) => (
))}
);
}