Commit Graph

152 Commits

Author SHA1 Message Date
4f6e3684bf release: 1.0.0+10 — chat: state in Riverpod + dispose fix
Chat refactor — no setState
- ChatConversationState: new isReconciling flag in the view state
- reconcileFromRemote sets isReconciling true/false with idempotent guard and try/catch error path; isReconciling is now the single source of truth for the AppBar refresh button and the inverted pull-to-refresh spinner
- ChatConversationScreen drops the local _isRefreshing field; AppBar e inverted pull-to-refresh read state.isReconciling and dispatch the controller directly
- ChatImageBubble migrated to a pure ConsumerWidget; local file existence check and remote file resolution are now FutureProvider.family.autoDispose providers (_localFileExistsProvider, _remoteFileProvider)

Chat dispose fix
- ChatContextNotifier exposes releaseList() and releaseConversation(chatId) so callers no longer need to read .state (which is @protected)
- ChatConversationScreen and ChatListScreen schedule the release inside Future.microtask from dispose(); Riverpod 3.x prohibits mutating providers during widget lifecycle, the microtask defers the mutation past tree finalization and resolves the "Tried to modify a provider while the widget tree was building" assertion
2026-05-11 03:55:14 -05:00
abdbc2bf2e release: 1.0.0+6 — chat notifications + videocall refactor
Chat notifications (production-ready)
- ChatDeeplinkService: resolves client chat ID from incoming push/WS payloads using the (senderId, chatId) matrix and switches selectedDeviceProvider when the payload references a different watch (multi-device deeplink)
- IncomingChatResolver in domain layer with full unit coverage of the 4-case matrix
- ChatContext provider (sealed: outsideChat / list / conversation) wired into ChatListScreen and ChatConversationScreen via initState/dispose to enable WhatsApp-style suppression
- notifications_init refactor: foreground CHAT_MESSAGE notifications are suppressed on chat list and matching conversation; tap navigation goes through ChatDeeplinkService
- ChatSyncService.subscribeToReconnect: reconciles from REST when the WebSocket comes back from a disconnect (recovers messages missed in background)
- 5s message-id dedup window in the WS listener (mitigates server-side duplicate chat-message-received events)
- Reconcile from remote on conversation mount (covers cached controller from background)
- AppBar refresh button + inverted pull-to-refresh in the conversation (overscroll either edge of the reverse list)

WS event parser fix
- chat-message-received normalises to chat_message_received; parser now accepts both that and chat_message so the conversation reactively refreshes when a watch sends a message

Chat application layer
- Split the conversation controller into services: chat_send_service, chat_sync_service, chat_participants_service, chat_permission_flow_service, chat_media_cleanup_service
- chat_conversation_config centralises page size, polling interval, dedup window
- chat_bubble_shell extracted; input bar split into smaller widgets
- emoji picker sheet + emoji blocking input formatter + watch_emoji_catalog
- Multipart upload header race fixed via synchronized.Lock around the shared Dio instance

Videocall (carryover from earlier work in this branch)
- Application services: incoming, outgoing, session
- Domain entities: VideocallIncomingArgs, VideocallRoom, VideocallUserId, parseDeviceIdFromRoom helper
- Views split: idle, incoming, active call, group call
- Widgets: picture_in_picture_video, remote_or_fallback_video, video_call_header
- videocall_config centralises timeouts, ringing duration, battery threshold
- Incoming via push (channel mode) with full-screen notification + ringtone
- Hangup-on-remote-left moved to controller; redirect on participant update documented
- treezor_token_interceptor: distinguish session expiry from operation-denied 401s

Localization & misc
- New keys for chat conversation, refresh, errors across en/es/de/fr/it/pt
- Location map: dispose ref-after-unmount fix; route history layer cleanup
- Legacy device view model: position update event handling
- AndroidManifest: notification channel + permissions for incoming-call full-screen intent
2026-05-08 07:20:40 -05:00
54b81818ec feat(chat): add 1:1 and family group chat module for legacy dashboard
- Module structure mirrors location pattern (core/data, core/domain, core/providers, features)
- Supports text, emoji, image and audio messages over POST /chat-messages multipart
- Optimistic UI with status reconciliation via 4s polling and circuit breaker after 3 errors
- Offline queue persisted in SharedPreferences, drained on conversation re-open
- WhatsApp-style audio recorder with long-press, slide-to-cancel, haptics and animated overlay
- Image picker (camera/gallery) with on-device 1024px JPEG compression
- Single-audio playback coordinator across bubbles
- Family group fan-out: N parallel POSTs sharing chatId, members derived from delegationId
- Reuses LegacyOptionCard extracted from videocall idle screen
- Tracking events legacy_chat_opened, message_sent, image_sent, audio_sent, permission_denied (no PII)
- WebSocket ChatMessageEvent parser added for future backend support
- Push command CHAT_MESSAGE handled in notifications_init for deep-linking
- 15 unit tests covering id resolver, file url builder and repository

Pending backend coordination: GET /chat-messages 500 (parseQueryParams), push routing heuristic, file size/mime limits.
2026-05-05 23:32:54 -05:00
62b38acab4 chore: pending staged changes 2026-05-05 10:31:43 -05:00
4510d2bb28 fix(location): add orderBy to positions query to show latest position 2026-04-27 18:09:30 +02:00
74f470219a feat(friends): add friends list screen with delete support 2026-04-27 01:14:48 +02:00
24ddbf34e4 fix(videocall): fix SDK re-entry lifecycle and add logout on session clear
- Skip login when SDK client is already logged in (early return with isSdkReady)
- Set isSdkReady immediately when login() returns true instead of waiting for async callback
- Add ref.mounted check after userInfo future to prevent state updates on disposed provider
- Logout VideocallClient on session clear to prevent stale SDK sessions
- Use getActiveCallItem() in onCallItemUpdate for fresh call state (matches JC demo app)
2026-04-27 00:22:48 +02:00
555a668481 feat(videocall): complete signaling integration, group call support, and UI polish
- Wire VIDEO_CALL_REQUEST/CANCEL/REFUSE/ROOM_COUNT commands via CommandsRepository
- Add VideocallChatType enum (single/multi) with chatType stored in state
- Implement auto-login to Juphoon SDK using sanitized email + user UUID
- Add runtime camera/microphone permissions before call start
- Add RetryInterceptor for transient TLS/socket errors in Dio
- Migrate VideocallItem to Freezed with isTalking extension
- Implement startGroupCall/leaveGroupCall using ChannelService with participant grid
- Add PopScope to intercept back navigation during active calls
- Redesign idle screen with device option cards and group call button
- Redesign active call UI with video overlay, PiP local view, and new controls layout
- Clean up SDK wrapper: remove unused streams, merge destroy+dispose into shutdown
- Add i18n keys for videocall UI across 6 locales
2026-04-26 21:53:32 +02:00
5aa0c0acc7 fix(videocall): apply PR review fixes and add i18n translations
- Replace setState with FutureBuilder in VideoViewWidget
- Remove comments from datasource impl
- Replace GetIt.I with ref.read(provider) in controllers
- Fix state comparison using isTalking getter
- Add hangUp/stopAudio/stopCamera cleanup on dispose
- Remove duplicate SafeArea from IncomingCallOverlay
- Add missedCall error event to enum
- Replace Colors.white70 with theme colorScheme
- Move VIDEOCALL_INTEGRATION.md to apps/mobile_app/docs/
- Gitignore config JSON files with app keys
- Add 28 i18n keys in 6 locales for all videocall strings
- Map error events to specific i18n messages
2026-04-26 21:53:32 +02:00
9f23ecb42e feat(videocall): integrate videocall feature with architecture refactor
Merges videocall stash, resolves conflicts with installed_apps routes,
and refactors UI to match current legacy patterns:
- Replace themePortProvider with context.sfColors and Theme.of(context)
- Replace showTopSnackbar with feedback dialogs
- Replace hardcoded colors with theme-aware colorScheme
- Wrap test login button in kDebugMode
- Rename error enum values to be more descriptive
2026-04-26 21:53:32 +02:00
a87c7e8732 refactor(heartbeat): refresh devices every 2min instead of pinging /auth/me 2026-04-26 21:53:05 +02:00
bbdaa25e12 feat(navigation): add routes for installed apps and app usage schedules 2026-04-26 21:53:03 +02:00
99da6e12fe todo 2026-04-26 21:53:01 +02:00
6ff11b8c1e fix(router): rename duplicate notifications route name 2026-04-26 21:52:59 +02:00
c3dcc6febc feat(legacy-settings): DST-aware timezone with phone auto-detect 2026-04-26 21:52:57 +02:00
3147566241 refactor(sf_shared): add help center URL to BrandLinks 2026-04-26 21:52:55 +02:00
f7e69b1184 refactor(sf_shared): move brand links to Firebase Remote Config 2026-04-26 21:52:55 +02:00
8cd01c6f3b feat(sf_tracking): consent-aware crashlytics wrapper 2026-04-26 21:52:54 +02:00
72d0c79c74 Revert "docs: add snackbar messages reference in Spanish"
This reverts commit 7ea415cb6e.
2026-04-26 21:52:53 +02:00
5b1826a10d docs: add snackbar messages reference in Spanish 2026-04-26 21:52:53 +02:00
e30f5dabcc fix(notifications): handle deep linking on cold start without crash 2026-04-26 21:52:52 +02:00
f7d3dbfd27 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-26 21:52:50 +02:00
afa916a30d feat(videocall): add native permissions for video calling
Android: RECORD_AUDIO, ACCESS_WIFI_STATE, MODIFY_AUDIO_SETTINGS,
BLUETOOTH permissions + camera/bluetooth uses-feature + ProGuard
rules for Juphoon/JusTalk.

iOS: NSMicrophoneUsageDescription, NSPhotoLibraryUsageDescription,
updated NSCameraUsageDescription + Podfile GCC_PREPROCESSOR_DEFINITIONS
for camera, photos and microphone permissions.
2026-04-17 15:51:27 +02:00
4347cefaed feat(videocall): add videocall_sdk package wrapping Juphoon jc_sdk
Full wrapper around jc_sdk v2.16.5 with clean architecture:
- 7 services covering 100% of jc_sdk public API (Client, Call, Device, Channel, Push, Net, Log)
- Constructor injection with GetIt DI module (follows sca_treezor pattern)
- VideocallSdkManager orchestrator for init/destroy lifecycle
- VideocallSdkConfig abstract for environment-specific AppKey
- Stream-based callbacks for reactive UI consumption
- Riverpod providers (service + stream) for feature layer
- AppKey configured per environment via dart-define-from-file
- Integrated in init_app.dart alongside scaTreezorModule
2026-04-17 15:17:56 +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
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
cbc40f7d95 feat(alerts): add device alerts screen with pagination, filters, and WebSocket 2026-04-17 04:00:25 +02:00
e83adbfdbf feat(firebase): add production config and push token refresh listener 2026-04-17 03:07:57 +02:00
238c15888b fix(interceptor): disable 500 token-expired handler in legacy mode 2026-04-17 03:05:59 +02:00
cda889a15b feat(websocket): add WebSocket service with typed events and auto-reconnect 2026-04-16 16:47:34 +02:00
514daf9c7c feat(do-not-disturb): add DND schedule feature with capabilities-driven UI 2026-04-16 13:54:13 +02:00
c7e32d1399 refactor(activity-meter): redesign screen with honest per-range stats 2026-04-15 21:51:08 +02:00
703b1e9fba docs(analytics): update catalog for signup and recover password changes 2026-04-15 17:07:02 +02:00
2fe5a2399d fix(app): scope user analytics listener to authenticated shells 2026-04-15 17:06:57 +02:00
cbaff2e763 analytics catalog 2026-04-15 12:00:36 +02:00
f36bc9afc1 fix(router): unique navigatorKey per StatefulShellBranch to avoid duplicate GlobalKey crash 2026-04-15 11:32:23 +02:00
95a03434ca fix(antelop): route FCM pushes through SDK, per-flavor plist on iOS, unify applicationId
- Add AntelopAwareMessagingService so the Antelop SDK gets first dibs on every
  FCM push before delegating to firebase_messaging. Unblocks SCA wallet
  activation on Android, which was waiting forever for pushes that the
  FlutterFirebaseMessagingService was swallowing ever since Firebase was
  integrated. Remove the stock Antelop and firebase_messaging services via
  manifest-merge so only the wrapper handles MESSAGING_EVENT.
- Add Copy AntelopRelease Build Phase in Xcode to copy
  Runner/AntelopRelease-{flavor}.plist over the fixed AntelopRelease.plist
  inside the .app bundle based on CONFIGURATION. Without this the iOS SDK
  silently used the production plist on every flavor.
- Revert the six applicationId values (3 AndroidManifest + 3 AntelopRelease
  plist) to the sample id 4713640103500149457, which is the only one
  provisioned in Antelop's backend today. The four env+platform ids they
  provided all fail with 9999 / cryptography: Error while decrypting data.
  To be updated once Treezor confirms the real ids.
- Add packages/flutter_treezor_entrust_sdk_bridge/example as a pub workspace
  member and bump its Dart SDK and flutter_lints constraints so
  'melos bootstrap' stops failing with 'dependencies for
  flutter_treezor_entrust_sdk_bridge_example missing'.
2026-04-15 11:25:06 +02:00
ec14ad49e5 bump build to 9 2026-04-14 15:16:39 +02:00
811e92defc bump build to 8 2026-04-14 11:03:10 +02:00
693f55369c feat(version-check): add in-app update prompt with Remote Config
Adds a production-grade in-app version check that prompts users to update when a new build is available. Soft updates are dismissable. Force updates block the app entirely. Configured via FirebaseRemote Config so rollouts can be triggered without redeploying.

- Sealed result types (NoUpdate / SoftUpdate / ForceUpdate) for type-safe pattern matching
- AppVersionCheckService
- AppUpdateGate widget encapsulates listener, route guard and dialog wireup, isolated from save_family_app
- Serialized notifier operations prevent race between dismiss and refresh, mounted checks blindar disposal edge cases                                                                                                  - Build-aware dismiss persistence via SharedPreferences
2026-04-09 14:52:37 +02:00
7445021cf3 bump build to 7 2026-04-08 16:30:38 +02:00
29887818f9 melos format 2026-04-07 17:07:47 +02:00
42ec003b05 refactor(tracking): tighten sf_tracking package
- Lazy-init sfTracking to avoid touching Firebase at import time
- DRY SfTrackingRepository with a single _broadcast helper
- Drop empty DashboardTracking, fix double step_completed in device_setup
- Move yearsBetween to packages/utils
- Add 5 unit tests for SfTrackingRepository
- Strip noisy comments from mixins and view models
2026-04-07 16:59:38 +02:00
7b91447cad feat(tracking): add sf_tracking package and instrument legacy module
Introduces packages/sf_tracking — a multi-client, GDPR-first analytics layer with feature mixins, a GoRouter listener for automatic screen views, and a user properties helper that runs on login.
Wires the package into the legacy module 61 events
2026-04-07 13:47:07 +02:00
81284d7efe feat(firebase): integrate Firebase + APNs/FCM push notifications
Phase 2 of multi-environment setup. Adds Firebase core, Crashlytics,
Analytics, Remote Config, Performance, Messaging and flutter_local_notifications,
plus full APNs configuration for iOS push.

- Wire setupFirebase(env) and setupNotifications() in initApp
- Add firebase_options for dev and staging via flutterfire (sf-platform-pre)
- Register google-services / firebase-perf / crashlytics gradle plugins
- Add per-flavor GoogleService-Info.plist with Build Phase script that
  copies the right plist into the .app bundle based on \$CONFIGURATION
- Bump iOS deployment target 13.0 -> 15.0 (required by firebase_analytics)
- Pin flutter_local_notifications to ^19.4.2 (v20+ needs Dart SDK >=3.10)
- Add aps-environment to staging (development) and production entitlements;
  development flavor intentionally excluded (no App Store Connect entry)
- Fix AppDelegate.swift to call super.application after forwarding to
  AntelopAppDelegate, otherwise Firebase Messaging swizzling breaks and
  the APNs token is never captured
- Crashlytics reports in all builds (debug + release) for early detection
- Tag analytics events with env user property per flavor
- App Check intentionally not included (debug-token friction with large
  QA team); can be re-added release-only later
2026-04-07 03:33:25 +02:00
c1954497b8 chore: add root .gitignore and untrack auto-generated cache files
The repo had no root-level .gitignore, so .dart_tool/, ephemeral plugin
symlinks and build artifacts were being tracked across the workspace.
Every flutter pub get regenerated them and they polluted PRs as recurring
noise.

- Add root .gitignore covering .dart_tool/, ephemeral plugin symlinks,
  build/, coverage/, IDE files
- Untrack ~39 existing cache files across root, modules and packages
2026-04-07 03:30:35 +02:00
9cdb4c7724 refactor: rename Questia to SaveFamily across the codebase 2026-04-07 00:47:29 +02:00
a560e19db2 fix: configure Antelop SDK applicationIds per environment 2026-04-07 00:37:39 +02:00
c263e4227e feat: split legacy/payment apps via APP_MODE flag 2026-04-07 00:09:48 +02:00