Commit Graph
23 Commits
Author SHA1 Message Date
rzuastiandClaude Opus 4.8 8b2c8e7285 Move welcome message into first-run backend config dialog
Drop the standalone welcome card from the Settings screen and show the
greeting inside the non-dismissible first-run configuration dialog,
noting that OOTT cannot function without a backend installed in the
network.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 12:33:31 -04:00
rzuastiandClaude Opus 4.8 55192d5665 Keep backend config dialog actions on a single row
The AlertDialog actions were stacked onto two lines on narrow phones
(e.g. iOS) because the default OverflowBar wraps once the buttons'
combined width exceeds the dialog width. Lay them out in a single Row
wrapped in a FittedBox(scaleDown) so they stay on one line, scaling
down uniformly on too-narrow screens rather than wrapping or overflowing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 12:27:01 -04:00
rzuastiandClaude Opus 4.8 dce683e396 Add Catppuccin Latte, Dracula, Alucard, Nord and Tokyo Night themes
Add five new selectable themes alongside the existing Catppuccin Mocha
and Gruvbox Dark, each built through the shared buildAppTheme so they
stay visually consistent. Palettes are taken from each project's
official source; a few intermediate surface shades are interpolated
where the canonical palette omits them (noted in the theme files).

Register the new themes in main.dart and add their dropdown entries in
settings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 10:19:56 -04:00
rzuastiandClaude Opus 4.8 587e097291 Add permanent device deletion and refine frontend UI/snackbars
Backend:
- Add db::devices::delete to erase a device and its events atomically
- Expose DELETE /api/devices/{mac}/permanently, wired to OpenAPI
- Cover the new db method and endpoint with tests

Frontend:
- Delete action for not-registered devices (detail screen + list row)
  and an opt-in "permanently delete" checkbox in the Forget dialog
- Navigate to the devices list after deleting from the detail screen
- Refine button emphasis to M3: single filled primary, error-colored
  text buttons for destructive actions, Test demoted to filled-tonal
- Flash the backend-config Test button red on a failed connection test
- Render snackbars through a top-level ScaffoldMessenger host so they
  show above dialogs; keep the built-in SnackBar (with an Overlay host)

Docs:
- CLAUDE.md: rustfmt edition 2024, don't revert formatter-only changes,
  prefer built-in Flutter components, follow existing patterns + M3

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 09:49:11 -04:00
rzuastiandClaude Opus 4.8 475edcb2cf Report delivered device count from the test-notification endpoint
POST /api/notifications/test previously returned a blanket 200 even when there
were no registered devices, so a test that reached nobody looked like a success.
push::send now returns the number of devices the relay confirmed delivery to, and
the endpoint returns it as {"delivered": N}. Settings shows "sent to N device(s)"
on success and an explicit "No devices are registered..." warning when N is 0,
which is the case that previously masqueraded as success.

Backend, API and widget tests updated; clippy and dart analyze clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 14:06:56 -04:00
rzuastiandClaude Opus 4.8 0075072196 Re-register push token at launch; refactor push_service
After a cold restart the app considered push enabled (from prefs) but never
re-registered its FCM token, so a rotated token (or a backend that lost its token
store) left the backend delivering to nothing while the test button still
reported success. Re-register the current token at launch so the backend
converges to the live token on every start, and re-attach the foreground display
handler then too (it was previously only wired during enable()).

Also tidy push_service.dart: a single initPushOnLaunch() startup entry point so
main.dart needs no push internals, one owner for the persisted push-enabled flag
(pushEnabledOnThisDevice / setPushEnabledOnThisDevice) instead of a hard-coded key
in four places, private internal helpers, deduped platform checks, and section
grouping. enable() now reuses the shared token-registration path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 13:56:13 -04:00
rzuastiandClaude Opus 4.8 817ec5872c Add a "Send test notification" button to verify push delivery
Adds POST /api/notifications/test, which delivers a canned test push to every
registered device through the existing relay path (not persisted to the
notifications list), wired into the router and OpenAPI. In Settings, a "Send test
notification" button appears under the push toggle, only when push is enabled on
this device, so the full backend -> relay -> FCM -> APNs -> device path can be
verified on demand without waiting for a real device event.

Backend, API and widget tests added; clippy and dart analyze clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 12:42:20 -04:00
rzuastiandClaude Opus 4.8 c7c5b45ae8 Surface native iOS APNs registration outcome for diagnostics
Enabling push still fails on iOS with getAPNSToken() returning null, so capture
the APNs registration result natively to find out why. AppDelegate now overrides
didRegister/didFailToRegister, logs the outcome, and exposes it over a
oott/push_diagnostics method channel (super still calls through so Firebase
swizzling is unaffected).

apnsRegistrationStatus() reads that channel, and the settings push toggle now
shows Apple's actual rejection reason in the error message when enabling fails,
so it can be diagnosed without a Mac to read the device console.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 12:06:11 -04:00
rzuastiandClaude Opus 4.8 67b94b89a1 Move backend config to a dialog; group settings into cards
Settings screen now splits responsibilities:
- Backend URL + API key move into a Test/Save popup dialog
  (backend_config_dialog.dart), reachable via a "Re-configure" action.
  Per M3 the Test button is left-aligned (neutral) with Cancel/Save
  grouped trailing. On first run the dialog auto-opens non-dismissible.
- The screen shows the connection read-only (URL plain, key masked with
  reveal) in a "Backend connection" card, and groups Theme + Push into an
  "App settings" card. Theme and push apply immediately, no Save button.
- Align all card titles to titleLarge across home/status/settings.

Adds a test seam (dioBuilderForTesting) so reconfigure keeps the mock Dio
in widget tests instead of issuing real requests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 18:15:10 -04:00
rzuastiandClaude Opus 4.8 1d85ec6f83 Gate push toggle on backend notification method
Add a GET /api/config endpoint exposing the front-end-facing backend
configuration (currently the notification delivery method, grouped under
a nested "notifications" object so the shape can grow). The settings
screen fetches it on init and only shows the per-device push toggle when
the backend method is "push" (and the platform supports push, which keeps
it off the browser).

Also fold in related push-notifications cleanups: fix the Android app
label ("frontend" -> "OOTT") so the notification permission dialog reads
correctly, remove the completed push_notifications.md plan, and update
TODO.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 17:26:53 -04:00
rzuastiandClaude Opus 4.8 4f3fe10332 Implement push notifications Phase 1 (FCM + project relay)
Backend (Rust):
- push_tokens migration, model (PushToken/PushPlatform), and db layer
  (upsert/list/delete/delete_many)
- PUT/DELETE /api/push_tokens endpoints wired into the router + OpenAPI
- "push" notification method: relay sender (reqwest) that forwards only the
  sanitized title/body and prunes dead tokens, plus settings with a default
  relay_url
- 164 tests pass, clippy clean

Relay (push_relay/, TypeScript Firebase Cloud Function):
- POST /v1/push (firebase-admin sendEach + per-token status mapping),
  GET /healthz, payload validation, per-IP Firestore rate limiting
- Jest tests + README documenting the manual project-owned setup

Frontend (Flutter):
- oott_api_push.dart (register/unregister), push_service.dart behind a
  PushService abstraction, and a per-device push toggle in settings
- firebase_core/firebase_messaging/flutter_local_notifications deps
- 145 tests pass, dart analyze clean

Dev shell:
- add nodejs_22 to the Nix dev shell so the relay tests/build run locally

Remaining (manual, project-owned): create the Firebase project, deploy the
relay and set the real default_relay_url, add native Firebase config, and test
on real devices.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:28:03 -04:00
rzuastiandClaude Opus 4.8 9367c359d7 Polish frontend: design tokens, theming, AppBar titles, empty/loading states
Frontend usability and aesthetics pass across web and mobile:

- Add shared layout tokens (theme/dimens.dart: Insets + Breakpoints) and
  replace magic-number spacing and per-file breakpoint consts.
- Route both themes through buildAppTheme (theme/theme_builder.dart): explicit
  useMaterial3 and a shared branded textTheme (Barlow Condensed for
  display/headline/title styles); logo now reads its style from the theme.
- Move screen titles into the shared AppBar (route-derived) and drop the
  redundant in-body headers; upgrade Settings buttons to M3 FilledButton.
- Add reusable EmptyState and skeleton loaders; Devices empty state links to
  scanner status, notifications get filter-aware messages.
- Add a first-run welcome intro and Save-disabled helper text in Settings.
- Make device Filter/Sort adaptive: bottom sheet on phones, dialog on wide.
- Collapse the device-list filter chips and Sort/Filter buttons into one row;
  rename the home Scanners card title and align its style.

Tests: add EmptyState/skeleton widget tests and Settings first-run cases;
update tests for the FilledButton swap and relocated titles. 89 passing,
dart analyze clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 08:39:37 -04:00
rzuastiandClaude Sonnet 4.6 9b521e0847 Stop interpolating raw exception into settings snackbar
Defense-in-depth: the persisted value is the (XOR-obfuscated) API key,
so a future SharedPreferences backend that surfaced the value in its
exception could leak it through the UI. Log the detail via debugPrint
and show a fixed message to the user instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 19:00:38 -04:00
rzuastiandClaude Sonnet 4.6 140ebab035 Catch uncaught async errors and surface response-shape failures
Wrap main in runZonedGuarded and install FlutterError.onError /
PlatformDispatcher.onError so background async failures get logged
instead of dying silently. Guard PrefUtil.init so a SharedPreferences
platform error no longer aborts startup. Recognize TypeError and
FormatException in dioErrorToUserMessage to give a clearer message
when the backend returns an unexpected JSON shape or bad timestamp.
Wrap Settings._save in try/catch so prefs write failures show a
snackbar instead of leaving the Save button silently dead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 18:55:35 -04:00
rzuastiandClaude Sonnet 4.6 e70676a193 Surface persistence and fetch errors instead of swallowing them
PrefUtil.setValue now returns the underlying SharedPreferences result
and throws on unsupported types; settings save reports failures rather
than always showing success. Device detail and event history report
the real Dio error message and skip cancellations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 18:43:46 -04:00
rzuastiandClaude Opus 4.7 d3b6c64e8b Apply Settings changes to the live BackendAPI without restart
Settings._save wrote new base_url/api_key to prefs but the BackendAPI
singleton kept the Dio it built at first access, so URL/key changes only
took effect after an app restart. Expose reconfigureFromPrefs() on
BackendAPI and call it from Settings._save so subsequent requests pick up
the new config immediately.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 09:17:15 -04:00
rzuastiandClaude Sonnet 4.6 e5c27f067c Align navigation with Material 3 responsive recommendations
- Replace single NavigationRail with adaptive navigation: NavigationBar
  (bottom) for compact <600dp, icon-only NavigationRail for medium
  600–840dp, and extended NavigationRail for expanded ≥840dp
- Remove nested Scaffold/AppBar from About, Settings, DeviceList, and
  DeviceDetail — all screens now render as plain widgets inside the
  single shell Scaffold; page titles surface as inline headings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 19:51:31 -04:00
rzuastiandClaude Sonnet 4.6 e6ec771f3b Simplify and modularize device, notification, and settings screens
Extract inline widget closures into named StatelessWidgets (_DeviceCard,
_DeviceFilterSheet, _NotificationCard), replace magic ints and hardcoded
repeated widgets with enum-driven loops, fix the InputDecorator/DropdownButton
hack in device_actions, merge symmetric _markAsRead/_markAsNew into _setRead,
and remove dead _isLoading/_isSaving state in settings where operations are
synchronous.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 11:14:29 -04:00
rzuastiandClaude Sonnet 4.6 f34dfe8386 Fix all dart analyze warnings in the Flutter frontend
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 13:57:03 -04:00
rzuastiandClaude Sonnet 4.6 a647e0d061 Add theme selection to settings page
Adds a dropdown in the settings page to switch between available themes
(Catppuccin Mocha, Gruvbox Dark). The selected theme is applied immediately
on save and persisted across restarts via SharedPreferences.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 11:38:16 -04:00
rzuasti 63ac1d04d5 Backend list notifications now supports pagination. Frontend
notification list is inifinite and lazy
2026-03-04 11:05:24 -05:00
rzuasti dc2521da65 Settings working on front-end and with it the notifications list 2026-03-03 12:20:57 -05:00
rzuasti 1d029c8c5f Added configuration redirect and partial settings form 2026-03-02 19:41:01 -05:00