Skip to content

Commit 098a41b

Browse files
committed
fix: share startup error logs as file and add export-to-file option
1 parent d8f0255 commit 098a41b

3 files changed

Lines changed: 70 additions & 27 deletions

File tree

lib/core/screens/app_init_error_screen.dart

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import 'package:bb_mobile/core/utils/constants.dart';
44
import 'package:bb_mobile/core/utils/logger.dart';
55
import 'package:bb_mobile/core/widgets/app_language_picker.dart';
66
import 'package:bb_mobile/core/widgets/buttons/button.dart';
7+
import 'package:bb_mobile/core/widgets/share_logs_bottom_sheet.dart';
78
import 'package:bb_mobile/core/widgets/snackbar_utils.dart';
89
import 'package:bb_mobile/generated/l10n/localization.dart';
910
import 'package:flutter/material.dart';
1011
import 'package:flutter_svg/flutter_svg.dart';
1112
import 'package:bull_ui/bull_ui.dart' show Gap;
12-
import 'package:share_plus/share_plus.dart';
1313
import 'package:url_launcher/url_launcher.dart';
1414

1515
class AppInitErrorScreen extends StatefulWidget {
@@ -24,25 +24,40 @@ class AppInitErrorScreen extends StatefulWidget {
2424
class _AppInitErrorScreenState extends State<AppInitErrorScreen> {
2525
Language _language = Language.fromKeyboard();
2626

27-
Future<void> _shareLogs(BuildContext context) async {
27+
Future<void> _shareLogs(BuildContext context, AppLocalizations loc) async {
2828
try {
2929
final logs = await log.readLogs();
30+
await shareLogsAsFile(logs);
31+
} catch (e) {
32+
log.severe(
33+
message: 'Failed to share logs',
34+
error: e,
35+
trace: StackTrace.current,
36+
);
3037
if (!context.mounted) return;
31-
await SharePlus.instance.share(
32-
ShareParams(
33-
text: logs.join('\n'),
34-
subject: 'bull_logs.tsv',
35-
title: 'bull_logs.tsv',
36-
),
38+
SnackBarUtils.showSnackBar(
39+
context,
40+
loc.errorSharingLogsMessage(e.toString()),
3741
);
42+
}
43+
}
44+
45+
Future<void> _exportLogs(BuildContext context, AppLocalizations loc) async {
46+
try {
47+
final logs = await log.readLogs();
48+
final saved = await exportLogsAsFile(logs);
49+
if (!context.mounted) return;
50+
if (saved) {
51+
SnackBarUtils.showSnackBar(context, loc.logsExportedMessage);
52+
}
3853
} catch (e) {
3954
log.severe(
40-
message: 'Failed to share logs',
55+
message: 'Failed to export logs',
4156
error: e,
4257
trace: StackTrace.current,
4358
);
4459
if (!context.mounted) return;
45-
SnackBarUtils.showSnackBar(context, 'Failed to share logs: $e');
60+
SnackBarUtils.showSnackBar(context, loc.logsExportFailedMessage);
4661
}
4762
}
4863

@@ -128,7 +143,18 @@ class _AppInitErrorScreenState extends State<AppInitErrorScreen> {
128143
textColor: context.appColors.text,
129144
borderColor: context.appColors.border,
130145
outlined: true,
131-
onPressed: () => _shareLogs(context),
146+
onPressed: () => _shareLogs(context, loc),
147+
),
148+
const Gap(12),
149+
BBButton.big(
150+
label: loc.logsShareOptionExport,
151+
iconData: Icons.file_download_outlined,
152+
iconFirst: true,
153+
bgColor: context.appColors.surface,
154+
textColor: context.appColors.text,
155+
borderColor: context.appColors.border,
156+
outlined: true,
157+
onPressed: () => _exportLogs(context, loc),
132158
),
133159
const Gap(12),
134160
BBButton.big(

lib/core/widgets/share_logs_bottom_sheet.dart

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import 'dart:convert';
2+
import 'dart:io';
23

34
import 'package:bb_mobile/core/utils/build_context_x.dart';
45
import 'package:bull_ui/bull_ui.dart';
56
import 'package:file_picker/file_picker.dart';
67
import 'package:flutter/material.dart';
8+
import 'package:path/path.dart' as p;
9+
import 'package:path_provider/path_provider.dart';
710
import 'package:share_plus/share_plus.dart';
811

912
Future<void> showLogsShareSheet({
@@ -58,15 +61,30 @@ Future<void> shareLogsAsText(List<String> logs) async {
5861
);
5962
}
6063

64+
Future<void> shareLogsAsFile(List<String> logs) async {
65+
final path = p.join(
66+
(await getTemporaryDirectory()).path,
67+
_timestampedLogFileName(),
68+
);
69+
await File(path).writeAsString(logs.join('\n'));
70+
await SharePlus.instance.share(
71+
ShareParams(files: [XFile(path)], subject: 'bull_logs.tsv'),
72+
);
73+
}
74+
6175
/// Returns true if the file was saved, false if the user cancelled.
6276
Future<bool> exportLogsAsFile(List<String> logs) async {
63-
final timestamp = DateTime.now()
64-
.toIso8601String()
65-
.replaceAll(':', '-')
66-
.replaceAll('.', '-');
6777
final result = await FilePicker.platform.saveFile(
6878
bytes: utf8.encode(logs.join('\n')),
69-
fileName: 'bull_logs_$timestamp.tsv',
79+
fileName: _timestampedLogFileName(),
7080
);
7181
return result != null;
7282
}
83+
84+
String _timestampedLogFileName() {
85+
final timestamp = DateTime.now()
86+
.toIso8601String()
87+
.replaceAll(':', '-')
88+
.replaceAll('.', '-');
89+
return 'bull_logs_$timestamp.tsv';
90+
}

lib/core/widgets/share_logs_widget.dart

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,18 @@ class ShareLogsWidget extends StatelessWidget {
1717
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2)),
1818
tileColor: context.appColors.transparent,
1919
title: Text(context.loc.shareLogsLabel),
20-
onTap: () => _onShareTapped(context),
20+
onTap: () => _shareLogs(context),
2121
trailing: const Icon(Icons.share_sharp),
2222
),
2323
const Gap(8),
24+
ListTile(
25+
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2)),
26+
tileColor: context.appColors.transparent,
27+
title: Text(context.loc.logsShareOptionExport),
28+
onTap: () => _exportLogs(context),
29+
trailing: const Icon(Icons.file_download_outlined),
30+
),
31+
const Gap(8),
2432
GestureDetector(
2533
onTap: () => _deleteLogs(context),
2634
child: Padding(
@@ -46,19 +54,10 @@ class ShareLogsWidget extends StatelessWidget {
4654
SnackBarUtils.showSnackBar(context, context.loc.logsDeletedMessage);
4755
}
4856

49-
Future<void> _onShareTapped(BuildContext context) async {
50-
await showLogsShareSheet(
51-
context: context,
52-
onShare: () => _shareLogs(context),
53-
onExport: () => _exportLogs(context),
54-
);
55-
}
56-
5757
Future<void> _shareLogs(BuildContext context) async {
5858
try {
5959
final logs = await log.readLogs();
60-
if (!context.mounted) return;
61-
await shareLogsAsText(logs);
60+
await shareLogsAsFile(logs);
6261
} catch (e) {
6362
if (!context.mounted) return;
6463
SnackBarUtils.showSnackBar(

0 commit comments

Comments
 (0)