Skip to content

Feature/export user data - #13

Open
aneesafatima wants to merge 3 commits into
AOSSIE-Org:mainfrom
aneesafatima:feature/export-user-data
Open

Feature/export user data#13
aneesafatima wants to merge 3 commits into
AOSSIE-Org:mainfrom
aneesafatima:feature/export-user-data

Conversation

@aneesafatima

@aneesafatima aneesafatima commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Week 10

Screenshots/Recordings:

iOS

WhatsApp.Video.2026-08-04.at.01.57.12.mp4

Saved File

WhatsApp Image 2026-08-04 at 01 57 12

Additional Notes:

  • Added export data feature to the profile screen. Users can now export their trip history as JSON and share it using share_plus.
  • Added a sustainability related message to the carbon tracker screen
  • Added a unit test for CarbonCalculator emissions.
  • Applied CodeRabbit's review suggestions from the previous commit.

Checklist

  • My code follows the project's code style and conventions
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contributing Guidelines

⚠️ AI Notice - Important!

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact.

Summary by CodeRabbit

  • New Features

    • Added a profile screen for editing personal details, transport and tracking preferences, and sustainability information.
    • Added trip-history deletion, data export and sharing, sign-out navigation, and monthly trip reset handling.
    • Added carbon-impact information and improved location-permission feedback.
    • Added reusable loading, selection, and profile-section components.
  • Bug Fixes

    • Improved user-state synchronization and corrected completed/cancelled trip actions.
    • Carbon savings now never display negative or zero-inconsistent values.
  • Tests

    • Added coverage for emissions and carbon-savings calculations.

@github-actions github-actions Bot added no-issue-linked PR is not linked to any issue configuration Configuration file changes tests Test file changes size/XL Extra large PR (>500 lines changed) repeat-contributor PR from an external contributor who already had PRs merged labels Aug 3, 2026
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The app adds a Riverpod-backed profile screen, shared transport and tracking models, trip export and deletion, monthly trip resets, structured location permission handling, carbon information content, and supporting widgets, dependencies, and tests.

Changes

Application feature updates

Layer / File(s) Summary
Core contracts and provider state
lib/core/..., lib/database/...
Adds shared tracking and transport options, reusable widgets, color constants, User.copyWith, provider state synchronization, trip deletion, and modal styling updates.
Onboarding tracking integration
lib/features/onboarding/..., lib/features/fitness/...
Moves onboarding data to core providers and option definitions. Tracking selections now use TrackingOption values.
Profile management and data export
lib/features/profile/..., pubspec.yaml
Replaces the placeholder profile screen with editable preferences, trip deletion, sign-out, JSON export, sharing, and supporting widgets.
Map permission and trip actions
lib/features/map/...
Returns structured location permission results, displays service messages, corrects trip modal callbacks, and updates trip loading state.
Carbon flow and startup reset
lib/features/carbon/..., lib/main.dart, test/carbon_calculator_test.dart, l10n.yaml
Adds carbon information content, clamps zero savings, adds calculator tests, performs monthly trip resets at startup, and removes the synthetic localization package setting.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ProfileScreen
  participant UserNotifier
  participant DatabaseHelper
  participant ExportDataService
  participant TemporaryDirectory
  participant SharePlus
  User->>ProfileScreen: edit and save profile
  ProfileScreen->>UserNotifier: updateUser
  UserNotifier->>DatabaseHelper: persist user
  User->>ProfileScreen: request data export
  ProfileScreen->>ExportDataService: shareFile
  ExportDataService->>DatabaseHelper: load trips
  ExportDataService->>TemporaryDirectory: write JSON file
  ExportDataService->>SharePlus: share exported file
Loading

Possibly related PRs

Poem

A rabbit checks the profile with care,
Saves green choices everywhere.
Trips are cleared when months renew,
Carbon notes and exports queue.
“Hop,” says the rabbit, “the app is bright!”

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: adding a feature to export user data.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 14

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/core/providers/trips_provider.dart`:
- Around line 30-37: Update deleteTrips so its catch block logs a
deletion-specific error message and rethrows the caught error after logging,
ensuring clearTrips failures propagate to callers instead of completing
normally.

In `@lib/features/carbon/data/carbon_modal_data.dart`:
- Around line 1-2: Declare carbonModalData and carbonModalTitle as const String
values, preserving their existing literal content while preventing runtime
mutation of the shared modal data.

In `@lib/features/carbon/screens/carbon_tracker.dart`:
- Around line 70-75: Add an accessible, localized label to the IconButton in the
carbon tracker screen, indicating that it opens the carbon recommendations
information. Reuse the existing localization mechanism and keep the current
showInfoModal behavior unchanged.

In `@lib/features/map/services/location_service.dart`:
- Around line 15-38: Update isPermissionGranted so
LocationPermission.deniedForever returns the same structured map contract as
other failed permission checks, including status: false and an appropriate
message, instead of propagating Future.error. Preserve the existing successful
and denied permission handling paths.

In `@lib/features/map/widgets/map_modal.dart`:
- Line 110: Update showMapModal and its onClose/onCompleted callback types to
Future<void> Function(), await each asynchronous action before dismissing the
dialog, and handle failures so cancellation errors are not unhandled. Ensure the
Cancel Trip dialog remains open until the callback completes successfully, then
invoke the existing dismissal flow.

In `@lib/features/profile/screens/profile.dart`:
- Around line 250-277: Update _weightRow and _sustainabilityThoughtsRow so each
edit affordance either requests focus on its adjacent TextField or is removed if
direct editing is sufficient. Also adjust the _weightController initialization
to display whole-number weights without a trailing “.0”, while preserving
decimal values when needed.
- Around line 62-70: Update onTrackingSelection to remove the unnecessary nested
block and read userProvider into a local variable; only call updateUser when
that user is non-null, passing the local user’s copyWith result instead of
force-unwrapping ref.read(userProvider).
- Around line 366-395: Update the Export Data and Clear Stored Trips handlers in
the profile screen to await their asynchronous operations inside try/catch
blocks, including awaiting deleteTrips() within the showInfoModal callback. Show
a SnackBar or equivalent success message when each action completes and an error
message when either operation fails, preserving the existing modal confirmation
flow.
- Around line 405-441: Update the sign-out callback in _buildSignOut so it is
asynchronous, awaits ref.read(userProvider.notifier).deleteUser(), and only then
calls context.goNamed('onboarding'); ensure deletion failures remain observable
rather than navigating immediately.

In `@lib/features/profile/services/export_data_service.dart`:
- Around line 31-61: Update shareFile to delete the temporary file created by
storeJsonToDir after SharePlus.instance.share completes, including when sharing
fails, by placing cleanup in a finally block around the share call. Preserve the
existing share behavior and rethrow error handling.
- Around line 11-29: Remove the debugPrint(jsonData) call from convertDataToJson
so exporting trips no longer writes the complete trip history to device logs;
leave JSON generation and return behavior unchanged.

In `@lib/main.dart`:
- Around line 24-25: Update the needsReset branch in main so monthly reset no
longer calls DatabaseHelper.clearTrips(), preserving all trip rows for
ExportDataService.convertDataToJson() and queryAllTrips(). Reset only the
current-month trip summary data using the existing summary-reset mechanism,
while leaving historical trip records intact.
- Around line 24-29: Wrap the needsReset branch containing dbHelper.clearTrips()
and dbHelper.updateData() in a single SQLite transaction so both writes commit
or roll back together. Use the database helper’s transaction API and ensure both
operations execute through its transaction handle, preserving the existing reset
values and sequencing.
- Around line 7-14: Update main so failures from _checkAndResetMonthlyData do
not prevent the root app from mounting: catch startup maintenance errors and
expose a controlled loading/error state with a retry path, or defer this
non-essential maintenance until after runApp initializes. Preserve the normal
startup flow when the database check succeeds.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 628fc452-c8f4-48ec-89bd-9e231acc60fc

📥 Commits

Reviewing files that changed from the base of the PR and between 6beae3e and 4b8ddb1.

⛔ Files ignored due to path filters (5)
  • ios/Podfile.lock is excluded by !**/*.lock, !**/ios/**
  • ios/Runner.xcodeproj/project.pbxproj is excluded by !**/ios/**
  • ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved is excluded by !**/Package.resolved, !**/ios/**
  • ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved is excluded by !**/Package.resolved, !**/ios/**
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (38)
  • l10n.yaml
  • lib/core/config/app_constants.dart
  • lib/core/data/tracking_options.dart
  • lib/core/data/transport_preferences.dart
  • lib/core/extensions/string_extensions.dart
  • lib/core/providers/trips_provider.dart
  • lib/core/providers/user_provider.dart
  • lib/core/widgets/loader.dart
  • lib/core/widgets/modal.dart
  • lib/database/database_helper.dart
  • lib/database/models/trips.dart
  • lib/database/models/user.dart
  • lib/features/carbon/data/carbon_modal_data.dart
  • lib/features/carbon/helpers/carbon_calculator.dart
  • lib/features/carbon/providers/summary_provider.dart
  • lib/features/carbon/screens/carbon_tracker.dart
  • lib/features/fitness/screens/fitness_metrics_screen.dart
  • lib/features/fitness/screens/main_screen.dart
  • lib/features/fitness/services/health_service.dart
  • lib/features/map/screens/map_screen.dart
  • lib/features/map/services/location_service.dart
  • lib/features/map/widgets/location_button.dart
  • lib/features/map/widgets/map_modal.dart
  • lib/features/onboarding/data/onboarding_options.dart
  • lib/features/onboarding/data/tracking_modes_info.dart
  • lib/features/onboarding/screens/splash_screen.dart
  • lib/features/onboarding/screens/user_info.dart
  • lib/features/profile/.gitkeep
  • lib/features/profile/data/trips_delete_modal_data.dart
  • lib/features/profile/screens/profile.dart
  • lib/features/profile/services/export_data_service.dart
  • lib/features/profile/widgets/section_header.dart
  • lib/features/profile/widgets/tracking_option_tile.dart
  • lib/features/profile/widgets/transport_chip.dart
  • lib/main.dart
  • lib/shared/widgets/.gitkeep
  • pubspec.yaml
  • test/carbon_calculator_test.dart
💤 Files with no reviewable changes (4)
  • lib/features/onboarding/data/onboarding_options.dart
  • l10n.yaml
  • lib/features/onboarding/data/tracking_modes_info.dart
  • lib/features/carbon/providers/summary_provider.dart

Comment thread lib/core/providers/trips_provider.dart
Comment on lines +1 to +2
String carbonModalData = 'The impact of how we move is easy to overlook. A short walk instead of a ride, a cycle instead of a car, or simply choosing a cleaner way to get somewhere can change the amount of carbon we leave behind. Your health and your footprint are connected in more ways than you might think. This is a small space to notice those choices, understand their impact, and make more mindful ones over time.';
String carbonModalTitle = 'A Closer Look at Your Carbon ';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Declare the modal data as compile-time constants.

These literal values currently expose mutable global state. Declare both variables as const String so other code cannot change shared modal content at runtime.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/carbon/data/carbon_modal_data.dart` around lines 1 - 2, Declare
carbonModalData and carbonModalTitle as const String values, preserving their
existing literal content while preventing runtime mutation of the shared modal
data.

Comment on lines +70 to +75
IconButton(
onPressed: () {
showInfoModal(context, carbonModalTitle, carbonModalData, "Close");
},
icon: const Icon(Icons.info_outline, color: Colors.black87),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files around target =="
git ls-files | rg 'lib/features/carbon/screens/carbon_tracker\.dart|pubspec\.yaml'

echo "== target excerpt =="
if [ -f lib/features/carbon/screens/carbon_tracker.dart ]; then
  wc -l lib/features/carbon/screens/carbon_tracker.dart
  sed -n '1,120p' lib/features/carbon/screens/carbon_tracker.dart | cat -n
fi

echo "== localized strings / modal tooltip terms =="
rg -n "carbonModalTitle|carbonModalData|nature|recommendations|Show information|information about|Close|showInfoModal|tooltip" lib -S || true

echo "== similar IconButton tooltip usage =="
rg -n "IconButton\\([^)]*tooltip:" lib -S || true

Repository: AOSSIE-Org/CarbonTracker

Length of output: 7552


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate localization/resource files =="
git ls-files | rg 'i18n|translations|arb|json|yaml|carton|carbon|strings' || true

echo "== all tooltip labels in UI =="
rg -n "tooltip:\\s*['\"]" lib -S || true

echo "== showInfoModal definition/usages =="
rg -n "void showInfoModal|showInfoModal\\(" lib -S || true

Repository: AOSSIE-Org/CarbonTracker

Length of output: 1670


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate localization/resource files =="
git ls-files | rg 'i18n|translations|arb|json|yaml|strings' || true

echo "== all tooltip labels in UI =="
rg -n "tooltip:\\s*['\"]" lib -S || true

echo "== showInfoModal definition/usages =="
rg -n "void showInfoModal|showInfoModal\\(" lib -S || true

Repository: AOSSIE-Org/CarbonTracker

Length of output: 1670


🌐 Web query:

Flutter IconButton tooltip accessibility semantics button without tooltip assistive technology

💡 Result:

In Flutter, the IconButton widget relies on the tooltip property as its primary mechanism for providing an accessible label to assistive technology [1][2][3]. When a tooltip is provided, it is automatically included in the semantics tree, allowing screen readers like TalkBack and VoiceOver to announce the button's purpose (e.g., "Delete item, button") [4][3][5]. If you omit the tooltip property, the IconButton lacks a meaningful semantic label [3][5]. In such cases, assistive technology may only announce "Button, double tap to activate," which provides no context to the user [3][5]. To ensure an IconButton is accessible without relying on the tooltip property, or to explicitly define its semantic identity, you can provide a semantic label directly to the icon widget nested within the button [6]. Example of providing an accessible label via the icon: IconButton( onPressed: => {}, icon: const Icon( Icons.settings, semanticLabel: 'Settings', // Explicitly defines the label for assistive technology),), When both a tooltip and a semantic label (on the child icon) are provided, they generally serve the same purpose in the semantics tree [6]. Best practice for accessibility dictates that every interactive element, especially icon-only buttons, must have a clear, descriptive label to communicate its function to users who rely on screen readers [3][7][5].

Citations:


Add an accessible label to the information button.

IconButton does not expose the icon name to assistive technology. Add a localized tooltip or semanticLabel that says the button opens the carbon recommendations information.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/carbon/screens/carbon_tracker.dart` around lines 70 - 75, Add an
accessible, localized label to the IconButton in the carbon tracker screen,
indicating that it opens the carbon recommendations information. Reuse the
existing localization mechanism and keep the current showInfoModal behavior
unchanged.

Comment on lines +15 to +38
static Future<Map<String, dynamic>> isPermissionGranted() async {
bool serviceEnabled;
LocationPermission permission;

// Test if location services are enabled.
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// Location services are not enabled
return Future.error('Location services are disabled.');
return {
'status': false,
'message': 'Location services are disabled.',
};
}

permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
// Permissions are denied
return Future.error('Location permissions are denied');

return {
'status': false,
'message': 'Location permissions are denied.',
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C 5 'MapService\.isPermissionGranted\s*\(' lib test

Repository: AOSSIE-Org/CarbonTracker

Length of output: 919


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## Find location service file"
fd -a 'location_service\.dart$' . || true

echo
echo "## MapService.isPermissionGranted implementation"
file="$(fd 'location_service\.dart$' . | head -n 1 || true)"
if [ -n "${file:-}" ]; then
  wc -l "$file"
  sed -n '1,90p' "$file" | cat -n
fi

echo
echo "## map_screen.dart callers and surrounding logic"
wc -l lib/features/map/screens/map_screen.dart
sed -n '45,85p' lib/features/map/screens/map_screen.dart | cat -n

echo
echo "## Any other references to isPermissionGranted"
rg -n -C 3 'isPermissionGranted\s*\('

Repository: AOSSIE-Org/CarbonTracker

Length of output: 5000


Return a structured result for deniedForever.

map_screen.dart expects MapService.isPermissionGranted() to return status and message for failed permission checks. The LocationPermission.deniedForever case currently returns Future.error, so the caller bypasses this contract and routes to the generic exception handler instead of setting _errMessage.

Proposed fix
 if (permission == LocationPermission.deniedForever) {
-  return Future.error(
-    'Location permissions are permanently denied, we cannot request permissions.',
-  );
+  return {
+    'status': false,
+    'message':
+        'Location permissions are permanently denied, we cannot request permissions.',
+  };
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/map/services/location_service.dart` around lines 15 - 38, Update
isPermissionGranted so LocationPermission.deniedForever returns the same
structured map contract as other failed permission checks, including status:
false and an appropriate message, instead of propagating Future.error. Preserve
the existing successful and denied permission handling paths.

TextButton(
onPressed: () {
onCompleted();
onClose();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C 8 \
  'showMapModal|VoidCallback onClose|VoidCallback onCompleted|cancelTrip|loadTrips' \
  lib/features/map

Repository: AOSSIE-Org/CarbonTracker

Length of output: 6709


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== map_modal.dart outline =="
ast-grep outline lib/features/map/widgets/map_modal.dart || true

echo "== map_modal.dart relevant lines =="
wc -l lib/features/map/widgets/map_modal.dart
sed -n '1,180p' lib/features/map/widgets/map_modal.dart | cat -n

echo "== map_screen.dart showMapModal call and resetState context =="
wc -l lib/features/map/screens/map_screen.dart
sed -n '170,240p' lib/features/map/screens/map_screen.dart | cat -n
rg -n -C 4 'resetState|_tripRepository\.cancelTrip|loadTrips\(\)' lib/features/map/screens/map_screen.dart

echo "== trip_repository.dart =="
sed -n '1,80p' lib/features/map/repositories/trip_repository.dart | cat -n

echo "== static verifier of signature/assignment/call behavior in source =="
python3 - <<'PY'
from pathlib import Path
import re

modal = Path('lib/features/map/widgets/map_modal.dart').read_text()
screen = Path('lib/features/map/screens/map_screen.dart').read_text()
repo = Path('lib/features/map/repositories/trip_repository.dart').read_text()

checks = {
    'showMapModal accepts synchronous VoidCallback callbacks': 'VoidCallback onClose, VoidCallback onCompleted' in modal,
    'current onClose call discards returned Future': re.search(r'onClose\(\)\s*;', modal) is not None,
    'current onCompleted call discards returned Future': re.search(r'onCompleted\(\)\s*;', modal) is not None,
    'call site passes current trip ID argument': 'cancelTrip(\n        _currentTripId!,' in screen,
    'call site awaits cancelTrip': 'await _tripRepository.cancelTrip' in screen,
    'call site awaits loadTrips and then calls resetState': re.search(r'await _tripRepository\.cancelTrip[^}]*await ref\s*\.\s*read\(tripProvider\.notifier\)\s*\.\s*loadTrips\(\)\s*;', screen, re.S),
    'no try catch around async callback invocation in map_screen callback': re.search(r'\(\)\s*async\s*\{[^}]*cancelTrip[^}]*\}', screen, re.S) is not None and 'try' not in re.search(r'\(\)\s*async\s*\{(?P<body>.*?)\n\s+\},', screen, re.S).group('body') if re.search(r'\(\)\s*async\s*\{(?P<body>.*?)\n\s+\},', screen, re.S) else False,
}
for name, ok in checks.items():
    print(f'{name}: {ok}')

print('\n== async type-widening check (semantic) ==')
print('Dart function type rule: async function returning Future<void>/int is assignable to VoidCallback (expected return type is Future).')
print('Consequently, onCall, runtime returned Future is discarded.')
PY

Repository: AOSSIE-Org/CarbonTracker

Length of output: 13289


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== map_screen.dart map modal call site =="
sed -n '200,230p' lib/features/map/screens/map_screen.dart | cat -n

echo "== map_screen.dart surrounding navigation/content for modal use context =="
sed -n '180,240p' lib/features/map/screens/map_screen.dart | cat -n

Repository: AOSSIE-Org/CarbonTracker

Length of output: 5255


Await the asynchronous cancellation action before closing the dialog.

showMapModal() accepts VoidCallback, but map_screen.dart passes async callbacks for both actions. onClose() discards the returned Future, so cancelTrip() failures become unhandled, and the Cancel Trip dialog also closes before the cancellation flow completes. Use Future<void> Function() for both onClose and onCompleted, await them, handle failures, and only dismiss afterward.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/map/widgets/map_modal.dart` at line 110, Update showMapModal and
its onClose/onCompleted callback types to Future<void> Function(), await each
asynchronous action before dismissing the dialog, and handle failures so
cancellation errors are not unhandled. Ensure the Cancel Trip dialog remains
open until the callback completes successfully, then invoke the existing
dismissal flow.

Comment on lines +11 to +29
static Future<String> convertDataToJson() async {
final List<Trip> trips = await DatabaseHelper().queryAllTrips();

try {
// Convert the list of trips to JSON
final String jsonData = jsonEncode(
trips.map((trip) {
final t = trip.toMap();
t['date'] = DateFormat('yyyy-MM-dd HH:mm:ss').format(trip.date);
return t;
}).toList(),
);
// Save the JSON data
debugPrint(jsonData);
return jsonData;
} catch (e) {
rethrow;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Remove the debug log of full trip data.

Line 24 calls debugPrint(jsonData), printing the complete exported trip history (dates, distances, transport modes, carbon figures) to the device log on every export. debugPrint is not stripped in release builds, so this data lands in logs outside the app's control. Remove this line, or gate it behind kDebugMode if it is needed for local debugging only.

🔒️ Proposed fix
       );
-      // Save the JSON data
-      debugPrint(jsonData);
       return jsonData;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static Future<String> convertDataToJson() async {
final List<Trip> trips = await DatabaseHelper().queryAllTrips();
try {
// Convert the list of trips to JSON
final String jsonData = jsonEncode(
trips.map((trip) {
final t = trip.toMap();
t['date'] = DateFormat('yyyy-MM-dd HH:mm:ss').format(trip.date);
return t;
}).toList(),
);
// Save the JSON data
debugPrint(jsonData);
return jsonData;
} catch (e) {
rethrow;
}
}
static Future<String> convertDataToJson() async {
final List<Trip> trips = await DatabaseHelper().queryAllTrips();
try {
// Convert the list of trips to JSON
final String jsonData = jsonEncode(
trips.map((trip) {
final t = trip.toMap();
t['date'] = DateFormat('yyyy-MM-dd HH:mm:ss').format(trip.date);
return t;
}).toList(),
);
return jsonData;
} catch (e) {
rethrow;
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/profile/services/export_data_service.dart` around lines 11 - 29,
Remove the debugPrint(jsonData) call from convertDataToJson so exporting trips
no longer writes the complete trip history to device logs; leave JSON generation
and return behavior unchanged.

Comment on lines +31 to +61
static Future<File> storeJsonToDir() async {
try {
final jsonData = await convertDataToJson();

final directory = await getTemporaryDirectory();

final logFile = File(
'${directory.path}/trips_data-${DateTime.now().millisecondsSinceEpoch}.json',
);

final sink = logFile.openWrite();

sink.write(jsonData);

await sink.flush();
await sink.close();

return logFile;
} catch (e) {
rethrow;
}
}

static Future<void> shareFile() async {
try {
File file = await storeJsonToDir();
await SharePlus.instance.share(ShareParams(files: [XFile(file.path)]));
} catch (e) {
rethrow;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Delete the exported temp file after sharing.

storeJsonToDir (Lines 31-52) writes a timestamped trips_data-*.json file to the temporary directory. Nothing ever removes this file, including after shareFile (Lines 54-61) completes. Every export leaves a full copy of the user's trip history on disk, and repeated exports accumulate multiple copies over time.

Delete the file once the share sheet finishes, for example by using the ShareResult returned by SharePlus.instance.share to trigger cleanup, or by deleting the file in a finally block after the share call.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/profile/services/export_data_service.dart` around lines 31 - 61,
Update shareFile to delete the temporary file created by storeJsonToDir after
SharePlus.instance.share completes, including when sharing fails, by placing
cleanup in a finally block around the share call. Preserve the existing share
behavior and rethrow error handling.

Comment thread lib/main.dart
Comment on lines +7 to +14
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();

final dbHelper = DatabaseHelper();
await _checkAndResetMonthlyData(dbHelper);

runApp(const ProviderScope(child: MyApp()));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | sed -n '1,120p'

echo "== lib/main.dart =="
if [ -f lib/main.dart ]; then
  cat -n lib/main.dart
else
  fd -a 'main\.dart$' .
fi

echo "== DatabaseHelper references =="
rg -n "class DatabaseHelper|DatabaseHelper|queryUser|clearTrips|updateData|_checkAndResetMonthlyData|runApp" -S .

Repository: AOSSIE-Org/CarbonTracker

Length of output: 8887


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== database_helper relevant methods =="
sed -n '1,230p' lib/database/database_helper.dart | nl -ba

echo "== exceptions definitions =="
cat -n lib/database/database_exceptions.dart

echo "== search for runZoned/error UI/failure handlers =="
rg -n "runZoned|runZonedGuarded|FlutterError.onError|PlatformDispatcher.onError|ErrorWidget|showDialog|Error|Exception|run\\(" lib -S

Repository: AOSSIE-Org/CarbonTracker

Length of output: 241


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== database_helper relevant methods =="
sed -n '1,230p' lib/database/database_helper.dart | cat -n

echo "== exceptions definitions =="
cat -n lib/database/database_exceptions.dart || true

echo "== search for runZoned/error UI/failure handlers =="
rg -n "runZoned|runZonedGuarded|FlutterError.onError|PlatformDispatcher.onError|ErrorWidget|showDialog|Error|Exception|run\\(" lib -S || true

Repository: AOSSIE-Org/CarbonTracker

Length of output: 13950


Provide a recovery path when the startup database check fails.

main awaits _checkAndResetMonthlyData before runApp. If queryUser, clearTrips, or updateData throws, the root app is never mounted and the user is left without a rendered retry or error state. Handle startup failures with a controlled loading/error screen, or run non-essential monthly reset maintenance after runApp initialization.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/main.dart` around lines 7 - 14, Update main so failures from
_checkAndResetMonthlyData do not prevent the root app from mounting: catch
startup maintenance errors and expose a controlled loading/error state with a
retry path, or defer this non-essential maintenance until after runApp
initializes. Preserve the normal startup flow when the database check succeeds.

Comment thread lib/main.dart
Comment on lines +24 to +25
if (needsReset) {
await dbHelper.clearTrips();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 8 'clearTrips|share_plus|export|trips' lib test --glob '*.dart'

Repository: AOSSIE-Org/CarbonTracker

Length of output: 29119


Align clearTrips() with trip-history export.

ExportDataService.convertDataToJson() exports every row from trips via DatabaseHelper().queryAllTrips(), but monthly reset calls clearTrips(), which deletes the whole table before generating next month’s export. If trip history should persist across months, reset only current-month trip summaries and leave trip rows intact.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/main.dart` around lines 24 - 25, Update the needsReset branch in main so
monthly reset no longer calls DatabaseHelper.clearTrips(), preserving all trip
rows for ExportDataService.convertDataToJson() and queryAllTrips(). Reset only
the current-month trip summary data using the existing summary-reset mechanism,
while leaving historical trip records intact.

Comment thread lib/main.dart
Comment on lines +24 to +29
if (needsReset) {
await dbHelper.clearTrips();
await dbHelper.updateData(
'user',
user.copyWith(lastResetMonth: now.month, lastResetYear: now.year),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Tracked files relevant to main.dart / dbHelper:"
git ls-files | rg '(^|/)(lib/main.dart|.*db.*|.*database.*|.*trips.*|.*settings.*)\.dart$|database\.dart' || true

echo
echo "lib/main.dart outline:"
if [ -f lib/main.dart ]; then
  ast-grep outline lib/main.dart --view compact || true
  echo
  sed -n '1,220p' lib/main.dart
fi

echo
echo "Search dbHelper definitions/usages:"
rg -n "class .*DbHelper|dbHelper|clearTrips|updateData|lastResetMonth|lastResetYear|transaction|SQLite" -S .

Repository: AOSSIE-Org/CarbonTracker

Length of output: 4328


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "database_helper.dart outline:"
ast-grep outline lib/database/database_helper.dart --view expanded || true

echo
echo "Relevant database_helper.dart sections:"
sed -n '1,280p' lib/database/database_helper.dart

echo
echo "Relevant other files:"
printf "\n--- lib/database/models/user.dart ---\n"
sed -n '1,120p' lib/database/models/user.dart
printf "\n--- lib/database/models/trips.dart ---\n"
sed -n '1,120p' lib/database/models/trips.dart
printf "\n--- lib/database/database_exceptions.dart ---\n"
sed -n '1,120p' lib/database/database_exceptions.dart
printf "\n--- lib/core/providers/trips_provider.dart ---\n"
sed -n '1,80p' lib/core/providers/trips_provider.dart

echo
echo "Static call sequence check:"
python3 - <<'PY'
from pathlib import Path
src=Path('lib/database/database_helper.dart').read_text()
for name in ['clearTrips', 'updateData', 'queryUser', 'runTransaction']:
    i=src.find(f'{name}<')
    print(name, i)
PY

Repository: AOSSIE-Org/CarbonTracker

Length of output: 13909


Wrap the monthly reset in one database transaction.

clearTrips() deletes all trip rows first, and updateData() updates last_reset_month / last_reset_year in a second db.delete() / db.update() call. If the update fails or app termination happens between them, the app can treat the next month as already reset while trip data no longer exists. Use a SQLite transaction for both writes, or make the reset resumable with a pending reset state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/main.dart` around lines 24 - 29, Wrap the needsReset branch containing
dbHelper.clearTrips() and dbHelper.updateData() in a single SQLite transaction
so both writes commit or roll back together. Use the database helper’s
transaction API and ensure both operations execute through its transaction
handle, preserving the existing reset values and sequencing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

configuration Configuration file changes needs-review no-issue-linked PR is not linked to any issue repeat-contributor PR from an external contributor who already had PRs merged size/XL Extra large PR (>500 lines changed) tests Test file changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant