Feature/export user data - #13
Conversation
WalkthroughThe 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. ChangesApplication feature updates
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
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (5)
ios/Podfile.lockis excluded by!**/*.lock,!**/ios/**ios/Runner.xcodeproj/project.pbxprojis excluded by!**/ios/**ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolvedis excluded by!**/Package.resolved,!**/ios/**ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolvedis excluded by!**/Package.resolved,!**/ios/**pubspec.lockis excluded by!**/*.lock
📒 Files selected for processing (38)
l10n.yamllib/core/config/app_constants.dartlib/core/data/tracking_options.dartlib/core/data/transport_preferences.dartlib/core/extensions/string_extensions.dartlib/core/providers/trips_provider.dartlib/core/providers/user_provider.dartlib/core/widgets/loader.dartlib/core/widgets/modal.dartlib/database/database_helper.dartlib/database/models/trips.dartlib/database/models/user.dartlib/features/carbon/data/carbon_modal_data.dartlib/features/carbon/helpers/carbon_calculator.dartlib/features/carbon/providers/summary_provider.dartlib/features/carbon/screens/carbon_tracker.dartlib/features/fitness/screens/fitness_metrics_screen.dartlib/features/fitness/screens/main_screen.dartlib/features/fitness/services/health_service.dartlib/features/map/screens/map_screen.dartlib/features/map/services/location_service.dartlib/features/map/widgets/location_button.dartlib/features/map/widgets/map_modal.dartlib/features/onboarding/data/onboarding_options.dartlib/features/onboarding/data/tracking_modes_info.dartlib/features/onboarding/screens/splash_screen.dartlib/features/onboarding/screens/user_info.dartlib/features/profile/.gitkeeplib/features/profile/data/trips_delete_modal_data.dartlib/features/profile/screens/profile.dartlib/features/profile/services/export_data_service.dartlib/features/profile/widgets/section_header.dartlib/features/profile/widgets/tracking_option_tile.dartlib/features/profile/widgets/transport_chip.dartlib/main.dartlib/shared/widgets/.gitkeeppubspec.yamltest/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
| 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 '; |
There was a problem hiding this comment.
📐 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.
| IconButton( | ||
| onPressed: () { | ||
| showInfoModal(context, carbonModalTitle, carbonModalData, "Close"); | ||
| }, | ||
| icon: const Icon(Icons.info_outline, color: Colors.black87), | ||
| ) |
There was a problem hiding this comment.
🎯 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 || trueRepository: 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 || trueRepository: 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 || trueRepository: 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:
- 1: https://api.flutter.dev/flutter/material/IconButton/tooltip.html
- 2: https://api.flutter.dev/flutter/material/IconButton-class.html
- 3: https://dcm.dev/blog/2025/06/30/accessibility-flutter-practical-tips-tools-code-youll-actually-use/
- 4: https://api.flutter.dev/flutter/material/Tooltip-class.html
- 5: https://www.tutorialpedia.org/blog/accessibility-scanner-issue-no-readable-label-for-flutter-apps/
- 6: IconButton Semantics Label flutter/flutter#148167
- 7: https://sagnikbhattacharya.com/blog/flutter-icons-iconbutton
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.
| 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.', | ||
| }; |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 5 'MapService\.isPermissionGranted\s*\(' lib testRepository: 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(); |
There was a problem hiding this comment.
🩺 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/mapRepository: 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.')
PYRepository: 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 -nRepository: 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.
| 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; | ||
| } | ||
| } |
There was a problem hiding this comment.
🔒 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.
| 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.
| 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; | ||
| } | ||
| } |
There was a problem hiding this comment.
🔒 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.
| Future<void> main() async { | ||
| WidgetsFlutterBinding.ensureInitialized(); | ||
|
|
||
| final dbHelper = DatabaseHelper(); | ||
| await _checkAndResetMonthlyData(dbHelper); | ||
|
|
||
| runApp(const ProviderScope(child: MyApp())); | ||
| } |
There was a problem hiding this comment.
🩺 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 -SRepository: 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 || trueRepository: 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.
| if (needsReset) { | ||
| await dbHelper.clearTrips(); |
There was a problem hiding this comment.
🗄️ 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.
| if (needsReset) { | ||
| await dbHelper.clearTrips(); | ||
| await dbHelper.updateData( | ||
| 'user', | ||
| user.copyWith(lastResetMonth: now.month, lastResetYear: now.year), | ||
| ); |
There was a problem hiding this comment.
🗄️ 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)
PYRepository: 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.
Week 10
Screenshots/Recordings:
iOS
WhatsApp.Video.2026-08-04.at.01.57.12.mp4
Saved File
Additional Notes:
share_plus.CarbonCalculatoremissions.Checklist
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
Bug Fixes
Tests