Files
harbour-henflow/README.md
T
kaneda 248e0db899 schermate di configurazione complete: razze disponibili, mio pollaio, registro galline, clienti, backup/ripristino nel formato dell'app Android, lingua a runtime, introduzione e informazioni
- datastore: export/import JSON con le chiavi camelCase dell'app Android (breedSettings/reservations/customers), scorte e ultimo aggiornamento preservati al ripristino
- settings: flag 'introduzione vista'
- desktop: permesso Documents (dove finisce il backup)
- nuovi controlli: tools/check_qml.py (chiavi di traduzione, simboli JS, pagine, componenti) e qmllint nella suite
- versione 0.2.0
2026-10-09 09:21:47 +02:00

10 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 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 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 da fare

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/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 da fare
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

  1. Calendario delle consegne (il tab Calendar dell'originale): griglia del mese con gli ordini per giorno e l'elenco del giorno scelto.
  2. 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.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.