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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
The device event history chart previously fetched all events and filtered
client-side. This wires the existing created_from API parameter to the
frontend so filtering happens in the backend.
Also fixes a bug where the Today filter returned no results: rusqlite was
storing datetimes with a space separator ("YYYY-MM-DD HH:MM:SS+00:00")
while SQL filters used RFC 3339 with a T separator, causing string
comparisons to fail for same-day events. All datetime storage across
device_events, devices, and notifications is now consistently RFC 3339.
A migration converts existing records.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Displays a scatter chart timeline of when the device was seen online,
with a time range selector (Today → Last Year). Tooltips show event
date/time, type, and highlight any IP or vendor changes relative to
the current device data.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Owner filter uses a server-side substring match (LIKE '%VALUE%');
device type filter matches the stored value exactly, sending an
empty string for unknown/unclassified devices. Both filters combine
with the existing registration status chip via AND logic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New /devices/:macAddress route showing all device fields with Register/Forget actions
- Device list items are now tappable; popup menu gains a "View details" item
- Notification list items with a linked device are tappable; popup menu gains a "View device" item
- Backend: new DB migration adds optional mac_address column to notifications
- Device-related notifications (NewDeviceFound, DeviceOnlineAfterTime, DeviceChanged) now store the triggering device's MAC address
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Centralises all SnackBar calls behind UISnackbars.showError/showSuccess/
showWarning/showInfo, each themed via AppColorExtension. New warning and
info colour tokens added to both themes. Calling any method clears any
visible snackbar before showing the new one.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a context menu to each device card with contextual actions:
- Unregistered devices show a Register option (dialog with owner field
and device type dropdown)
- Registered devices show a Forget option (confirmation dialog)
Also fixes CORS to allow PUT and DELETE methods, adds device type
tooltips on the icon, and renames the "New" filter/badge to
"Not registered".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lists devices with New/Registered/All filtering, pull-to-refresh,
device-type icons, and visual differentiation between new and registered
devices. Introduces a reusable StatusBadge widget for colour-coded labels.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Show "Just now" for notifications 2 minutes old or less
- Auto-refresh the list every minute, preserving the active filter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows a done_all icon button in the AppBar when the New filter is active
and the list is non-empty, calling the notifications/mark_all_as_old endpoint.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Adds a "Mark as unread" option to the notification popup menu (shown only
for already-read items) and a swipe-right gesture, mirroring the existing
"Mark as read" actions. Swipe/menu actions respect the active filter:
items are removed from the list only when filtered to New or Old, and
snap back when viewing All.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Users can now mark a notification as read via a popup menu on each list
item or by swiping left. Both actions call the backend API, remove the
item from the list, and show a snackbar confirmation. Swiping an already
read notification shows an error snackbar and bounces the item back.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>