Skip to content

feat: add configurable trade history retention in settings#571

Merged
grunch merged 8 commits into
mainfrom
settings-cleanup-orders
Apr 17, 2026
Merged

feat: add configurable trade history retention in settings#571
grunch merged 8 commits into
mainfrom
settings-cleanup-orders

Conversation

@Catrya

@Catrya Catrya commented Apr 9, 2026

Copy link
Copy Markdown
Member
  • Add session expiration selector with 6 presets: 1 week, 1 month, 3 months, 6 months, 1 year, never
  • Default remains 1 month (720 hours), stored in Settings and persisted via SharedPreferences
  • SessionNotifier reads expiration from user settings with Config fallback
  • Value 0 means never delete, protected by explicit guards in init() and cleanup()
  • New Trade History card in settings screen between Notifications and Dev Tools
  • Localization keys added for EN, ES, IT, FR, DE
  • 11 unit tests covering serialization, presets, backward compatibility, and never mode
  • Includes log output when user changes the setting

Summary by CodeRabbit

  • New Features

    • User-configurable session expiration (hours) with presets (1w/1m/3m/6m/1y/never); new Trade History settings card and selector; setting is persisted.
  • Behavior

    • Expiration now cascades deletion of expired sessions and associated data; “never” loads all sessions (no cutoff).
  • Internationalization

    • Added DE/EN/ES/FR/IT translations for trade-history UI and options.
  • Tests

    • Added tests for defaults, updates, clearing, serialization, and presets.
  • Documentation

    • Updated docs covering retention, “never” mode, and cleanup.

@coderabbitai

coderabbitai Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a nullable Settings.sessionExpirationHours persisted to prefs, UI for trade-history retention, notifier method to update it, session init/cleanup to respect the setting (0 = never) with cascade deletion of session-related data, localization keys, tests, and documentation updates.

Changes

Cohort / File(s) Summary
Settings model & notifier
lib/features/settings/settings.dart, lib/features/settings/settings_notifier.dart
Add final int? sessionExpirationHours to Settings; wire into constructor, copyWith() (with clearSessionExpiration), toJson()/fromJson(); add SettingsNotifier.updateSessionExpirationHours(int) that persists and logs changes.
Settings UI
lib/features/settings/settings_screen.dart, lib/shared/widgets/trade_history_selector.dart
Insert "Trade History" card in settings and add TradeHistorySelector ConsumerWidget rendering a retention dropdown; selection shows confirmation and calls notifier to update setting.
Session lifecycle & init / cleanup
lib/shared/notifiers/session_notifier.dart, lib/shared/providers/app_init_provider.dart
Derive expiration hours from settings with fallback to Config; treat 0 as “never” (no cutoff); compute cutoff when applicable; skip deletion for active sessions; add _cleanupSessionData to cascade-delete per-session data (eventStore by order/dispute, mostro messages, notifications, SharedPreferences read-state) and wrap cleanup calls in try/catch.
Repositories
lib/data/repositories/notifications_history_repository.dart
Add Future<void> deleteByOrderId(String orderId) to NotificationsRepository and implement deletion in storage.
Status enum
lib/data/models/enums/status.dart
Add bool get isTerminal to classify terminal order statuses.
Localization
lib/l10n/intl_en.arb, lib/l10n/intl_de.arb, lib/l10n/intl_es.arb, lib/l10n/intl_fr.arb, lib/l10n/intl_it.arb
Add tradeHistory keys, tradeHistoryDescription, tradeHistoryInfoText, and duration option keys (oneWeek, oneMonth, threeMonths, sixMonths, oneYear, never); minor punctuation tweak to tooManyRequests.
Tests
test/features/settings/session_expiration_test.dart
Add tests for default/null behavior, fallback to Config.sessionExpirationHours, copyWith() set/update/clear semantics, 0 = never behavior, and JSON serialization/deserialization roundtrips for preset values.
Docs
docs/architecture/...
docs/architecture/APP_INITIALIZATION_ANALYSIS.md, .../SESSION_AND_KEY_MANAGEMENT.md, .../TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md
Update docs to describe user-configurable retention presets (default 720h), “never” mode, cascade-deletion of associated session data, and adjusted init/cleanup flow descriptions.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant UI as SettingsScreen
    participant Notifier as SettingsNotifier
    participant Model as SettingsModel
    participant Session as SessionNotifier
    participant Config as Config

    User->>UI: open trade history settings
    UI->>UI: show dropdown of presets
    User->>UI: select retention hours
    UI->>Notifier: updateSessionExpirationHours(newValue)
    Notifier->>Model: copyWith(sessionExpirationHours: newValue)
    Notifier->>Notifier: _saveToPrefs()

    Note over Session,Model: On init or periodic cleanup
    Session->>Model: read sessionExpirationHours
    alt sessionExpirationHours is null
        Session->>Config: use Config.sessionExpirationHours
    else sessionExpirationHours == 0
        Session->>Session: skip cleanup (forever)
    else
        Session->>Session: compute cutoff and delete expired sessions + cascade cleanup
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • BraCR10
  • grunch

Poem

🐇
I nibbled at settings, a ribbon of time,
Weeks, months, or forever — pick your rhyme.
Trade tracks I tuck or let them roam free,
A hop, a toggle, now history's key. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add configurable trade history retention in settings' clearly and concisely summarizes the main change: introducing user-configurable session expiration/trade history retention in the settings UI.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch settings-cleanup-orders

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
lib/features/settings/settings_notifier.dart (1)

208-213: Guard sessionExpirationHours against invalid values before persisting.

This setter accepts any integer. Invalid values (especially negatives) can break retention semantics in downstream cutoff calculations. Consider restricting to known presets plus 0 (“never”).

Proposed hardening
   Future<void> updateSessionExpirationHours(int newValue) async {
+    const allowedValues = <int>{0, 168, 720, 2160, 4320, 8760};
+    if (!allowedValues.contains(newValue)) {
+      logger.w('Ignoring invalid session expiration value: $newValue');
+      return;
+    }
+
     final oldValue = state.sessionExpirationHours;
+    if (oldValue == newValue) return;
+
     state = state.copyWith(sessionExpirationHours: newValue);
     await _saveToPrefs();
     logger.i('Session expiration changed: $oldValue → $newValue hours');
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/settings/settings_notifier.dart` around lines 208 - 213, The
updateSessionExpirationHours setter currently accepts any int; validate the
incoming newValue in updateSessionExpirationHours before mutating state and
calling _saveToPrefs: ensure newValue is one of the allowed presets (e.g., 0 for
“never” and the defined retention options) and reject or clamp negatives/unknown
values (log/warn and return early or set to a safe default). Use
state.sessionExpirationHours and the same copyWith/state update pattern only
after validation, and keep the existing logger.i call to record the effective
change.
lib/shared/notifiers/session_notifier.dart (1)

90-93: Avoid running periodic no-op cleanup when retention is set to “Never”.

_cleanup() now exits early for _isForever, but the periodic timer still triggers. Consider skipping scheduling in that mode and re-scheduling when settings change.

♻️ Suggested refactor
 void _scheduleCleanup() {
   _cleanupTimer?.cancel();
+  if (_isForever) return;
   _cleanupTimer = Timer.periodic(
     const Duration(minutes: Config.cleanupIntervalMinutes),
     (timer) => _cleanup(),
   );
 }

 void updateSettings(Settings settings) {
   _settings = settings.copyWith();
+  _scheduleCleanup();
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shared/notifiers/session_notifier.dart` around lines 90 - 93, The
periodic cleanup timer is still scheduled even when _cleanup() immediately
returns for _isForever; update the scheduling logic to skip creating the timer
when _isForever is true and to cancel any existing timer when settings flip to
forever. Locate the timer creation/management code (e.g., _startCleanupTimer,
_schedulePeriodicCleanup or the constructor where a Timer/Timer.periodic is
created) and add a guard that only schedules the periodic Timer when
!_isForever; also ensure you cancel and null out the timer (e.g.,
_cleanupTimer?.cancel(); _cleanupTimer = null) when _isForever becomes true and
reschedule the timer when settings change back to finite retention.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/features/settings/settings_notifier.dart`:
- Around line 208-213: The updateSessionExpirationHours setter currently accepts
any int; validate the incoming newValue in updateSessionExpirationHours before
mutating state and calling _saveToPrefs: ensure newValue is one of the allowed
presets (e.g., 0 for “never” and the defined retention options) and reject or
clamp negatives/unknown values (log/warn and return early or set to a safe
default). Use state.sessionExpirationHours and the same copyWith/state update
pattern only after validation, and keep the existing logger.i call to record the
effective change.

In `@lib/shared/notifiers/session_notifier.dart`:
- Around line 90-93: The periodic cleanup timer is still scheduled even when
_cleanup() immediately returns for _isForever; update the scheduling logic to
skip creating the timer when _isForever is true and to cancel any existing timer
when settings flip to forever. Locate the timer creation/management code (e.g.,
_startCleanupTimer, _schedulePeriodicCleanup or the constructor where a
Timer/Timer.periodic is created) and add a guard that only schedules the
periodic Timer when !_isForever; also ensure you cancel and null out the timer
(e.g., _cleanupTimer?.cancel(); _cleanupTimer = null) when _isForever becomes
true and reschedule the timer when settings change back to finite retention.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aafa8c9e-88ef-4db8-bfa6-5a05432a0d16

📥 Commits

Reviewing files that changed from the base of the PR and between 37e8024 and 31428c5.

📒 Files selected for processing (10)
  • lib/features/settings/settings.dart
  • lib/features/settings/settings_notifier.dart
  • lib/features/settings/settings_screen.dart
  • lib/l10n/intl_de.arb
  • lib/l10n/intl_en.arb
  • lib/l10n/intl_es.arb
  • lib/l10n/intl_fr.arb
  • lib/l10n/intl_it.arb
  • lib/shared/notifiers/session_notifier.dart
  • lib/shared/providers/app_init_provider.dart

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
lib/shared/widgets/trade_history_selector.dart (1)

28-29: Avoid hardcoding 720 for dropdown fallback.

At Line 29, the literal 720 can drift from Config.sessionExpirationHours. Use a config-derived default (and guard that it exists in _optionKeys) to keep UI behavior aligned with runtime config.

♻️ Suggested refactor
-    final displayValue =
-        _optionKeys.containsKey(effectiveValue) ? effectiveValue : 720;
+    final defaultValue = _optionKeys.containsKey(Config.sessionExpirationHours)
+        ? Config.sessionExpirationHours
+        : _optionKeys.keys.first;
+    final displayValue =
+        _optionKeys.containsKey(effectiveValue) ? effectiveValue : defaultValue;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shared/widgets/trade_history_selector.dart` around lines 28 - 29, The
dropdown fallback currently hardcodes 720 in the assignment to displayValue
which can drift from Config.sessionExpirationHours; update the logic that
computes displayValue (the variable named displayValue in
trade_history_selector.dart) to derive the default from
Config.sessionExpirationHours and only use it if that value exists in
_optionKeys (otherwise fall back to a safe existing key from _optionKeys), i.e.
replace the literal 720 with a config-derived default and a membership guard
against _optionKeys so the shown value always matches available options.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/shared/widgets/trade_history_selector.dart`:
- Around line 28-29: The dropdown fallback currently hardcodes 720 in the
assignment to displayValue which can drift from Config.sessionExpirationHours;
update the logic that computes displayValue (the variable named displayValue in
trade_history_selector.dart) to derive the default from
Config.sessionExpirationHours and only use it if that value exists in
_optionKeys (otherwise fall back to a safe existing key from _optionKeys), i.e.
replace the literal 720 with a config-derived default and a membership guard
against _optionKeys so the shown value always matches available options.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0660c515-d0e1-4571-bb9d-51d18803cf1a

📥 Commits

Reviewing files that changed from the base of the PR and between 31428c5 and 63126ab.

📒 Files selected for processing (2)
  • lib/shared/widgets/trade_history_selector.dart
  • test/features/settings/session_expiration_test.dart

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/shared/notifiers/session_notifier.dart`:
- Around line 57-77: The _cleanupSessionData method currently force-unwraps
Session.orderId which can be null; update _cleanupSessionData to guard early
(e.g., if session.orderId == null) and return without performing cascade deletes
when orderId is null, so eventStore.deleteWhere,
mostroStore.deleteAllMessagesByOrderId, notificationsStore.deleteWhere and
SharedPreferences.remove are only called with a non-null orderId; keep
references to the same symbols (_cleanupSessionData, Session.orderId,
eventStore, mostroStore, notificationsStore, prefs) so the change is localized
and safe.
- Around line 125-129: In _cleanup() the code force-unwraps session.orderId when
calling _storage.deleteSession and _sessions.remove; change to first capture
final orderId = session.orderId and if orderId == null continue (skip this
session) before calling _storage.deleteSession(orderId) and
_sessions.remove(orderId), preserving the call to _cleanupSessionData(session)
only if you intend to run it for sessions with a valid orderId (or similarly
guard that call), so replace direct uses of session.orderId! with a null-checked
local variable to avoid force-unwrapping.
- Around line 79-97: In init(), avoid force-unwrapping session.orderId; before
using it in both branches filter or guard sessions with null orderId (e.g., skip
if session.orderId == null) and only then assign to _sessions[session.orderId!]
or call _storage.deleteSession(session.orderId!) and
_cleanupSessionData(session); ensure the _cleanupSessionData call is also
executed only when session.orderId is non-null so all occurrences in init() (the
loops and deletions) are null-safe and consistent with other guards in the
class.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0725e2a2-c206-4812-826e-e41d29875fcf

📥 Commits

Reviewing files that changed from the base of the PR and between 63126ab and 8c9537d.

📒 Files selected for processing (4)
  • docs/architecture/APP_INITIALIZATION_ANALYSIS.md
  • docs/architecture/SESSION_AND_KEY_MANAGEMENT.md
  • docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md
  • lib/shared/notifiers/session_notifier.dart

Comment thread lib/shared/notifiers/session_notifier.dart
Comment thread lib/shared/notifiers/session_notifier.dart Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
lib/shared/notifiers/session_notifier.dart (2)

129-134: ⚠️ Potential issue | 🟡 Minor

Same null-safety concern in _cleanup().

Lines 131 and 132 force-unwrap session.orderId! without null guards, consistent with the unresolved concern in init().

🛡️ Proposed fix
   for (final session in expiredSessions) {
+    if (session.orderId == null) continue;
     if (session.startTime.isBefore(cutoff)) {
       await _storage.deleteSession(session.orderId!);
       _sessions.remove(session.orderId!);
       await _cleanupSessionData(session);
     }
   }

,

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shared/notifiers/session_notifier.dart` around lines 129 - 134, The loop
in _cleanup() force-unwraps session.orderId (used in _storage.deleteSession and
_sessions.remove and passed to _cleanupSessionData) which can be null; guard
against this by capturing session.orderId into a local (e.g., orderId), check
for null and skip that session (or handle it explicitly) before calling
_storage.deleteSession(orderId), _sessions.remove(orderId), and
_cleanupSessionData(session), mirroring the null-safety fix you applied in
init().

85-100: ⚠️ Potential issue | 🟡 Minor

Null-safety guards still missing in init().

Lines 87, 94, 96, and 97 force-unwrap session.orderId! without first checking for null. While _cleanupSessionData now guards against null, the init() loops do not, which can cause runtime exceptions if a malformed session exists in storage.

🛡️ Proposed defensive approach
   if (_isForever) {
     for (final session in allSessions) {
+      if (session.orderId == null) continue;
       _sessions[session.orderId!] = session;
     }
   } else {
     final cutoff = DateTime.now()
         .subtract(Duration(hours: _expirationHours));
     for (final session in allSessions) {
+      if (session.orderId == null) continue;
       if (session.startTime.isAfter(cutoff)) {
         _sessions[session.orderId!] = session;
       } else {
         await _storage.deleteSession(session.orderId!);
         _sessions.remove(session.orderId!);
         await _cleanupSessionData(session);
       }
     }
   }

,

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shared/notifiers/session_notifier.dart` around lines 85 - 100, The init()
loops currently force-unwrap session.orderId! (in the _isForever branch and the
else branch) which can throw for malformed sessions; update both loops in init()
to null-check session.orderId before using it (skip entries where orderId is
null), only assign to _sessions[orderId] and call
_storage.deleteSession(orderId) / _cleanupSessionData(session) when orderId !=
null, and ensure you do not assume orderId downstream when interacting with the
_sessions map or calling _storage.deleteSession and _cleanupSessionData.
🧹 Nitpick comments (2)
lib/shared/widgets/trade_history_selector.dart (1)

8-83: Well-structured widget with proper value resolution and localization.

The fallback logic correctly handles null preferences, invalid stored values, and uses S.of(context)! for all user-facing strings. One minor optimization:

The Icon widget (lines 56-60) has all constant parameters and could be marked const:

♻️ Suggested improvement
-                  Icon(
-                    Icons.schedule,
-                    color: AppTheme.mostroGreen,
-                    size: 20,
-                  ),
+                  const Icon(
+                    Icons.schedule,
+                    color: AppTheme.mostroGreen,
+                    size: 20,
+                  ),

,

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shared/widgets/trade_history_selector.dart` around lines 8 - 83, Summary:
Make the Icon in TradeHistorySelector build constant to improve performance.
Change the non-const Icon(...) instance inside the DropdownMenuItem builder (the
Icon using Icons.schedule, AppTheme.mostroGreen, size: 20) to a const Icon so
the widget becomes compile-time constant; ensure the surrounding Row children
that include const SizedBox and Text remain valid (you may need to adjust const
placement on the Text if localization prevents const). Update the Icon
instantiation in the _optionKeys.map builder accordingly.
lib/shared/notifiers/session_notifier.dart (1)

63-66: Type cast bypasses the repository abstraction.

The cast as NotificationsStorage couples this code to the concrete implementation rather than the NotificationsRepository interface. If the provider's backing implementation changes, this will fail at runtime.

Consider adding deleteWhere to the NotificationsRepository interface, or exposing a dedicated method like deleteByOrderId(String orderId) that encapsulates this logic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shared/notifiers/session_notifier.dart` around lines 63 - 66, The code in
session_notifier.dart reads notificationsRepositoryProvider and force-casts it
to NotificationsStorage (notificationsStore = ref.read(... ) as
NotificationsStorage), which breaks the NotificationsRepository abstraction and
can fail if implementation changes; update the NotificationsRepository interface
to include the needed deletion operation (e.g., add deleteWhere or
deleteByOrderId(String orderId)) and replace the cast with the interface type
(NotificationsRepository notificationsRepo) then call the new repository method
(e.g., notificationsRepo.deleteByOrderId(orderId) or
notificationsRepo.deleteWhere(predicate)) instead of relying on the concrete
NotificationsStorage methods.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@lib/shared/notifiers/session_notifier.dart`:
- Around line 129-134: The loop in _cleanup() force-unwraps session.orderId
(used in _storage.deleteSession and _sessions.remove and passed to
_cleanupSessionData) which can be null; guard against this by capturing
session.orderId into a local (e.g., orderId), check for null and skip that
session (or handle it explicitly) before calling
_storage.deleteSession(orderId), _sessions.remove(orderId), and
_cleanupSessionData(session), mirroring the null-safety fix you applied in
init().
- Around line 85-100: The init() loops currently force-unwrap session.orderId!
(in the _isForever branch and the else branch) which can throw for malformed
sessions; update both loops in init() to null-check session.orderId before using
it (skip entries where orderId is null), only assign to _sessions[orderId] and
call _storage.deleteSession(orderId) / _cleanupSessionData(session) when orderId
!= null, and ensure you do not assume orderId downstream when interacting with
the _sessions map or calling _storage.deleteSession and _cleanupSessionData.

---

Nitpick comments:
In `@lib/shared/notifiers/session_notifier.dart`:
- Around line 63-66: The code in session_notifier.dart reads
notificationsRepositoryProvider and force-casts it to NotificationsStorage
(notificationsStore = ref.read(... ) as NotificationsStorage), which breaks the
NotificationsRepository abstraction and can fail if implementation changes;
update the NotificationsRepository interface to include the needed deletion
operation (e.g., add deleteWhere or deleteByOrderId(String orderId)) and replace
the cast with the interface type (NotificationsRepository notificationsRepo)
then call the new repository method (e.g.,
notificationsRepo.deleteByOrderId(orderId) or
notificationsRepo.deleteWhere(predicate)) instead of relying on the concrete
NotificationsStorage methods.

In `@lib/shared/widgets/trade_history_selector.dart`:
- Around line 8-83: Summary: Make the Icon in TradeHistorySelector build
constant to improve performance. Change the non-const Icon(...) instance inside
the DropdownMenuItem builder (the Icon using Icons.schedule,
AppTheme.mostroGreen, size: 20) to a const Icon so the widget becomes
compile-time constant; ensure the surrounding Row children that include const
SizedBox and Text remain valid (you may need to adjust const placement on the
Text if localization prevents const). Update the Icon instantiation in the
_optionKeys.map builder accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5572a42e-145f-431d-99f1-2979c85490d8

📥 Commits

Reviewing files that changed from the base of the PR and between 8c9537d and 59b1e72.

📒 Files selected for processing (2)
  • lib/shared/notifiers/session_notifier.dart
  • lib/shared/widgets/trade_history_selector.dart

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/shared/notifiers/session_notifier.dart (1)

142-144: ⚠️ Potential issue | 🟡 Minor

Apply retention changes immediately.

updateSettings() only swaps _settings. If the user changes from never or a longer preset to a shorter one, already-expired sessions stay in _sessions and in the emitted state until the next periodic cleanup tick, so the new setting does not take effect right away.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shared/notifiers/session_notifier.dart` around lines 142 - 144,
updateSettings currently only replaces _settings leaving already-expired
sessions in _sessions until the next periodic cleanup; after assigning _settings
= settings.copyWith() immediately apply the new retention by removing/filtering
expired sessions from _sessions (or call the existing cleanup method if one
exists), then emit the updated state so the change takes effect immediately
(i.e., updateSettings should update _settings, prune _sessions according to the
new settings, and emit the new state).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/shared/notifiers/session_notifier.dart`:
- Around line 56-79: Make the session cleanup best-effort by catching and
logging errors so a single failure can't abort the notifier: wrap each storage
operation in _cleanupSessionData (eventStore.deleteWhere,
mostroStore.deleteAllMessagesByOrderId, notificationsRepo.deleteByOrderId,
SharedPreferences.remove calls) in try/catch blocks (or wrap the whole
per-session sequence in a try/catch) and log failures using the pre-configured
singleton logger from package:mostro_mobile/services/logger_service.dart instead
of creating a new Logger; apply the same pattern to the other cleanup call sites
(init and _cleanup) so individual errors are logged and swallowed while allowing
remaining sessions to proceed.

---

Outside diff comments:
In `@lib/shared/notifiers/session_notifier.dart`:
- Around line 142-144: updateSettings currently only replaces _settings leaving
already-expired sessions in _sessions until the next periodic cleanup; after
assigning _settings = settings.copyWith() immediately apply the new retention by
removing/filtering expired sessions from _sessions (or call the existing cleanup
method if one exists), then emit the updated state so the change takes effect
immediately (i.e., updateSettings should update _settings, prune _sessions
according to the new settings, and emit the new state).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 24011443-b30a-4584-a4e8-236537851317

📥 Commits

Reviewing files that changed from the base of the PR and between 59b1e72 and 79b8aca.

📒 Files selected for processing (3)
  • lib/data/repositories/notifications_history_repository.dart
  • lib/shared/notifiers/session_notifier.dart
  • lib/shared/widgets/trade_history_selector.dart

Comment thread lib/shared/notifiers/session_notifier.dart
@Catrya

Catrya commented Apr 9, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
lib/shared/notifiers/session_notifier.dart (2)

131-140: ⚠️ Potential issue | 🟡 Minor

Same null-safety concern in _cleanup().

Line 133 and 134 force-unwrap session.orderId! without a null check. Apply the same guard pattern here for consistency with the init() fix.

🛡️ Proposed fix
   for (final session in expiredSessions) {
+    if (session.orderId == null) continue;
     if (session.startTime.isBefore(cutoff)) {
       await _storage.deleteSession(session.orderId!);
       _sessions.remove(session.orderId!);
       try {
         await _cleanupSessionData(session);
       } catch (e) {
         logger.e('Failed to cleanup data for session ${session.orderId}: $e');
       }
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shared/notifiers/session_notifier.dart` around lines 131 - 140, In
_cleanup() avoid force-unwrapping session.orderId; mirror the guard used in
init(): check if session.orderId is null before calling _storage.deleteSession,
_sessions.remove and _cleanupSessionData (e.g., if (session.orderId == null)
continue or log and continue), and only use the non-null orderId afterwards to
prevent null-safety violations when invoking _storage.deleteSession,
_sessions.remove, and _cleanupSessionData for that session.

83-102: ⚠️ Potential issue | 🟡 Minor

Apply null-safety guard for session.orderId in init() loops.

Lines 85, 92, 94, and 95 force-unwrap session.orderId! without first checking for null. If a session has a null orderId, this will throw a runtime exception before the guarded _cleanupSessionData is reached. Add a null check at the start of each loop iteration.

🛡️ Proposed defensive approach
 Future<void> init() async {
   final allSessions = await _storage.getAllSessions();
   if (_isForever) {
     for (final session in allSessions) {
+      if (session.orderId == null) continue;
       _sessions[session.orderId!] = session;
     }
   } else {
     final cutoff = DateTime.now()
         .subtract(Duration(hours: _expirationHours));
     for (final session in allSessions) {
+      if (session.orderId == null) continue;
       if (session.startTime.isAfter(cutoff)) {
         _sessions[session.orderId!] = session;
       } else {
         await _storage.deleteSession(session.orderId!);
         _sessions.remove(session.orderId!);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shared/notifiers/session_notifier.dart` around lines 83 - 102, The init()
loop uses session.orderId! in multiple places and can throw if orderId is null;
add a null-safety guard at the start of each iteration (inside both branches
that iterate allSessions) by reading final id = session.orderId and skipping the
iteration if id == null (continue), then use id instead of session.orderId! when
assigning to _sessions, calling _storage.deleteSession(id), and in the
logger/_cleanupSessionData calls; this ensures _cleanupSessionData and the
delete path only run for sessions with a non-null orderId.
🧹 Nitpick comments (1)
lib/shared/notifiers/session_notifier.dart (1)

56-79: Consider per-operation error handling for resilience.

While the callers wrap _cleanupSessionData in try/catch, a failure in any individual operation (e.g., eventStore.deleteWhere) will prevent subsequent cleanup steps from executing. For best-effort cleanup, wrapping each storage operation individually would allow partial success.

That said, the current approach does log failures and continue processing other sessions, which may be acceptable if cleanup failures are rare.

♻️ Optional: per-operation try/catch for maximum resilience
 Future<void> _cleanupSessionData(Session session) async {
   final orderId = session.orderId;
   if (orderId == null) {
     logger.w('Skipping cleanup for session with null orderId');
     return;
   }
   final eventStore = ref.read(eventStorageProvider);
   final mostroStore = ref.read(mostroStorageProvider);
   final notificationsRepo = ref.read(notificationsRepositoryProvider);

-  await eventStore.deleteWhere(Filter.equals('order_id', orderId));
+  try {
+    await eventStore.deleteWhere(Filter.equals('order_id', orderId));
+  } catch (e) {
+    logger.e('Failed to delete events for order $orderId: $e');
+  }
   if (session.disputeId != null) {
-    await eventStore.deleteWhere(
-        Filter.equals('dispute_id', session.disputeId));
+    try {
+      await eventStore.deleteWhere(
+          Filter.equals('dispute_id', session.disputeId));
+    } catch (e) {
+      logger.e('Failed to delete dispute events for ${session.disputeId}: $e');
+    }
   }
-  await mostroStore.deleteAllMessagesByOrderId(orderId);
-  await notificationsRepo.deleteByOrderId(orderId);
+  try {
+    await mostroStore.deleteAllMessagesByOrderId(orderId);
+  } catch (e) {
+    logger.e('Failed to delete mostro messages for order $orderId: $e');
+  }
+  try {
+    await notificationsRepo.deleteByOrderId(orderId);
+  } catch (e) {
+    logger.e('Failed to delete notifications for order $orderId: $e');
+  }

   final prefs = await SharedPreferences.getInstance();
-  await prefs.remove('chat_last_read_$orderId');
+  try {
+    await prefs.remove('chat_last_read_$orderId');
+  } catch (e) {
+    logger.e('Failed to remove chat_last_read pref for order $orderId: $e');
+  }
   if (session.disputeId != null) {
-    await prefs.remove('dispute_last_read_${session.disputeId}');
+    try {
+      await prefs.remove('dispute_last_read_${session.disputeId}');
+    } catch (e) {
+      logger.e('Failed to remove dispute_last_read pref: $e');
+    }
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shared/notifiers/session_notifier.dart` around lines 56 - 79, The
_cleanupSessionData function performs multiple storage/removal operations
atomically so a failure in one (e.g., eventStore.deleteWhere,
mostroStore.deleteAllMessagesByOrderId, notificationsRepo.deleteByOrderId,
prefs.remove) aborts the rest; change it to perform each operation in its own
try/catch so failures are logged per-operation but do not prevent subsequent
cleanup steps, i.e., wrap each call (including both eventStore.deleteWhere
calls, mostroStore.deleteAllMessagesByOrderId,
notificationsRepo.deleteByOrderId, and each prefs.remove) in small try/catch
blocks that log the error via logger.e/w and continue.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@lib/shared/notifiers/session_notifier.dart`:
- Around line 131-140: In _cleanup() avoid force-unwrapping session.orderId;
mirror the guard used in init(): check if session.orderId is null before calling
_storage.deleteSession, _sessions.remove and _cleanupSessionData (e.g., if
(session.orderId == null) continue or log and continue), and only use the
non-null orderId afterwards to prevent null-safety violations when invoking
_storage.deleteSession, _sessions.remove, and _cleanupSessionData for that
session.
- Around line 83-102: The init() loop uses session.orderId! in multiple places
and can throw if orderId is null; add a null-safety guard at the start of each
iteration (inside both branches that iterate allSessions) by reading final id =
session.orderId and skipping the iteration if id == null (continue), then use id
instead of session.orderId! when assigning to _sessions, calling
_storage.deleteSession(id), and in the logger/_cleanupSessionData calls; this
ensures _cleanupSessionData and the delete path only run for sessions with a
non-null orderId.

---

Nitpick comments:
In `@lib/shared/notifiers/session_notifier.dart`:
- Around line 56-79: The _cleanupSessionData function performs multiple
storage/removal operations atomically so a failure in one (e.g.,
eventStore.deleteWhere, mostroStore.deleteAllMessagesByOrderId,
notificationsRepo.deleteByOrderId, prefs.remove) aborts the rest; change it to
perform each operation in its own try/catch so failures are logged per-operation
but do not prevent subsequent cleanup steps, i.e., wrap each call (including
both eventStore.deleteWhere calls, mostroStore.deleteAllMessagesByOrderId,
notificationsRepo.deleteByOrderId, and each prefs.remove) in small try/catch
blocks that log the error via logger.e/w and continue.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: afd6ae56-c897-4568-8606-ed15ee79ae48

📥 Commits

Reviewing files that changed from the base of the PR and between 59b1e72 and e892c8a.

📒 Files selected for processing (3)
  • lib/data/repositories/notifications_history_repository.dart
  • lib/shared/notifiers/session_notifier.dart
  • lib/shared/widgets/trade_history_selector.dart

mostronatorcoder[bot]
mostronatorcoder Bot previously approved these changes Apr 9, 2026

@mostronatorcoder mostronatorcoder Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

16-file change implementing configurable trade history retention. Well-scoped and thoroughly tested.

Settings / SettingsNotifier

sessionExpirationHours added to Settings model with full serialization support. copyWith has clearSessionExpiration flag for explicit null reset. updateSessionExpirationHours persists and logs the change. Correct.

SessionNotifier

_expirationHours getter reads from user settings with Config fallback. _isForever cleanly handles the "never" mode (value 0). Cascade cleanup in both init() and _cleanup() — when a session expires, it now deletes associated data from eventStore, mostroStorage, notificationsRepository, and SharedPreferences. Each cleanup step has try/catch with logger.e() so failures are logged but do not propagate. _isForever guard in _cleanup() skips the timer entirely — correct, no wasted work.

TradeHistorySelector

Dropdown with 6 presets (1w, 1m, 3m, 6m, 1y, never), all labels via ARB localization. Smart fallback when stored value does not match any preset — falls back to Config default. Uses the widget pattern consistently with the rest of the settings screen.

app_init_provider

Reads sessionExpirationHours from settings at startup. "Never" mode correctly bypasses cutoff filtering (cutoff = null).

notifications_history_repository

New deleteByOrderId method enables cascade deletion from the notifications store.

Docs

TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md table documenting what gets cleaned up and what does NOT (and why — dedup stubs in eventStore are intentionally retained to prevent duplicate event processing). This is important context that belongs in the docs.

Tests

11 tests covering: defaults, Config fallback, copyWith roundtrip, clear flag, "never" mode (value 0), full serialization roundtrip, null handling, backward compat with old JSON, all preset values. Good coverage.

CodeRabbit findings (3)

  1. Hardcoded 720 in TradeHistorySelector — partially valid. _optionKeys hardcodes the preset that matches Config.sessionExpirationHours. Could use the Config constant as the map key source, but functionally correct.

  2. No validation of invalid values in settings_notifier — mitigated by design. TradeHistorySelector only passes values from _optionKeys; no other public call site exists. Defensive validation is optional.

  3. session.orderId! force-unwrap in _cleanup() — consistent with the same pattern already used in init() and throughout the codebase. Not a bug.

CI

Flutter checks: all passing.

LGTM. Complete, well-tested, good docs.

@AndreaDiazCorreia AndreaDiazCorreia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Catrya, I've been testing this branch and wanted to share a few things.

One thing that happened to me: when I started the app on this branch, it deleted my previous session right away, and I don't think that should happen.

Another thing, it seems like it's not checking whether there are active trades, and I think it would be a good idea, just as a precaution, to evaluate that before deleting anything.

And one more, about UX: the text that's currently in the info message of the widget should actually be shown as a confirmation dialog before applying the change, so the user confirms they understand what this change means.

Catrya added 6 commits April 14, 2026 13:46
  - Add session expiration selector with 6 presets: 1 week, 1 month, 3 months, 6 months, 1 year, never
  - Default remains 1 month (720 hours), stored in Settings and persisted via SharedPreferences
  - SessionNotifier reads expiration from user settings with Config fallback
  - Value 0 means never delete, protected by explicit guards in init() and cleanup()
  - New Trade History card in settings screen between Notifications and Dev Tools
  - Localization keys added for EN, ES, IT, FR, DE
  - 11 unit tests covering serialization, presets, backward compatibility, and never mode
  - Includes log output when user changes the setting
  - When a session expires, now also deletes: chat events, dispute chat events, mostro messages, notifications, and
  read status keys
  - Extracted shared cleanup logic into _cleanupSessionData() used by both init() and periodic cleanup
  - MostroService dedup stubs intentionally retained to prevent duplicate event processing
  - Updated architecture docs to reflect cascading cleanup and user-configurable retention
…feedback

  - Add null orderId guard with logging in _cleanupSessionData()
  - Replace hardcoded 720 with Config.sessionExpirationHours in trade history selector
…onst icon

  - Add deleteByOrderId() to NotificationsRepository interface instead of casting to concrete implementation
  - Mark Icon widget as const in trade history selector
…cking others

  - Wrap _cleanupSessionData calls in try/catch in both init() and _cleanup()
  - Errors are logged but don't abort remaining session cleanup
@Catrya Catrya force-pushed the settings-cleanup-orders branch from e892c8a to 280a0be Compare April 14, 2026 19:48
@Catrya

Catrya commented Apr 14, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
lib/shared/notifiers/session_notifier.dart (2)

143-167: ⚠️ Potential issue | 🟠 Major

Don’t tie _pendingChildSessions to the history-retention window.

These entries are temporary child-order placeholders, not retained trade history. Using _expirationHours here keeps dead trade keys in state for a week/month/year, and Line 143 skips their cleanup forever in “never” mode. Give them a dedicated short TTL and prune them before the _isForever early return.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shared/notifiers/session_notifier.dart` around lines 143 - 167, The
cleanup currently ties temporary child-order placeholders
(_pendingChildSessions) to the long-term retention window (_expirationHours) and
is skipped when _isForever is true; move pruning of _pendingChildSessions to run
before the early return and use a dedicated short TTL (e.g.,
_pendingChildTTLHours or similar) instead of _expirationHours, and remove
entries whose session.startTime is older than
DateTime.now().subtract(Duration(hours: _pendingChildTTLHours)); update any
references to _pendingChildSessions removal logic in this file (and add the new
TTL constant/field) so child placeholders are pruned independently of _isForever
and the trade history retention window.

136-170: ⚠️ Potential issue | 🟠 Major

Change _cleanup() to return Future<void> and add top-level error handling.

Line 148 declares void _cleanup() async, which means async failures from _storage.getAllSessions(), _isActiveSession(), or _storage.deleteSession() will escape as uncaught errors instead of being logged. This allows cleanup to fail silently, causing orphan sessions to accumulate.

Change the return type to Future<void> and wrap the method body in a try-catch to log cleanup failures:

Suggested fix
  void _scheduleCleanup() {
    _cleanupTimer?.cancel();
    _cleanupTimer = Timer.periodic(
      const Duration(minutes: Config.cleanupIntervalMinutes),
-     (timer) => _cleanup(),
+     (_) {
+       unawaited(_cleanup());
+     },
    );
  }

-  void _cleanup() async {
-    if (_isForever) return;
-
-    final cutoff = DateTime.now()
-        .subtract(Duration(hours: _expirationHours));
-    final expiredSessions = await _storage.getAllSessions();
-
-    for (final session in expiredSessions) {
-      if (session.startTime.isBefore(cutoff)) {
-        if (await _isActiveSession(session)) {
-          logger.i('Skipping cleanup for active session ${session.orderId}');
-          continue;
-        }
-        await _storage.deleteSession(session.orderId!);
-        _sessions.remove(session.orderId!);
-        try {
-          await _cleanupSessionData(session);
-        } catch (e) {
-          logger.e('Failed to cleanup data for session ${session.orderId}: $e');
-        }
-      }
-    }
-
-    _pendingChildSessions.removeWhere(
-      (_, session) => session.startTime.isBefore(cutoff),
-    );
-
-    _emitState();
+  Future<void> _cleanup() async {
+    try {
+      if (_isForever) return;
+
+      final cutoff = DateTime.now()
+          .subtract(Duration(hours: _expirationHours));
+      final expiredSessions = await _storage.getAllSessions();
+
+      for (final session in expiredSessions) {
+        if (session.startTime.isBefore(cutoff)) {
+          if (await _isActiveSession(session)) {
+            logger.i('Skipping cleanup for active session ${session.orderId}');
+            continue;
+          }
+          await _storage.deleteSession(session.orderId!);
+          _sessions.remove(session.orderId!);
+          try {
+            await _cleanupSessionData(session);
+          } catch (e) {
+            logger.e('Failed to cleanup data for session ${session.orderId}: $e');
+          }
+        }
+      }
+
+      _pendingChildSessions.removeWhere(
+        (_, session) => session.startTime.isBefore(cutoff),
+      );
+
+      _emitState();
+    } catch (e) {
+      logger.e('Periodic session cleanup failed: $e');
+    }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shared/notifiers/session_notifier.dart` around lines 136 - 170, The
_cleanup method should return Future<void> and add top-level error handling:
change the signature from "void _cleanup() async" to "Future<void> _cleanup()
async" and wrap the entire body in a try-catch that logs failures via logger.e
(so failures from _storage.getAllSessions(), _isActiveSession(),
_storage.deleteSession(), or _cleanupSessionData() are caught and logged); keep
existing per-session try-catch for _cleanupSessionData() but ensure any
unexpected errors in the whole cleanup flow are caught, then still call
_emitState() in a finally block so state is always emitted and cleanup of
_pendingChildSessions continues.
🧹 Nitpick comments (1)
lib/shared/notifiers/session_notifier.dart (1)

14-14: Use the model barrel for the new Order dependency.

This adds another direct model-file import in a codepath that should be coming through the barrel instead. Please pull Order from package:mostro_mobile/data/models.dart here.

♻️ Minimal import change
-import 'package:mostro_mobile/data/models/order.dart';
+import 'package:mostro_mobile/data/models.dart' show Order;

As per coding guidelines **/{lib,test}/**/*.dart: Import models through data/models.dart instead of importing individual model files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shared/notifiers/session_notifier.dart` at line 14, The file
session_notifier.dart currently imports the Order model directly; replace the
direct import of package:mostro_mobile/data/models/order.dart with the model
barrel package:mostro_mobile/data/models.dart so Order is resolved via the
shared models barrel (keep other imports intact and ensure no name collisions
with existing symbols in SessionNotifier).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@lib/shared/notifiers/session_notifier.dart`:
- Around line 143-167: The cleanup currently ties temporary child-order
placeholders (_pendingChildSessions) to the long-term retention window
(_expirationHours) and is skipped when _isForever is true; move pruning of
_pendingChildSessions to run before the early return and use a dedicated short
TTL (e.g., _pendingChildTTLHours or similar) instead of _expirationHours, and
remove entries whose session.startTime is older than
DateTime.now().subtract(Duration(hours: _pendingChildTTLHours)); update any
references to _pendingChildSessions removal logic in this file (and add the new
TTL constant/field) so child placeholders are pruned independently of _isForever
and the trade history retention window.
- Around line 136-170: The _cleanup method should return Future<void> and add
top-level error handling: change the signature from "void _cleanup() async" to
"Future<void> _cleanup() async" and wrap the entire body in a try-catch that
logs failures via logger.e (so failures from _storage.getAllSessions(),
_isActiveSession(), _storage.deleteSession(), or _cleanupSessionData() are
caught and logged); keep existing per-session try-catch for
_cleanupSessionData() but ensure any unexpected errors in the whole cleanup flow
are caught, then still call _emitState() in a finally block so state is always
emitted and cleanup of _pendingChildSessions continues.

---

Nitpick comments:
In `@lib/shared/notifiers/session_notifier.dart`:
- Line 14: The file session_notifier.dart currently imports the Order model
directly; replace the direct import of
package:mostro_mobile/data/models/order.dart with the model barrel
package:mostro_mobile/data/models.dart so Order is resolved via the shared
models barrel (keep other imports intact and ensure no name collisions with
existing symbols in SessionNotifier).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 588f207b-51af-4c0c-a4a1-8f1aeb659913

📥 Commits

Reviewing files that changed from the base of the PR and between e892c8a and ffcd399.

📒 Files selected for processing (17)
  • docs/architecture/APP_INITIALIZATION_ANALYSIS.md
  • docs/architecture/SESSION_AND_KEY_MANAGEMENT.md
  • docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md
  • lib/data/models/enums/status.dart
  • lib/data/repositories/notifications_history_repository.dart
  • lib/features/settings/settings.dart
  • lib/features/settings/settings_notifier.dart
  • lib/features/settings/settings_screen.dart
  • lib/l10n/intl_de.arb
  • lib/l10n/intl_en.arb
  • lib/l10n/intl_es.arb
  • lib/l10n/intl_fr.arb
  • lib/l10n/intl_it.arb
  • lib/shared/notifiers/session_notifier.dart
  • lib/shared/providers/app_init_provider.dart
  • lib/shared/widgets/trade_history_selector.dart
  • test/features/settings/session_expiration_test.dart
✅ Files skipped from review due to trivial changes (3)
  • docs/architecture/APP_INITIALIZATION_ANALYSIS.md
  • lib/l10n/intl_es.arb
  • lib/l10n/intl_en.arb
🚧 Files skipped from review as they are similar to previous changes (7)
  • lib/features/settings/settings_screen.dart
  • lib/shared/providers/app_init_provider.dart
  • lib/l10n/intl_it.arb
  • lib/l10n/intl_de.arb
  • lib/shared/widgets/trade_history_selector.dart
  • lib/l10n/intl_fr.arb
  • docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md

@AndreaDiazCorreia AndreaDiazCorreia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tACK

@grunch grunch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tACK

@grunch grunch merged commit 8116311 into main Apr 17, 2026
2 checks passed
@grunch grunch deleted the settings-cleanup-orders branch April 17, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants