Banner prodotti aperti:
- Rimosse le opzioni 'Usa comunque' e 'Ignora' (non hanno senso
se il prodotto è solo aperto — rimangono solo 'L\''ho finito!',
'L\''ho buttato', 'Correggi data')
- Per prodotti scaduti non aperti il comportamento rimane invariato
Preloader startup check:
- Sostituito il mini-label monospace con una ruota 3D (stile cooking wheel)
- Testo grande, colorato: VERDE=ok, ARANCIONE=warning, ROSSO=errore
- Il check precedente sale in cima (rotateX tilt, dimmed) mentre il
nuovo entra dal basso con animazione 3D
- setProgress() ora guida la ruota; slowAnim() aggiorna solo la barra
Defaults / non-bloccante:
- Gemini API key non impostata → ok:true 'non configurata' (verde)
- Bring! token non ancora generato → ok:true (verde, auto-generato al 1° accesso)
- La configurazione mancante mostra ✅ informativo, non ⚠️ warning
Step 5 del wizard ora mostra 4 toggle (pre-compilati se già configurati):
- Salvaschermo orologio (screensaver_enabled)
- Prezzi lista spesa (price_enabled)
- Piano pasti (meal_plan_enabled)
- Suggerimenti zero-waste durante cottura (zerowaste_tips_enabled)
Solo i toggle NON ancora impostati in prefs partono da false (fresh install).
Tutti e 4 vengono salvati in SharedPreferences e inviati al server via
save_settings al completamento del wizard.
PHP/JS: zerowaste_tips_enabled aggiunto come impostazione server-side
(ZEROWASTE_TIPS_ENABLED in .env), sincronizzata nel WebView via
_applySyncedSettings() al caricamento.
- GITHUB_RELEASES_API ora punta a /releases/tags/kiosk-latest (non alla
webapp latest) per confrontare versioni kiosk vs kiosk
- checkForUpdates() estrae la versione reale dal body della release con
regex kiosk-X.Y.Z invece di usare il tag non-semver 'kiosk-latest'
- installApk() aggiunge validazione pre-install via PackageArchiveInfo:
package name diverso → errore + issue report
versionCode uguale/inferiore → banner dismesso + report install_no_upgrade
- Bump versionCode 16→17, versionName 1.7.15→1.7.16
Fix: STATUS=1 causato da confronto versione webapp (1.7.22) vs kiosk
(1.7.15) → falso update → scaricava stesso APK già installato → rifiuto
- health_check: use evershelf.db (not dispensa.db); auto-migrate if needed
- removed dispensa.db (legacy, obsolete)
- backups check: verify files exist (not dir writability, cron writes as root)
- bring_token: read data/bring_token.json (not env var)
- warning popup: 5s countdown bar with label+hint per warning, auto-closes
- error popup: blocking panel with title + hint per critical failure
- db_legacy check: warns if old dispensa.db still present
- 32 total checks (added db_legacy, tts_url, scale_gateway)
- hint messages on every check explaining cause and fix
- translations: added check_db_legacy, check_tts, check_scale,
critical_error_intro, error_network_detail in it/en/de
- Replace banner checklist with real-time progress bar + per-check label
Bar fills smoothly (0→100%) as each check runs; label shows current check.
On success: bar stays green briefly then fades. On warnings: amber badges
shown for 2.2s. On critical error: bar turns red + error block + Retry.
- Extend health_check to 29 comprehensive checks:
PHP 8.0+ version, 4 critical extensions (pdo_sqlite/curl/json/mbstring),
4 optional extensions (openssl/fileinfo/zip/intl), PHP memory/timeout/upload,
data/ writable, rate_limits/ dir, backups/ dir, actual file-write test,
free disk space, SQLite connect, required tables, PRAGMA quick_check integrity,
WAL mode, DB file size, inventory row count, .env file, Gemini AI key,
Bring! credentials + token, cURL SSL version, internet reachability (Gemini API)
- Fresh-install detection: if dispensa.db not found + data/ writable → OK (auto-create)
- Translations: startup.* expanded to 28 keys in IT, EN, DE, FR, ES
- CSS: new .preloader-progress-wrap, .preloader-bar-track, .preloader-bar,
.preloader-check-label, .preloader-warn-badge; removed old .preloader-checks
- Version: v1.7.21, assets v=20260520b
- Add ?action=health_check PHP endpoint (early-exit, before rate-limiter)
Checks: PHP version, required extensions, data/ writability, SQLite DB
connection + table integrity, .env file, Gemini AI key, Bring! token
- Display animated checklist in splash screen with per-item icons
(ok/warn/error); critical failures block app launch with clear error
message and Retry button; optional warnings shown but don't block
- New JS: _runStartupCheck(), _startupRetry(); called first in _initApp()
- New HTML elements in #app-preloader: #preloader-checks, #preloader-error-msg,
#preloader-retry-btn (hidden until startup check completes)
- New CSS: .preloader-checks, .preloader-check-row, .preloader-error-msg,
.preloader-retry-btn with state colors (ok=green, warn=amber, error=red)
- Translations: startup.* keys (10 per language) in IT, EN, DE, FR, ES
- Asset version bump: v=20260520a
- Complete fr.json (1049 keys, 52 sections)
- Complete es.json (1049 keys, 52 sections)
- Language selector updated with Francais and Espanol
- Setup wizard localized for fr/es
- Default fallback language changed from 'it' to 'en'
- Version bump to 1.7.17
- Add scan history (last 20 products) stored server-side via app_settings
- Render recent chips in scan page; tap to select product without re-scanning
- Migrate shopping_tags, pinned_bring, pref_use_loc, pref_move_loc,
auto_added_bring, bring_blocklist, no_expiry_dismissed from localStorage
to server-synced in-memory caches (_saveToServer pattern)
- Extend syncSettingsFromDB to load all 7 data caches + scan_history on startup
- One-time migration: existing localStorage data auto-uploaded to server on
first load, old keys removed
- Fix dangling try/catch in toggleShoppingTag (was missing opening try)
- Banner (loadBannerAlerts): add step 1b — any item from statsData.opened
with is_edible=false is queued as expired even when client-side
getExpiredSafety would consider it 'ok' (e.g. conserve <30d past expiry).
Applies to all product types, not just conserve.
Requires fetching stats in the same Promise.all (no extra round-trip since
loadDashboard already calls stats separately).
- CSS: remove text-decoration:line-through from .alert-item-spoiled .alert-item-name.
The badge (⛔/⚠️) already communicates the state; strikethrough added no
information and confused users into thinking the item had been deleted.
- Kiosk (Android): btnSettings was positioned top|end with alpha=0.12,
sitting invisibly on top of the HTML scan button in the webapp header.
Moved to bottom|end (marginBottom=80dp, alpha=0.28) so it never
overlaps the header. Kiosk versionCode 15→16, versionName 1.7.15.
- Web (Android Chrome/Brave): pointerleave fired before pointerup when
finger drifted, cancelling the long-press timer and letting a synthetic
click bubble to an unintended handler. Fixed with setPointerCapture +
preventDefault + replaced pointerleave with pointercancel. Added
touch-action:manipulation to .header-scan-btn CSS.
Brave on iOS injects a user-script that wraps SpeechSynthesisVoice objects
with a fake proxy. Accessing v.lang on the proxy threw 'undefined is not an
object (evaluating Object.getPrototypeOf(voice))'.
Fix: wrap the v.lang access in _initBrowserTtsVoices filter() inside its
own try/catch — bad proxies are silently discarded.
- TTS: tts_engine, tts_rate, tts_pitch, tts_auth_header_name, tts_auth_header_value,
tts_extra_fields now stored in .env and synced across devices via get_settings/save_settings
- meal_plan: persisted to SQLite app_settings table on every edit (selectMealPlanType,
resetMealPlan) and restored on startup via syncSettingsFromDB — all devices stay in sync
- tts_voice: also synced to SQLite for best-effort cross-device restore
- saveSettings() sends meal_plan + tts_voice to app_settings_save after env write
- Remove deprecated SPESA_PROVIDER and SPESA_AI_PROMPT from .env
- .env.example: full rewrite documenting all 30+ keys in labelled sections
(AI, Shopping, TTS, Preferences, Appliances, Scale, Meal Plan, Screensaver, Prices,
Security, Developer)
- _applianceDisplayName(): reverse lookup from canonical Italian names
to settings.appliances.* i18n keys, with emoji stripping — appliance
chips now show 'Air fryer', 'Heißluftfritteuse', etc. in EN/DE
- renderAppliances(): uses translated display name; remove button title
uses t('btn.delete') instead of hardcoded 'Rimuovi'
- addApplianceQuick(): toast now uses t('toast.appliance_added') instead
of hardcoded Italian ' aggiunto'
- saveSettings(): gemini_key in localStorage preserved when input is empty
(key is not pre-populated for security — blank != user deleted the key)
- saveSettings(): _geminiAvailable re-synced from server after each save
so recipe buttons immediately reflect correct state without page reload
- Complete i18n audit: 25+ new translation keys (en/it/de) — vacuum toast,
TTS voices, timer steps, product notes, error prefixes, form placeholders,
barcode hints, recipe/cooking ingredient labels, unit variants
- pz/conf unit labels now use t('units.pz') / t('units.conf') throughout
- Splash screen: minimum 3-second display (_splashStart recorded at parse
time, fade delayed by remaining ms if app loads faster)
- Quantity decimal precision: qtyNum in recipe/cooking buttons and conf
fallback display capped to 1 decimal (was showing 7+ from raw AI output)
- Recipe/cooking buttons: removed Italian fallback strings from t() calls
- README: translated remaining Italian phrases; added demo.gif to Screenshots
- CHANGELOG: updated 1.7.15 entry with all session changes
- assets/img/demo.gif: EverShelf.gif processed at 2x speed (~36s)
After every develop→main merge, reads the version tag from index.html
(e.g. v1.7.13), checks if that tag already exists, and creates a new
GitHub Release if not. Body is pulled from CHANGELOG.md.
This powers the in-app update badge (`check_update` action) so
self-hosted Docker users see a notification when a new version is
available.
The BLE scale gateway is fully integrated into the EverShelf Kiosk app
since v1.6.0. This standalone Android app is no longer needed or maintained.
Removal also resolves GitHub secret scanning alert #1 (legacy plain-text
GitHub PAT in ErrorReporter.kt — already revoked by GitHub automatically).
Brave browser's anti-fingerprinting user-script (makeFakeVoiceFromVoice)
intercepts the SpeechSynthesis voices array and crashes with
'undefined is not an object (evaluating Object.getPrototypeOf(voice))'
when iterating over null voice entries.
Defensive fix: filter null/undefined/no-lang entries from getVoices()
before processing, so Brave's proxy never receives invalid objects.
Fixes#58