Commit Graph

290 Commits

Author SHA1 Message Date
72c88cc4b0 refactor(legacy-account): migrate app_users to FutureProvider 2026-04-21 20:03:45 +02:00
b21b234b9a feat(sf_tracking): consent-aware crashlytics wrapper 2026-04-21 19:33:54 +02:00
f89bca99b3 feat(sf_shared): add FailureType + handleFailure + feedback dialogs 2026-04-21 19:32:49 +02:00
1056895c31 chore(legacy): add test infrastructure + mocktail 2026-04-21 19:32:40 +02:00
424b8d9034 fix 2026-04-21 18:24:25 +02:00
4aa91c355e Revert "docs: add snackbar messages reference in Spanish"
This reverts commit 7ea415cb6e.
2026-04-21 18:15:07 +02:00
7ea415cb6e docs: add snackbar messages reference in Spanish 2026-04-21 17:59:58 +02:00
21fd1e0197 chore(i18n): add missing translation keys and device debug logging 2026-04-21 17:59:50 +02:00
d618ed76d0 feat(wifi): enable wifi settings entry point and add debug logging 2026-04-21 17:59:39 +02:00
dfd7ba9c41 fix(sos-contacts): prevent deleting last emergency contact 2026-04-21 17:59:29 +02:00
b8f5c5d6f8 feat(block-phone): add edit contact functionality 2026-04-21 17:59:16 +02:00
d470ed470a fix(location): add sharePositionOrigin for iPad share 2026-04-21 17:59:08 +02:00
a400fef77d fix(control-panel): match dropdown value by device id to prevent crash 2026-04-21 17:59:00 +02:00
febc21a590 fix(do-not-disturb): return empty schedule on 404 instead of error 2026-04-21 17:58:52 +02:00
29fca859fc fix(personal-data): send all required fields in PUT and autofill form 2026-04-21 17:58:44 +02:00
d92fe887fd fix(rewards): send rewards key instead of amount in command data 2026-04-21 17:58:27 +02:00
315e5b2908 fix(volume): use capabilities max per slider and fix 0-10 scale 2026-04-21 17:58:20 +02:00
244e5bbd03 feat(capabilities): add volume limits (media, ringtone, alarm) 2026-04-21 17:58:12 +02:00
a86041885c feat(settings): add falldown, falldownLevel and rename gps to location 2026-04-21 17:58:03 +02:00
12011ce525 fix(notifications): handle deep linking on cold start without crash 2026-04-21 17:57:53 +02:00
c92e2fb67f refactor(legacy): align with Riverpod architecture (DTOs, repos, drop UseCases) 2026-04-19 23:20:38 +02:00
7e1ead9cae refactor(legacy): split legacy_shared into cohesive packages
legacy_shared was the junk drawer of the legacy app mode — 37 files
mixing device entities, command infrastructure, UI primitives, generic
formatters, and a duplicate of sf_infrastructure's dio_error_mapper.
Any module needing one piece pulled the whole bag into its graph.

Split it by responsibility, following Mandamiento 4 of Real-World Flutter:

- legacy_ui (new): 6 widget/layout primitives (PageLayout, MenuButton,
  SectionButton, PulsingLocationMarker, RefreshableErrorState,
  WeekDayChips) plus mapStyleProvider — shared UI state that was the
  only reason two modules needed a common package.
- legacy_device_state (expanded 9 → 30): absorbed device entities,
  commands infrastructure (datasource + repo + provider + guard),
  device settings update flow, and the CSV exporter. Now one package
  owns the device domain end-to-end.
- packages/utils: absorbed battery_utils and date_format_utils as pure
  formatters that never belonged in a legacy-scoped package.
- legacy_shared: deleted entirely.

The duplicate dio_error_mapper in legacy_shared is gone; callers now use
the sf_infrastructure version (which was always the superset — it adds
ApiException and the dart:io socket handling).

DeviceEntity note: legacy_device_state keeps its own DeviceEntity (with
int timestamps and typed paymentOptions) separate from sf_shared's
DeviceEntity (String timestamps, untyped paymentOptions). The legacy
one is intentionally not exported from the barrel to avoid the
ambiguous_import collision that legacy_shared quietly hid by never
exporting it in the first place. Unifying the two is a domain-model
refactor out of scope here.

0 cross-module imports remain among legacy feature modules.
2026-04-19 05:42:31 +02:00
e59ce36033 chore(sf_localizations): switch i18n source to es.json and add parity check
Spanish is the app default (SFLocalizations.testInit uses 'es',
localeResolutionCallback falls back to the first supported locale), so
make that explicit by pointing the code generator at es.json instead of
en.json. Regenerating picked up 12 activity-meter keys that were already
present in every locale file but had drifted out of I18n.

Add scripts/check_i18n_parity.dart: treats es.json as the template and
reports any missing or orphan keys in en/fr/de/it/pt. Exits non-zero so
it can gate CI or a pre-commit hook later.
2026-04-19 04:58:09 +02:00
aa3ffdb6a7 refactor(legacy): migrate theming to Material 3 + SfColors extension
Replace the ThemePort/ThemeCode abstraction (GetIt-registered adapter)
with a Riverpod-driven Material 3 ColorScheme, an SfColors ThemeExtension
for brand tokens, and a user-facing appearance selector for light/dark/
system modes. Persisted via SharedPreferences, reacts to system
brightness changes. Payments mode keeps the existing ThemePort API.

Highlights
- New legacy_theme package: LegacyAppTheme (light/dark), LegacyColorSchemes,
  SfColors ThemeExtension, LegacyThemePreferences, LegacyThemeNotifier,
  LegacyThemeSelector. Timeframe-based variants scaffolded but disabled.
- New /legacy/dashboard/control_panel/settings/appearance route + screen.
- MaterialApp.router picks the legacy theme only when isLegacyMode.
- ~90 ThemeCode.* usages migrated to colorScheme.* / context.sfColors.*.
- 25 widgets dropped the 'ThemePort theme' constructor param.
- ~145 hardcoded colors migrated (exact hex 1:1, grey.shade tiers,
  destructive red -> colorScheme.error, background whites -> surface).
  Content-over-color whites, transparents, and brand semantic reds/
  oranges/greens intentionally preserved.
- sf_localizations updated with appearance / appearanceDescription keys
  in all six locales.
2026-04-19 04:47:22 +02:00
2eddb99c47 chore: ignore flutter auto-generated files
These files are regenerated by flutter pub get and contain machine-
specific paths (FLUTTER_ROOT, FLUTTER_APPLICATION_PATH). They should
not be tracked — the file header itself states "do not check into
version control".

- Update .gitignore: prepend **/ to .flutter-plugins-dependencies so
  it matches in sub-packages, add Generated.xcconfig and
  flutter_export_environment.sh patterns.
- git rm --cached the 4 files that were already tracked (keeps them
  on disk for local builds but removes from repo).
2026-04-19 01:58:45 +02:00
c461519597 refactor(legacy): extract shared device state into legacy_device_state package
- New package legacy_device_state hosts shared device/position state
  consumed by both control_panel and location screens. ControlPanel*
  classes renamed to LegacyDevice* in 8 moved files.
- Move PositionEntity + AddressEntity + NetworkEntity +
  LatestPositionsResponseModel + BatteryUtils + DateFormatUtils from
  control_panel to legacy_shared (shared domain entities).
- Remove anti-pattern re-export of sf_infrastructure/dio_error_mapper
  from legacy_shared barrel. Update consumers to import
  sf_infrastructure directly.
- Clean 14 unused legacy_shared imports across view models in
  account, device_management, legacy_auth and settings modules.

Eliminates cross-module dependency location -> control_panel.
0 errors, 0 warnings in flutter analyze across 11 packages/modules.
2026-04-19 01:55:11 +02:00
919ee55c45 fix(location): keep map visible on transient network errors
Show snackbar instead of replacing the entire map with an error state
when a reload fails. Only show RefreshableErrorState on first load failure.
2026-04-17 17:49:25 +02:00
f5350f5e78 fix(location): guard frequency timer minimum and filter zero options
Prevent position polling timer from firing with frequency < 60s (protects
against 0s values that saturate the API). Also filter out 0 from location
capability options in the frequency selector.
2026-04-17 17:11:01 +02:00
e7ebe7f403 fix 2026-04-17 15:13:34 +02:00
ed41b82076 chore(legacy): hide wifi settings and forgot password entry points
Comment out the navigation entry points for the wifi settings flow
from the device settings list and for the forgot-password link from
the login screen while these flows wait for product sign-off. The
underlying screens and providers stay in place so re-enabling them is
a single uncomment.
2026-04-17 11:16:08 +02:00
9470f54867 chore(notifications): log push token registration outcome for debugging
Add debugPrint entries before and after POST /universal-notifications/push
so we can trace whether a given token was accepted by the backend
during debugging of push delivery issues.
2026-04-17 11:13:59 +02:00
f82d222df3 style(device_setup): redesign activation code dialog with icon and OK action
Replace the bare centered text with a rounded dialog that leads with a
mail icon, keeps the activation-code message, and adds an explicit OK
button to dismiss. Aligns the dialog with the rest of the legacy
design system.
2026-04-17 11:13:53 +02:00
fd8ef27185 feat(splash): route to device setup when a logged-in user has zero devices
Extend CheckSessionUseCase to fetch /devices after confirming the
session and return InitialRoute.deviceSetup when the list is empty
(or the call fails). The splash builder now injects a
SharedDevicesRepository alongside the user repository, and the app
router maps the new route to legacyDeviceSetup / deviceSetup per
shell. The legacy builder reads legacyDevicesProvider to derive
isFirstDevice so the setup screen adapts its copy accordingly.
2026-04-17 11:13:48 +02:00
5b6ed5cf16 fix(config): swap production apiBaseUrl and apiOrigin back to correct hosts
The two fields had been flipped, pointing API calls at the origin host
(platform.savefamily.app) and sending the Origin header as the gateway
host (api-platform.savefamily.app). Restore the intended wiring:
gateway handles /api, and Origin is the platform domain.
2026-04-17 11:13:40 +02:00
bf1032245a refactor(device_setup): type API errors with status-code-based enum
Introduce LegacyDeviceSetupErrorEvent and two mappers for the
generate-activation-key and create-device endpoints. 403 on
activation-key maps to invalidIdentificator; 404 to deviceNotFound;
401 (already activated or B2B) unifies as deviceNotAvailable. On
create-device, 403 → invalidField and 404 → invalidActivationKey.
The view state now splits validation vs API errors with a
displayErrorKey extension, replacing the previous e.toString() leak
that could surface stack traces to users.
2026-04-17 11:12:37 +02:00
fad2c8792c refactor(recover_password): type API errors and hide email enumeration
Map PUT /auth/reset-password and PUT /auth/recovery-password failures
into LegacyRecoverPasswordErrorEvent. Reset-password now treats 404
(email not found) as success and surfaces a generic sent-if-exists
flow, closing an account enumeration vector. Recovery-password
differentiates 401 (tokenExpired), 404 (tokenNotFound), 403+Property
(invalidField) from 403 without Property (weakPassword). The view
state splits validation vs API errors with a displayErrorKey extension
for the inline error text.
2026-04-17 11:12:14 +02:00
73d9de45a2 refactor(signup): type API errors with status-code-based enum
Introduce LegacySignupErrorEvent to map backend failures from
POST /auth/signup: 400 → emailAlreadyExists, 403 → invalidField,
429 → tooManyAttempts, timeout → network. The view state now
separates validationErrorKey (pre-submit i18n keys) from apiErrorEvent
(typed API outcomes), and the screen listens to both to show proper
i18n messages instead of leaking raw backend text.
2026-04-17 11:11:52 +02:00
56d89fcdc4 refactor(login): type auth errors with status-code-based enum
Replace the raw errorMessage string in the login view state with a
typed LegacyAuthErrorEvent that classifies backend failures by HTTP
status (403/404 → invalidCredentials, 423 → accountLocked, 401 +
NOT_VERIFIED/expired → dedicated events, 429 → tooManyAttempts,
timeout → network). The login screen and 2FA sheet now switch on the
enum to show specific i18n messages instead of surfacing raw backend
text. Adds auth i18n keys for the full set of mapped states.
2026-04-17 11:11:23 +02:00
eff6f01924 feat(infra): preserve status code and network flag in ApiException
Replace the untyped Exception(msg) thrown by mapDioError with an
ApiException that exposes statusCode and isNetworkError alongside the
message. Callers can now classify failures by HTTP status without
string-matching on the error message — this unblocks typed error
mapping in the auth feature modules.
2026-04-17 11:10:13 +02:00
72d44b81df feat(legacy): add pull-to-refresh for control_panel and location
Introduce a shared RefreshableErrorState widget that wraps the retry
hint in a RefreshIndicator with an explicit 'pull down to retry'
caption, so users can recover from load failures without navigating
away. Wire it into the location screen's error fallbacks and make the
control_panel body pull-to-refresh at any time, invalidating the device
list so the dashboard picks up fresh data.
2026-04-17 09:44:29 +02:00
2942d7393e feat(wifi_settings): support setting saved networks as current
Add a setNetwork action that sends the setWifi command for a saved
network and waits for wifiCurrent confirmation with a 15s timeout.
Saved network cards are now tappable to trigger the switch, and
dedicated success/error states surface the result via snackbar.
2026-04-17 09:44:16 +02:00
0b160758e2 refactor(legacy): make guardDeviceCommand async with stale TTL refetch
Convert the shared command guard to an async check that refetches
/devices when the cached state is older than 30s, so the isDisconnect
flag reflects reality before a command runs. A TopSnackBar explains the
check only if the fetch takes longer than 400ms, avoiding noise on fast
responses. Update all 44 call sites to await the guard.
2026-04-17 09:43:52 +02:00
ecbb6d1e76 feat(notifications): handle tap navigation to device alerts
Route FCM and local notification taps to the device alerts screen
when the user is already inside the legacy dashboard. Adds payload
parsing with command-based routing and richer debug logs.
2026-04-17 09:42:07 +02:00
e7a4653c01 refactor(shared): move device providers and data layer from legacy_shared to sf_shared
Device providers (legacyDevicesProvider, selectedDeviceProvider), repository,
datasource, and GetDevicesResponseModel now live in sf_shared. Also moved
dio_error_mapper (safeCall, mapDioError, formatErrorMessage) to sf_infrastructure.
Consumers import directly from sf_shared instead of re-exporting through legacy_shared.
2026-04-17 08:48:38 +02:00
05ffe572c8 fix(logout): invalidate device providers to clear stale data on user switch 2026-04-17 04:10:33 +02:00
cbc40f7d95 feat(alerts): add device alerts screen with pagination, filters, and WebSocket 2026-04-17 04:00:25 +02:00
27e26ca921 refactor(utils): extract query params builder, migrate health and location 2026-04-17 04:00:12 +02:00
e83adbfdbf feat(firebase): add production config and push token refresh listener 2026-04-17 03:07:57 +02:00
973fc2490c feat(wifi-settings): redesign with device commands, WebSocket scan, and connect flow 2026-04-17 03:06:23 +02:00
e148b9fdfa fix(websocket): handle notification wrapper, hyphenated types, and stringified data 2026-04-17 03:06:09 +02:00