# harbour-henflow Porting **nativo Sailfish OS** dell'app Android *Henflow* (`it.carlobaratto.henflow`). UI in QML/Silica, calcolo e dati locali, pubblicazione prevista sullo **store Jolla Harbour**. Il porting **non** usa il runtime Flutter community (`flutter-sailfishos`): quello resta una via OpenRepos, non Harbour-safe, e non supporta i plugin nativi Android (AdMob/File picker/Share). Nessun account, nessun cloud, come l'originale. ## Sorgenti di riferimento - Flutter (originale): `workspace/henflow-flutter` — export da Nextcloud, repo Gitea `HenFlow`. - Screenshot dello store usati come riferimento UI. ## Stato | parte | stato | |---|---| | Packaging (spec RPM, desktop SailJail, icone, i18n) | pronto | | Livello dati C++ (`src/datastore.*`, JSON compatibile con l'app Android) | fatto e verificato | | Motore di calcolo (`qml/js/henflow.js`) | portato e verificato con node | | Traduzioni 4 lingue (`qml/js/i18n.js`, 126 chiavi) | portate e verificate con node | | Catalogo razze con foto (`qml/js/catalog.js`) | portato e verificato con node | | Home con produzione stimata e coda ordini | fatta | | Scheda ordine (nuovo/modifica, data calcolata) | fatta | | Cover con produzione e ordini pendenti | fatta | | Razze / pollaio / galline, clienti, backup, lingua, intro | da fare | ## Struttura ``` harbour-henflow.pro # QT += quick qml, INSTALLS icone rpm/harbour-henflow.spec # lrelease + desktop cercati con find (layout sorgente variabile) rpm/harbour-henflow.desktop # profilo [X-Sailjail] esplicito (dati persistenti) src/main.cpp # SailfishApp, context property appSettings/appData, lingua src/settings.h/.cpp # QSettings su path esplicito AppConfigLocation + sync() src/datastore.h/.cpp # dati in un JSON in AppConfigLocation (chiavi = SharedPreferences dell'app) qml/harbour-henflow.qml # ApplicationWindow: initialPage + cover qml/pages/MainPage.qml # Home: produzione stimata, coda ordini, menu nuovo ordine qml/pages/OrderPage.qml # nuovo/modifica ordine con anteprima della data di consegna qml/cover/CoverPage.qml # produzione del giorno e ordini pendenti qml/js/henflow.js # motore di calcolo (ES5, testabile con node) qml/js/i18n.js # 126 chiavi x 4 lingue + t(chiave, arg, lingua) qml/js/catalog.js # 7 razze: nome/descrizione/colore uova/foto assets/breeds/*.jpg # foto delle razze (installate nel datadir) tools/gen_from_flutter.py # rigenera i18n.js e catalog.js dai sorgenti Flutter tests/run.sh # motore + traduzioni (node) tests/cpp_test.sh # livello dati (Qt 5.15 locale, config in cartella temporanea) icons/make_icons.py # icone 86/108/128/172 dall'artwork originale translations/*.ts # stringhe statiche QML (il testo dell'app viene da i18n.js) ``` ## Test ```sh sh tests/run.sh # motore di calcolo + traduzioni e catalogo (node, nessuna dipendenza) sh tests/cpp_test.sh # livello dati: compila src/datastore.cpp col Qt locale e prova # persistenza, rilettura, compatibilita' delle chiavi, backup, file corrotto ``` Il testo dell'app vive in `i18n.js` (come il TranslationService dell'originale), quindi la lingua cambia **a runtime** senza riavviare l'app: cosa che i `.ts` di Qt 5.6 non permettono. La lingua predefinita segue quella del telefono. ## Motore di calcolo (portato fedelmente da `lib/main.dart`) - **Produzione stimata** per razza = galline in deposizione x uova/gallina/giorno (catalogo: tutte 1.0). Una gallina conta da **6 mesi** in su (`_ageInMonths`). - **Magazzino**: all'apertura, i giorni passati dall'ultimo aggiornamento aggiungono la produzione giornaliera alle scorte di ogni razza attiva. - **Coda FIFO**: ordini in ordine di creazione; ognuno consuma le scorte virtuali della sua razza, chi le trova consegna oggi, gli altri aspettano `ceil(uova_mancanti / produzione)` giorni. Gli ordini gia' consegnati non consumano scorte. - **Stima per un nuovo ordine**: `ceil((pendenti + richieste - scorte) / produzione)`. Le date sono sempre normalizzate a `YYYY-MM-DD` e le funzioni non mutano l'input. ## Modello dati (fedele a `StorageService`, SharedPreferences + JSON) | chiave | contenuto | |---|---| | `breed_settings_v2` | `[{breedId, hens:[{id, name, birthDate, isLaying}]}]` | | `reservations_v2` | `[{id, customerName, breedId, eggsRequested, createdAt, deliveryDate, isDelivered}]` | | `customers_v1` | `[{id, firstName, lastName, phoneNumber}]` | | `warehouse_stock_v1` | `{breedId: uova}` | | `last_production_update_v1` | data ISO dell'ultimo accrual | Nel porting: un unico file JSON in `~/.config/harbour/henflow/` (nessuna dipendenza extra, nessun driver SQL da verificare sul device) con la stessa struttura, cosi' il backup/ripristino resta compatibile con l'app Android. ## Schermate mappate (da sorgenti + screenshot) | schermata Flutter | contenuto | porting | |---|---|---| | `OnboardingPage` | 4 passi introduttivi | da fare | | `ModeSelectionPage` | modalita' allevatore / cliente | da decidere | | `HomePage` | tab *Orders list* / *Calendar* (+ magazzino), card produzione stimata, card ordini con stato e azioni (condividi/modifica/elimina), FAB *New order* | da fare | | `SettingsPage` | tab *Available breeds* (catalogo con foto e spunta) / *My coop* (galline registrate, in deposizione, produzione) + backup/ripristino JSON + lingua | da fare | | `BreedDetailPage` | galline della razza, eta' in mesi, stato deposizione | da fare | | `CustomersPage` + dialoghi | anagrafica clienti, nuovo ordine | da fare | | `AboutPage` | info app | da fare | | `CoopIcon` (CustomPainter) | icona pollaio disegnata a mano | da ridisegnare (Canvas QML o PNG) | Differenze da tenere presenti: **niente annunci** (AdMob non esiste su Sailfish), **share/export** via file JSON in Download invece di `share_plus`/`file_picker`. ## Build e deploy Sul PC con l'SDK Sailfish (qui non c'e' sfdk): ```sh sfdk target use SailfishOS-5.0.0.62-aarch64 # o il target del tuo device sfdk build # RPM in build-*/RPMS/ sfdk deploy # oppure Qt Creator ``` Tarball per lo store (con `--prefix`, esclude .git e i file non tracciati): ```sh git archive --format=tar.gz --prefix=harbour-henflow-0.1.0/ -o harbour-henflow-0.1.0.tar.gz HEAD ``` ## Aperti 1. **Schermate da fare**: razze disponibili / mio pollaio / anagrafica galline, clienti, backup-ripristino, scelta lingua, introduzione iniziale, calendario. 2. **Verifica su device**: la UI Silica non e' verificabile senza SDK/dispositivo (qui si verificano solo motore, traduzioni, catalogo e livello dati). ## Correzioni rispetto all'originale (bug trovati nei sorgenti Flutter) - `app_language.dart` e' una copia **vecchia** della tabella traduzioni (111 chiavi contro 126), usata solo da `lib/dialogs/confirm_dialogs.dart`, e ha gli a capo scritti `\\n`: quei dialoghi mostrano un backslash visibile. Il porting estrae dalla tabella viva in `main.dart`. - Il francese ha 2 chiavi in meno (`name_generic`, `desc_generic`) e `t()` non ha fallback: in francese la razza "generic" usciva come testo `name_generic`. Nel porting i buchi sono colmati dall'inglese e un test lo verifica. - `assets/breeds/olive.jpg` era un **WEBP** con estensione .jpg: Qt 5.6 su Sailfish non ha il plugin webp. Riconvertito in JPEG. - Niente annunci (AdMob non esiste su Sailfish) e niente `share_plus`/`file_picker`: export/import JSON in Download.