@@ -20,12 +20,12 @@ import '../utils/debug_logger_io.dart';
2020import '../utils/distance_formatter.dart' ;
2121import '../models/user_preferences.dart' ;
2222import '../services/debug_file_logger.dart' ;
23- import '../services/debug_submit_service.dart' ;
2423import '../services/gps_simulator_service.dart' ;
2524import '../services/offline_session_service.dart' ;
2625import '../services/permission_disclosure_service.dart' ;
2726import '../utils/constants.dart' ;
2827import '../widgets/bug_report_dialog.dart' ;
28+ import '../widgets/upload_logs_dialog.dart' ;
2929import 'package:intl/intl.dart' ;
3030import '../widgets/app_toast.dart' ;
3131
@@ -42,93 +42,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
4242 int _versionTapCount = 0 ;
4343 DateTime ? _lastVersionTap;
4444
45- // Debug log upload tracking
46- String ? _uploadingFilePath;
47- final Set <String > _uploadedFiles = {};
45+ Future <void > _showUploadLogsDialog (BuildContext context, AppStateProvider appState) async {
46+ final result = await showUploadLogsDialog (context, appState);
4847
49- Future <void > _uploadSingleLogFile (AppStateProvider appState, File file) async {
50- final filename = file.path.split ('/' ).last;
51-
52- setState (() {
53- _uploadingFilePath = file.path;
54- });
55-
56- try {
57- final service = DebugSubmitService ();
58-
59- // Use persistent device info
60- final publicKey = appState.devicePublicKey ??
61- appState.lastConnectedPublicKey ??
62- 'not-connected' ;
63- final deviceName = appState.lastConnectedDeviceName ?? 'not-connected' ;
64-
65- final success = await service.uploadDebugFileOnly (
66- file: file,
67- deviceId: deviceName,
68- publicKey: publicKey,
69- appVersion: AppConstants .appVersion,
70- devicePlatform: DebugSubmitService .getDevicePlatform (),
71- userNotes: 'Direct debug log upload from $deviceName ' ,
72- );
73-
74- service.dispose ();
75-
76- if (! mounted) return ;
77-
78- if (success) {
79- setState (() {
80- _uploadedFiles.add (file.path);
81- _uploadingFilePath = null ;
82- });
83- AppToast .success (context, 'Uploaded $filename ' );
84- } else {
85- setState (() {
86- _uploadingFilePath = null ;
87- });
88- AppToast .error (context, 'Failed to upload $filename ' );
89- }
90- } catch (e) {
91- debugError ('[SETTINGS] Log file upload error: $e ' );
92- if (mounted) {
93- setState (() {
94- _uploadingFilePath = null ;
95- });
96- AppToast .error (context, 'Upload error: $e ' );
97- }
98- }
99- }
100-
101- /// Upload the current (active) log file by rotating it first
102- Future <void > _uploadCurrentLogFile (AppStateProvider appState, File currentFile) async {
103- final originalPath = currentFile.path;
104-
105- setState (() {
106- _uploadingFilePath = originalPath;
107- });
108-
109- try {
110- // Rotate the log: closes current file, starts a new one
111- // The original file is now closed and safe to upload
112- await appState.prepareDebugLogsForUpload ();
113-
114- if (! mounted) return ;
115-
116- // The original file still exists at the same path, now closed
117- final closedFile = File (originalPath);
118- if (! closedFile.existsSync ()) {
119- throw Exception ('Log file not found after rotation' );
120- }
48+ if (! context.mounted || result == null ) return ;
12149
122- // Now upload the closed file
123- await _uploadSingleLogFile (appState, closedFile);
124- } catch (e) {
125- debugError ('[SETTINGS] Current log upload error: $e ' );
126- if (mounted) {
127- setState (() {
128- _uploadingFilePath = null ;
129- });
130- AppToast .error (context, 'Upload error: $e ' );
50+ if (result.success) {
51+ String message = 'Uploaded ${result .uploadedCount } log file${result .uploadedCount == 1 ? '' : 's' }' ;
52+ if (result.failedCount > 0 ) {
53+ message += ' (${result .failedCount } failed)' ;
13154 }
55+ AppToast .success (context, message);
56+ } else if (result.errorMessage != null ) {
57+ AppToast .error (context, result.errorMessage! );
13258 }
13359 }
13460
@@ -716,12 +642,18 @@ class _SettingsScreenState extends State<SettingsScreen> {
716642 ),
717643 ),
718644 const Spacer (),
719- if (appState.debugLogFiles.isNotEmpty)
645+ if (appState.debugLogFiles.isNotEmpty) ...[
646+ TextButton .icon (
647+ icon: const Icon (Icons .cloud_upload, size: 18 ),
648+ label: const Text ('Upload' ),
649+ onPressed: () => _showUploadLogsDialog (context, appState),
650+ ),
720651 TextButton .icon (
721652 icon: const Icon (Icons .delete_sweep, size: 18 ),
722653 label: const Text ('Delete All' ),
723654 onPressed: () => _confirmDeleteAllLogs (context, appState),
724655 ),
656+ ],
725657 ],
726658 ),
727659 ),
@@ -741,18 +673,13 @@ class _SettingsScreenState extends State<SettingsScreen> {
741673 final file = entry.value;
742674 final filename = file.path.split ('/' ).last;
743675 final sizeBytes = file.lengthSync ();
744- final isUploading = _uploadingFilePath == file.path;
745- final wasUploaded = _uploadedFiles.contains (file.path);
746- // First file (index 0) is the most recent/active log - can't upload
747676 final isCurrentLog = index == 0 ;
748- // Parse unix timestamp from filename (meshmapper-debug-{timestamp}.txt)
749677 final timestampMatch = RegExp (r'meshmapper-debug-(\d+)\.txt' ).firstMatch (filename);
750678 final fileDate = timestampMatch != null
751679 ? DateTime .fromMillisecondsSinceEpoch (int .parse (timestampMatch.group (1 )! ) * 1000 )
752680 : null ;
753681 final dateStr = fileDate != null ? DateFormat ('MMM d, h:mm a' ).format (fileDate) : filename;
754682
755- // Format size and show part count for oversized files
756683 String sizeDisplay;
757684 final partCount = DebugFileLogger .estimatePartCount (sizeBytes);
758685 if (sizeBytes >= DebugFileLogger .maxUploadSizeBytes) {
@@ -775,48 +702,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
775702 trailing: Row (
776703 mainAxisSize: MainAxisSize .min,
777704 children: [
778- // Upload button
779- if (wasUploaded)
780- Container (
781- width: 40 ,
782- height: 40 ,
783- alignment: Alignment .center,
784- child: const Icon (
785- Icons .check_circle,
786- size: 20 ,
787- color: Colors .green,
788- ),
789- )
790- else if (isUploading)
791- Container (
792- width: 40 ,
793- height: 40 ,
794- alignment: Alignment .center,
795- child: const SizedBox (
796- width: 20 ,
797- height: 20 ,
798- child: CircularProgressIndicator (strokeWidth: 2 ),
799- ),
800- )
801- else
802- IconButton (
803- icon: const Icon (Icons .cloud_upload, size: 20 ),
804- onPressed: _uploadingFilePath != null
805- ? null
806- : () => isCurrentLog
807- ? _uploadCurrentLogFile (appState, file)
808- : _uploadSingleLogFile (appState, file),
809- tooltip: isCurrentLog
810- ? 'Close log and upload'
811- : 'Upload to developer' ,
812- ),
813- // View button
814705 IconButton (
815706 icon: const Icon (Icons .visibility, size: 20 ),
816707 onPressed: () => _showLogViewer (context, appState, file),
817708 tooltip: 'View' ,
818709 ),
819- // Share button
820710 IconButton (
821711 icon: const Icon (Icons .share, size: 20 ),
822712 onPressed: () => appState.shareDebugLog (file),
0 commit comments