- WebView background set to #F5F5F5 to avoid white flash
- SSL certificate errors now show a dialog (user can accept self-signed)
- HTTP errors (4xx/5xx) shown with status code
- Migrates old 'public_url' key to 'server_url' automatically
- Better error messages with troubleshooting tips
- Progress bar shows real page load progress
- No-server-configured state shows clear instructions
Added actions/cache@v3 for:
- ~/android-sdk (keyed on OS + API 34) — skips 150MB download
- ~/.gradle + android/.gradle-home (keyed on OS + gradle 8.7
+ gradle files hash) — skips 130MB gradle download + all
dependency resolution and jar caching
- ~/.cache/pip (keyed on requirements.txt hash) — skips
flask/click/werkzeug installs
Build step now checks if cached paths exist before downloading.
Expected speedup: 5-6 min → ~1-2 min on cache hits.
Rewrote the Android WebView app to load the entire meal-tracker
web application, not just the public sharing page:
- User configures their server base URL (e.g. https://meals.example.com)
- Full login/register, dashboard, meal responses, history, settings
- Cookie/session support — login persists across app restarts
- Dynamic toolbar title from page
- 'Clear Data' button in settings to wipe cookies/cache/sessions
- Server URL auto-normalized (trailing slash stripped, https:// added)
- Back button navigates within WebView history
- Swipe-to-refresh, loading bar, network error handling
Removed tag-triggered release publishing since secrets.GITEA_TOKEN
is not available on this Gitea instance. CI now tests and verifies
the APK compiles. Releases created manually via API as shown with v1.0.0.
Release: https://gitea.ct.cozytren.ch/romane/agentbox-test/releases/tag/v1.0.0
Switched from 'zip -x' patterns (which don't handle .git/ correctly)
to 'find | zip -@' with explicit path exclusions. Release zip now
~130 KB instead of ~4.5 MB.
CI now triggers on tags matching 'v*':
- Builds APK (always, after tests pass)
- On tag push: creates a Gitea Release and attaches
meal-tracker.apk + meal-tracker.zip as release assets
- Removed CI artifact steps (replaced by release publishing)
The v1.0.0 release is live with APK (5.3 MB) and ZIP (134 KB).
CI runs with -o pipefail, so 'yes | sdkmanager --licenses' fails
with exit code 141 when sdkmanager closes stdin. Wrapped with
{ yes || true; } | ... || true to handle this gracefully.
Add hard-to-guess public URLs (/public/<token>) that show a read-only
meal status dashboard for each household. Only people with the link can
see the data — no login required.
- models.py: public_token + public_enabled columns on households,
get_household_by_public_token(), generate_public_token(),
set_public_enabled()
- meals.py: /public/<token> route, /admin/public-link for
generate/disable/enable/regenerate
- templates/public.html: clean read-only public dashboard
- templates/public-not-found.html: 404 for invalid/disabled links
- templates/dashboard.html: admin UI with copy/regenerate/disable
- static/style.css: public page and URL box styles
- i18n.py: 18 new translation keys (en/fr/de)
- models.py: ORDER BY CASE so lunch always appears before dinner in the
dashboard table (was alphabetical, putting dinner first).
- style.css: add @media (max-width: 600px) with mobile-friendly layout:
stacked navbar, full-width dashboard header, reduced padding/margins,
larger touch targets for chips/buttons, stacked manage forms, and
compact table cells.
The auto-login route needs these fields to populate the session, but the
query only selected id, username, household_id. Added JOIN with
households table and selected is_admin + household_name.
- ntfy.py: switch from JSON body to HTTP headers (Title, Priority, Tags,
Actions, Click) with plain message body. ntfy servers treat the POST
body as the message, so JSON body fields were ignored and displayed as
raw JSON.
- ntfy.py: replace em dashes in headers with hyphens (HTTP headers must
be latin-1 encodable).
- meals.py: add /auto-login/<token> route that logs users in via their
ntfy callback token so clicking a notification opens the dashboard
without re-entering credentials.
- ntfy.py: update Click URL to /auto-login/<token>?date=...
Add .manage-form label styling and .manage-form input[type="text"] rule
to match the existing password input styling, so the ntfy topic URL field
looks consistent with the rest of the UI.
- meal-cron now runs send_reminders_cron.sh (simple wget loop, no DB mount)
- New /api/cron-tick endpoint handles all timezone logic server-side
- base_url captured from request.host_url when user saves ntfy settings,
stored per-user — no APP_BASE_URL env var needed
- Cron endpoint protected by CRON_SECRET shared env var
- Removed send_reminders_cron.py, DB volume from cron container
- tests: 3 new cron-tick + base_url capture tests (69 total)
- docker-compose.yml: meal-cron service loops send_reminders_cron.py every 60s
- send_reminders_cron.py: checks each household's local time, sends ntfy
reminders at 10:00 (lunch) and 16:00 (dinner), guarded against duplicates
- models.py: timezone, last_lunch_reminder, last_dinner_reminder on households
- meals.py: /admin/timezone route for admins to set household timezone
- dashboard: timezone selector (20 common zones) in Account Settings
- i18n: 5 new translation keys for timezone strings
- Dockerfile: copy send_reminders_cron.py
- tests: 4 new timezone tests (admin set, non-admin denied, default UTC, auth)
- docs: updated AGENTS.md