Commit Graph
83 Commits
Author SHA1 Message Date
rzuastiandClaude Opus 4.8 a90bcc360d Document backend connection options and allow iOS local networking
Add NSAllowsLocalNetworking ATS exception so the iOS app can reach a
backend over HTTP on the local network, and document the supported
connection paths (direct private-IP HTTP vs. reverse proxy with HTTPS)
in the README.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 19:38:43 -04:00
rzuastiandClaude Opus 4.8 abeba88fec Change scanner status label "Not yet started" to "Not started"
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 19:25:26 -04:00
rzuastiandClaude Opus 4.8 4e975598f9 Add DHCP scanner to frontend status displays
Add a DHCP scanner card to the Status screen and a DHCP row to the
consolidated Status card on the Home screen, mirroring the existing
mDNS/SSDP implementations. Also label DHCP-discovered device events as
"DHCP" instead of the raw "Dhcp".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 18:45:38 -04:00
rzuastiandClaude Opus 4.8 440959b10d Add SSDP/UPnP scanner to frontend status displays
Surface the SSDP/UPnP scanner alongside ARP and mDNS: a dedicated card on
the Status screen and a row in the consolidated Status card on Home. Mirrors
the existing mDNS implementation and reuses the generic ScannerStatusCard and
PolledValue infrastructure. Also maps the 'Ssdp' device-event scanner to the
"SSDP/UPnP" label.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 18:13:33 -04:00
rzuastiandClaude Sonnet 4.6 29a5b7cf3e Expand notification cards in-place instead of navigating on tap
Tapping a notification card now toggles an expanded view showing the
full body text and M3 action buttons (Mark as read/unread, View device),
replacing the former popup menu and direct navigation on tap.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 16:18:40 -04:00
rzuastiandClaude Opus 4.7 55a6b29d1c Show scanner source in device event tooltip
Surface the originating scanner ("ARP" or "mDNS") in the event timeline
tooltip so users can tell which scanner reported each sighting.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 08:19:49 -04:00
rzuastiandClaude Opus 4.7 d94cc2183d Route scanner status colors through the active theme
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 07:55:34 -04:00
rzuastiandClaude Opus 4.7 376eb3c367 Use grey dot when ARP scanner is waiting for next run
Amber implied a problem when the scanner is simply idle between runs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 07:46:51 -04:00
rzuastiandClaude Opus 4.7 408585c056 Keep status pills inline and show tooltips only on overflow
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 07:42:43 -04:00
rzuastiandClaude Opus 4.7 0304c50efb Move home screen route from /notifications to /
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 07:17:41 -04:00
rzuastiandClaude Opus 4.7 6303951b3d Show online/offline status dot beside device last-seen
Highlights devices seen within the last 10 minutes with a green dot so
recent activity is scannable at a glance; older devices get a muted
outline-colored dot with an "Appears offline" tooltip.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 07:15:30 -04:00
rzuastiandClaude Opus 4.7 17712c25f1 Collapse scanner-card and pagination duplication into shared widgets
Extract ScannerStatusCard<T> and PaginationBar so the ARP/mDNS cards and
the two paginated lists share one implementation each. Also fold the
four UISnackbars methods over a severity enum and fix Notification.toJson
serializing a method tear-off instead of the enum name.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 07:09:24 -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 Sonnet 4.6 f7f255e20f Compare parsed /test response instead of stringified Dio dump
Previously BackendAPI.test() called response.toString().contains('OOTT_API_OK'),
which scans the full stringified Response (status line, headers, body, request
path). A reverse proxy or captive portal returning HTML that happens to echo the
token would falsely pass the connectivity check. Compare response.data — the
JSON-decoded String the backend's /api/test handler returns — against the
expected value with exact equality instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 18:32:40 -04:00
rzuastiandClaude Opus 4.7 8e9e221048 Omit Authorization header when API key is empty
Send no Authorization header at all on a fresh install (rather than
"Bearer " with an empty token), so the backend can distinguish "no
credentials supplied" from "credentials supplied but invalid".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 18:26:00 -04:00
rzuastiandClaude Sonnet 4.6 b5da90be59 Pause polling and show a banner when the backend is unreachable
Adds a centralized BackendReachability singleton fed by a Dio
interceptor (connection-level failures only) and connectivity_plus.
PolledValue subscribes to it and pauses on offline / reloads
immediately on reconnect, replacing the previous per-card error
cascade. MainShell renders a global OfflineBanner with Retry and
Settings actions; cards downgrade error→stale when offline so the
banner is the only red element.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 18:21:55 -04:00
rzuastiandClaude Sonnet 4.6 8be65f3ffa Fold pagination +1 trick into API methods, return typed records
Remove dead _pageSize from BackendAPI and centralise the
"fetch perPage+1, detect hasNextPage, trim" logic into
listDevices and listNotifications. Both methods now accept
page/perPage and return ({items, hasNextPage}) records,
eliminating the duplicated boilerplate at each call site.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 17:40:18 -04:00
rzuastiandClaude Sonnet 4.6 f75d1ce313 Pause polling when screens are off-route or app is backgrounded
PolledValue gains pause()/resume() methods. ScannersStatusCard and
NotificationsList now implement RouteAware (didPushNext/didPopNext)
and WidgetsBindingObserver to stop API calls and tick timers when
the widget is not visible, resuming with an immediate fetch on return.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 17:34:19 -04:00
rzuastiandClaude Sonnet 4.6 838b292456 Add dio_smart_retry for automatic GET request retries
Transient network hiccups and temporary backend errors (5xx, timeouts)
now retry transparently up to twice (1 s then 3 s backoff) before
surfacing an error to the user. Non-idempotent methods (POST, PUT,
DELETE), cancellations, and non-retryable status codes are excluded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 17:19:00 -04:00
rzuastiandClaude Opus 4.7 61f2579152 Make notification mark-as-read reliably update the list
Three intertwined fixes so that marking a notification as read from the
trailing menu, swipe-dismiss, and 'Mark all as read' all consistently
update the UI and surface backend failures.

- Guard the mark-read, mark-new, and mark-all calls with try/catch; on
  failure show a snackbar with the mapped error and leave the list
  untouched.
- Use the State's own `mounted` and `context` after the await instead of
  the per-card BuildContext, so the SliverList rebuild reliably runs
  even when the originating NotificationCard's element has been swapped
  out by the reconciliation. _setRead and _markAllAsRead no longer take
  a BuildContext.
- Replace `Dismissible(key: UniqueKey())` with `ValueKey(item.id)` in
  the notification card. UniqueKey was regenerated on every build,
  forcing the Dismissible/PopupMenuButton subtree to be disposed on any
  parent rebuild; the open menu's `mounted` check would then drop
  `onSelected` silently.
- Add `ValueKey(device.macAddress)` to the device list rows for the
  same reason, anchoring each row's Element (and its PopupMenuButton
  State) to its device identity instead of its list position.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 10:28:23 -04:00
rzuastiandClaude Opus 4.7 bb677aeebf Centralize status polling with last-known-good, stale, and error tiers
The four polling status cards (ARP, mDNS, scanners summary, devices
summary) each rolled their own Timer/_status/_error/_isLoading state and
flipped to a red 'Error' UI on the first failed poll, throwing away
last-known-good data that was still in memory. A momentary backend blip
made every card flash red.

Introduce PolledValue<T>, a ChangeNotifier that owns the periodic timer,
in-flight CancelToken, last value, last error, and a freshness
classification (initialLoading / fresh / stale / error). 'stale' kicks in
on the first failure but keeps the value visible; 'error' only takes over
after staleErrorAfter elapses without a success (30s for the 5s scanner
polls, 3min for the 1min device-summary poll). All four cards now branch
on freshness and show a small live-updating warning icon with a tooltip
('Last updated N ago — <error>') while stale, instead of flipping red.

Also threads CancelToken? through the three polled GET endpoints.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 10:03:19 -04:00
rzuastiandClaude Opus 4.7 d7276c8383 Replace per-method debugPrints with a debug-only Dio LogInterceptor
Every BackendAPI method had hand-rolled 'About to call ...' and
'Received: ...' debug lines, and the constructor printed the base URL and
a masked API-key marker. Replace all of that with a single LogInterceptor
attached under `if (kDebugMode)` to both the singleton Dio and the
ephemeral Dio used by BackendAPI.test(). Headers and bodies are kept off
so the Authorization bearer never lands in the console. The interceptor
is tree-shaken from release builds.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 09:39:06 -04:00
rzuastiandClaude Opus 4.7 30df22df56 Cancel in-flight paginated fetches to prevent stale-write races
Rapidly switching filter chips, sort columns, or pagination on the devices
and notifications lists could overlap fetches, and an older response
landing after a newer one would silently overwrite the list with results
that no longer match the active query.

Thread an optional CancelToken into listDevices and listNotifications.
Each State now keeps one active token, cancels it on every new fetch and
on dispose, and bails from both success and catch branches when its token
has been superseded. DioExceptionType.cancel is silenced so internal
cancellations don't surface as user-visible errors. The notifications
list's prior `if (_isLoading) return;` guard is removed so the 1-min
periodic refresh is no longer skipped during user activity.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 09:30:03 -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 Opus 4.7 7954a9c755 Add Dio timeouts and map backend errors to user-friendly messages
Set 5s/15s/15s connect/receive/send timeouts so calls fail fast instead of
hanging. Introduce dioErrorToUserMessage() and route DioException-throwing
call sites (device actions, devices list, notifications list, scanners
status card) through it, replacing raw e.toString() output. Notifications
list now surfaces load errors inline in the theme's error color, matching
the devices list pattern. Status dots show the mapped detail as a tooltip.
Stop logging the API key in cleartext.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 09:06:56 -04:00
rzuastiandClaude Opus 4.7 0f6296759d Add edit dialog for registered devices
Extend PUT /api/devices/{mac} to accept an optional name and surface an
Edit action from both the device detail screen and the per-row overflow
menu, letting users modify owner, device type, vendor and name without
forgetting and re-registering.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 11:27:52 -04:00
rzuastiandClaude Opus 4.7 3a579f0321 Redesign devices list as a sortable, responsive headered list
GET /api/devices accepts sort_by/sort_order (whitelisted columns; default
last_seen DESC, stable secondary sort on mac_address), device registration
captures an optional hostname, and the Flutter list switches between a
wide headered layout and a compact ListTile under 600px. Per-row overflow
menu retains View details / Register / Forget actions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 11:09:55 -04:00
rzuastiandClaude Opus 4.7 926a902377 Refresh device and notification lists on return navigation
Subscribes the devices list and notifications list to a shared
RouteObserver so they refetch when a pushed route (e.g. device detail)
is popped, avoiding stale state after registering or updating a device.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 15:13:00 -04:00
rzuastiandClaude Opus 4.7 2ba51769ec Show registered devices by owner's device type name
Display registered devices with a "John's Laptop" title and move their
IP address into the card body, instead of showing the owner separately.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 14:24:40 -04:00
rzuastiandClaude Opus 4.7 7fb457ca4a Make device detail IP and info fields selectable
Use SelectableText for the IP address and all info-card values so users
can select and copy them (e.g. the MAC address).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 13:56:16 -04:00
rzuastiandClaude Opus 4.7 251c06277e Add pagination to the devices list screen
Mirror the notifications list paging pattern (offset/limit, fetch one
extra row to detect the next page, First/Prev/Next controls) across the
devices DB query, REST endpoint, API client and UI. Also add a First
page button to the notifications pagination row.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 13:07:27 -04:00
rzuastiandClaude Sonnet 4.6 75f597bfe4 Add mDNS scanner status to Status and Home screens
Add an mDNS Scanner status card to the Status screen mirroring the ARP
card, and replace the ARP card on the Home screen with a combined Status
card that summarizes both scanners in one line each. Extract the shared
duration formatting helper into utils/duration_formatter.dart.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 10:19:50 -04:00
rzuastiandClaude Sonnet 4.6 2fe850c070 Auto-focus owner field and submit on Enter in register device dialog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 08:29:50 -04:00
rzuastiandClaude Sonnet 4.6 1da0306b61 Replace device detail popup menu with inline CTA buttons
Register and Forget Device actions are now always-visible buttons
(FilledButton and error-styled OutlinedButton) placed inline below
the device info card, following M3 guidelines for in-content actions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 08:25:11 -04:00
rzuastiandClaude Sonnet 4.6 39f382fd0d Extract NotificationsList widget from HomeScreen into its own file
HomeScreen is now a StatelessWidget responsible only for the responsive
layout structure. All notification state, pagination, filtering, and
mark-as-read logic lives in the new NotificationsList widget.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 08:06:30 -04:00
rzuastiandClaude Sonnet 4.6 85b86026cb Replace infinite-scroll notifications with button-based pagination
- Swap PagingController/PagedSliverList for explicit page state and
  Previous/Next icon buttons below the list
- Fetch pageSize+1 items to detect the last page without a total-count
  API; pagination controls are hidden when there is only one page
- Remove unused infinite_scroll_pagination dependency

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 07:56:03 -04:00
rzuastiandClaude Sonnet 4.6 d14c8beb60 Fix empty-state layout: compact message instead of viewport-filling indicator
Replace Center-in-Expanded / SliverFillRemaining empty states in the
notifications and devices lists with compact, centered inline text so
status widgets below remain visible without scrolling. Also align
notification filter chips to match the devices list spacing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 20:03:11 -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 ff83ada576 Improve home screen widget UX: visual hierarchy and navigation
- DeviceSummaryCard and ArpScannerCard are now tappable, navigating to
  the devices list and status screen respectively
- Elevated "Devices" title to titleLarge; section subtitles promoted to
  bodyMedium/w600 while row content is demoted to bodySmall
- Renamed top-level row label to "Registered in the system"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 19:34:42 -04:00
rzuastiandClaude Sonnet 4.6 f113da9cd0 Refactor frontend: extract widgets and eliminate duplicated scanner state
- Convert ArpScannerCard to a self-managing StatefulWidget (owns its own 5s refresh + 1s tick timers), removing the duplicated state management that existed in both HomeScreen and StatusScreen
- Extract NotificationCard to home/notification_card.dart
- Extract DeviceSummaryCard (with its 1-min refresh timer) to widgets/device_summary_card.dart
- HomeScreen drops from 515 to 253 lines; StatusScreen from 82 to 19 lines

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 19:23:27 -04:00
rzuastiandClaude Sonnet 4.6 251d8a479e Redesign Home screen with device summary and scanner status
Adds a responsive Home screen combining the notification list with a
device summary panel and ARP scanner status, visible side-by-side on
wide screens and stacked on narrow ones.

Backend:
- New GET /api/devices/summary endpoint returning registered device
  counts and "seen in last 24h / 7 days" breakdowns by registration
  status
- Migration 07 adds indexes on (is_registered) and (last_seen,
  is_registered) so summary queries use index range scans instead of
  full table scans

Frontend:
- HomeScreen replaces NotificationList, embedding notifications,
  device summary card, and ArpScannerCard in a LayoutBuilder-driven
  two-column (≥700 px) or single-column layout
- ArpScannerCard extracted to a shared public widget reused by both
  HomeScreen and StatusScreen
- Nav rail entry renamed to "Home" with home icon

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 19:10:08 -04:00
rzuastiandClaude Sonnet 4.6 dc8f47e66d Add Status screen with ARP scanner live status display
Shows ARP scanner state (running/waiting/error) with a colored indicator
and time that ticks locally every second, refreshing from the API every 5s.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 18:16:04 -04:00
rzuastiandClaude Sonnet 4.6 97e4bb1000 Add About OOTT page with version, links, and legal notices
Implements the /about route with app description, version info,
clickable links to the source repo and AGPL-3.0 license, and
third-party copyright notices. Adds url_launcher for cross-platform
link handling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 17:29:55 -04:00
rzuastiandClaude Sonnet 4.6 e28eb349ba Align frontend device types with backend canonical list
Replaces the old frontend-only `router` type and adds the full set used
by the backend: network_appliance, home_security, home_appliance, watch,
pc, gaming_console. Adds an `apiName` getter to map camelCase enum values
to the snake_case strings the API expects. Updates the unknown device icon
to a question mark.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 16:44:34 -04:00
rzuasti babf1aee41 Updated favicons 2026-05-28 11:32:35 -04:00
rzuastiandClaude Sonnet 4.6 dcb0bcaed1 Update docs, TODO progress, and add run/db helper scripts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 11:28:13 -04:00
rzuastiandClaude Sonnet 4.6 3d7f465c99 Style app title as primary-color pill with Barlow Condensed font
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 11:27:45 -04:00
rzuastiandClaude Sonnet 4.6 c34ff42177 Move favicon assets from lib/assets/ to web/ and wire up HTML/manifest
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 11:19:32 -04:00