/** * Una ricetta del ricettario di Quanto Basta: si legge com'è, e con un tocco * si aggiunge al proprio ricettario (ricettaDaCatalogo). Da lì si apre nel * Dosatore, dove si riscala e si modifica come le altre. * * Se una ricetta con lo stesso titolo c'è già nel ricettario dell'utente, il * tasto non la aggiunge una seconda volta: la apre (giaNelRicettario). * * I colori non si scrivono qui: vengono da tema.ts, come nelle altre schermate. */ import { useCallback, useMemo, useRef, useState } from 'react'; import { useFocusEffect } from '@react-navigation/native'; import { Alert, ScrollView, StyleSheet, Text, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { ricettaCatalogo } from '../../catalogo/catalogo.ts'; import { portata, regione } from '../../catalogo/regioni.ts'; import { elencoCategorie, salvaCategoria } from '../../data/categorie.ts'; import { elencoRicette, salvaRicetta } from '../../data/ricette.ts'; import { formattaQuantita } from '../../domain/format.ts'; import { vocabolario } from '../../domain/lingua/index.ts'; import type { Categoria, Ricetta } from '../../domain/types.ts'; import Barra from '../componenti/Barra.tsx'; import { BottonePrincipale, BottoneSecondario } from '../componenti/Bottoni.tsx'; import { TitoloBlocco } from '../componenti/Campo.tsx'; import { useApp } from '../contesto.ts'; import { giaNelRicettario, ricettaDaCatalogo } from '../logica-catalogo.ts'; import type { PropsSchermata } from '../navigazione.ts'; import { creaScrittoreUnico } from '../scritturaUnica.ts'; import type { Colori } from '../tema.ts'; import { CIFRE, RAGGIO, SPAZIO, TESTO, useColori } from '../tema.ts'; export default function RicettaItaliana({ navigation, route }: PropsSchermata<'RicettaItaliana'>) { const { db, lingua, testo } = useApp(); const colori = useColori(); const stili = useMemo(() => creaStili(colori), [colori]); const sotto = useSafeAreaInsets().bottom; const voc = useMemo(() => vocabolario(lingua), [lingua]); const r = ricettaCatalogo(route.params.id); const [suoi, setSuoi] = useState(null); const [categorie, setCategorie] = useState([]); // Due tocchi veloci su «Aggiungi» non devono aggiungere due ricette. const scriviUnaSola = useRef(creaScrittoreUnico()).current; // Si rilegge ogni volta che la pagina torna in primo piano: aggiunta la // ricetta si apre il Dosatore, e tornando qui il tasto deve dire «aprila», // non proporre di aggiungerla una seconda volta. useFocusEffect( useCallback(() => { let vivo = true; Promise.all([elencoRicette(db, { tipo: 'tutte' }), elencoCategorie(db)]) .then(([ricette, cat]) => { if (!vivo) return; setSuoi(ricette); setCategorie(cat); }) .catch(() => { if (vivo) setSuoi([]); }); return () => { vivo = false; }; }, [db]), ); if (r === undefined) return ; const gia = suoi === null ? null : giaNelRicettario(r, suoi); const aggiungi = () => scriviUnaSola(async () => { const { ricetta, categoriaNuova } = ricettaDaCatalogo(r, categorie, lingua, new Date().toISOString()); try { if (categoriaNuova !== null) await salvaCategoria(db, categoriaNuova); await salvaRicetta(db, ricetta); navigation.navigate('Dosatore', { ricettaId: ricetta.id }); } catch { Alert.alert(testo('app.nome'), testo('errore.scrittura')); } }); const sottotitolo = [ regione(r.regione)?.nome[lingua], portata(r.portata).nome[lingua], testo('catalogo.porzioni', { porzioni: String(r.porzioni) }), ].join(' · '); return ( {r.titolo} {sottotitolo} {r.gruppi.map((g, gi) => ( {g.nome !== null && r.gruppi.length > 1 ? {g.nome} : null} {g.righe.map((x, i) => ( 0 ? stili.separata : null]}> {formattaQuantita(x.quantita, x.unita, voc)} {x.nome} ))} ))} {r.procedimento} {/* Il tasto sta fermo in fondo: la ricetta può essere lunga, e l'azione per cui si è qui non deve finire in fondo alla pagina. */} {gia !== null ? ( navigation.navigate('Dosatore', { ricettaId: gia })} stile={stili.tasto} /> ) : ( void aggiungi()} spento={suoi === null} /> )} ); } const creaStili = (colori: Colori) => StyleSheet.create({ schermo: { flex: 1, backgroundColor: colori.fondo }, testa: { paddingHorizontal: SPAZIO.l, paddingTop: SPAZIO.s, gap: SPAZIO.xs }, titolo: { ...TESTO.titolo, fontSize: 27, lineHeight: 32, color: colori.testo }, sotto: { ...TESTO.etichetta, color: colori.testoTenue }, sezione: { ...TESTO.piccolo, fontWeight: '700', letterSpacing: 0.6, textTransform: 'uppercase', color: colori.testoTenue, paddingHorizontal: SPAZIO.l + 14, paddingTop: SPAZIO.m, paddingBottom: 6, }, riquadro: { marginHorizontal: SPAZIO.l, backgroundColor: colori.superficie, borderRadius: RAGGIO.riga, overflow: 'hidden' }, riga: { flexDirection: 'row', alignItems: 'baseline', gap: SPAZIO.m, paddingVertical: 12, paddingHorizontal: 14 }, separata: { borderTopWidth: StyleSheet.hairlineWidth, borderTopColor: colori.bordo }, quantita: { minWidth: 72, textAlign: 'right', ...TESTO.corpo, ...CIFRE, fontWeight: '600', color: colori.testo }, qb: { fontWeight: '400', color: colori.testoTenue }, nome: { flex: 1, ...TESTO.corpo, color: colori.testo }, procedimento: { ...TESTO.corpo, lineHeight: 25, color: colori.testo, paddingHorizontal: SPAZIO.l }, piede: { paddingHorizontal: SPAZIO.l, paddingTop: SPAZIO.m, borderTopWidth: StyleSheet.hairlineWidth, borderTopColor: colori.bordo, backgroundColor: colori.fondo, }, tasto: { minHeight: 50 }, });