11 KiB
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 GiteaHenFlow. - 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 disponibili, mio pollaio, registro galline | fatte |
| Clienti (anagrafica) | fatta |
| Backup e ripristino (JSON dell'app Android) | fatto |
| Scelta lingua a runtime (4 lingue) | fatta |
| Introduzione (primo avvio e dalle impostazioni) | fatta |
| Calendario delle consegne | fatto |
Struttura
harbour-henflow.pro # QT += quick qml, INSTALLS icone + foto razze
rpm/harbour-henflow.spec # lrelease + desktop cercati con find (layout sorgente variabile)
rpm/harbour-henflow.desktop # profilo [X-Sailjail] + permesso Documents (backup)
src/main.cpp # SailfishApp, context property appSettings/appData/appVersion
src/settings.h/.cpp # QSettings su path esplicito AppConfigLocation + sync() (lingua, intro vista)
src/datastore.h/.cpp # dati in un JSON in AppConfigLocation + backup nel formato dell'app Android
qml/harbour-henflow.qml # ApplicationWindow: initialPage + cover
qml/pages/MainPage.qml # Home: produzione stimata, coda ordini, menu (ordine, clienti, impostazioni)
qml/pages/OrderPage.qml # nuovo/modifica ordine con anteprima della data di consegna
qml/pages/CalendarPage.qml # calendario delle consegne: griglia del mese + ordini del giorno
qml/pages/SettingsPage.qml # indice delle impostazioni
qml/pages/BreedsPage.qml # razze disponibili (attiva/disattiva con conferma)
qml/pages/CoopPage.qml # mio pollaio: conteggi e produzione per razza
qml/pages/BreedDetailPage.qml# scheda razza + registro galline (deposizione, elimina)
qml/pages/HenPage.qml # nuova/modifica gallina: nome, data di nascita, deposizione
qml/pages/CustomersPage.qml # anagrafica clienti
qml/pages/CustomerPage.qml # nuovo/modifica cliente
qml/pages/BackupPage.qml # esporta in Documenti / copia negli appunti / ripristina da JSON
qml/pages/LanguagePage.qml # lingua (EN/IT/FR/DE) applicata a runtime
qml/pages/IntroPage.qml # introduzione in 4 passi
qml/pages/AboutPage.qml # informazioni e versione
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
tools/check_qml.py # controlli statici: chiavi, simboli JS, pagine, componenti
tests/run.sh # motore + traduzioni (node) + controlli QML + qmllint
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 tests/run.sh # motore di calcolo, traduzioni e catalogo (node), controlli
# statici della QML (chiavi, simboli JS, pagine, componenti) e qmllint
sh tests/cpp_test.sh # livello dati: compila src/datastore.cpp col Qt locale e prova
# persistenza, rilettura, compatibilita' delle chiavi, backup, file corrotto
I controlli statici della QML servono perche' la UI non e' eseguibile senza SDK:
prima di consegnare verificano che ogni chiave di traduzione usata esista in tutte
e quattro le lingue, che le funzioni del motore chiamate esistano, che le pagine
aperte con pageStack esistano davvero e che non si usino componenti inesistenti
(es. Toast, che in Sailfish non c'e'). qmllint aggiunge la sintassi: gli errori
di sintassi sono proprio quelli che sul device fanno fallire la pagina con
"Impossibile caricare la pagina".
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' i due formati restano leggibili.
Backup (formato dell'app Android)
L'export e il ripristino usano le chiavi dell'app Android — breedSettings,
reservations, customers (camelCase) — non quelle interne del file: un backup
esportato sul telefono Android si ripristina qui e viceversa. Il file viene
scritto in ~/Documents (serve il permesso Documents nel profilo SailJail);
in alternativa il JSON si copia negli appunti. Scorte e data dell'ultimo
aggiornamento non stanno nel backup: al ripristino restano quelli correnti, cosi'
non si azzera il magazzino. tests/cpp_test.sh prova il round-trip con un backup
scritto esattamente come lo esporta l'app Android.
Schermate mappate (da sorgenti + screenshot)
| schermata Flutter | contenuto | porting |
|---|---|---|
OnboardingPage |
4 passi introduttivi | IntroPage (primo avvio + dalle impostazioni) |
ModeSelectionPage |
modalita' allevatore / cliente | non portata: solo allevatore, come deciso |
HomePage |
card produzione stimata, ordini con stato e azioni, FAB New order | MainPage + OrderPage |
HomePage tab Calendar |
calendario delle prenotazioni | CalendarPage |
SettingsPage tab Available breeds |
catalogo con foto e spunta, conferma alla disattivazione | BreedsPage |
SettingsPage tab My coop |
razze allevate, galline registrate/in deposizione, produzione | CoopPage + BreedDetailPage |
BreedDetailPage + AddHenDialog |
galline della razza, eta' in mesi, stato deposizione | BreedDetailPage + HenPage (data con tre selettori) |
CustomersPage + dialoghi |
anagrafica clienti | CustomersPage + CustomerPage |
AddReservationSheet |
nuovo/modifica ordine | OrderPage |
SettingsPage backup |
esporta su file / importa da JSON | BackupPage (formato identico all'Android) |
AboutPage |
info app | AboutPage |
CoopIcon (CustomPainter) |
icona pollaio disegnata a mano | non portata: la Home usa la card con la produzione |
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):
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):
git archive --format=tar.gz --prefix=harbour-henflow-0.2.0/ -o harbour-henflow-0.2.0.tar.gz HEAD
Aperti
- Verifica su device: la UI Silica non e' verificabile senza SDK/dispositivo (qui si verificano motore, traduzioni, catalogo, livello dati, chiavi e sintassi della QML, ma non il comportamento a schermo).
Correzioni rispetto all'originale (bug trovati nei sorgenti Flutter)
app_language.darte' una copia vecchia della tabella traduzioni (111 chiavi contro 126), usata solo dalib/dialogs/confirm_dialogs.dart, e ha gli a capo scritti\\n: quei dialoghi mostrano un backslash visibile. Il porting estrae dalla tabella viva inmain.dart.- Il francese ha 2 chiavi in meno (
name_generic,desc_generic) et()non ha fallback: in francese la razza "generic" usciva come testoname_generic. Nel porting i buchi sono colmati dall'inglese e un test lo verifica. assets/breeds/olive.jpgera 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. - Il tab Calendar ha i messaggi del dialogo di consegna scritti a mano in italiano ("Conferma Consegna", ...): non tradotti in nessuna delle 4 lingue. Nel porting la consegna si segna con l'interruttore della riga, come nella Home.
- Gli ordini dell'app Android hanno anche
clientNoteesource: il ricalcolo della coda ricopia tutti i campi, cosi' un ordine che arriva dall'Android non perde la nota del cliente (c'e' un test apposta). - La consegna scala le uova dal magazzino della razza, come nell'originale; nel porting annullarla le rimette (l'originale non prevedeva l'annulla).