Skip to content

Commit 446c636

Browse files
committed
Merge PR #847.
2 parents 8ebcafe + 7170e1a commit 446c636

File tree

4 files changed

+75
-8
lines changed

4 files changed

+75
-8
lines changed

lib/app/state.dart

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@ import 'models.dart';
2727

2828
final _log = Logger('app.state');
2929

30+
// When non-null, an unrecoverable error preventing the app from functioning has occurred.
31+
final applicationError =
32+
StateNotifierProvider<ApplicationErrorNotifier, String?>(
33+
(ref) => ApplicationErrorNotifier(),
34+
);
35+
36+
class ApplicationErrorNotifier extends StateNotifier<String?> {
37+
ApplicationErrorNotifier() : super(null);
38+
39+
void setApplicationError(String? error) {
40+
_log.debug('Set ApplicationError to $error');
41+
state = error;
42+
}
43+
}
44+
3045
// Override this to alter the set of supported apps.
3146
final supportedAppsProvider =
3247
Provider<List<Application>>((ref) => Application.values);
@@ -42,8 +57,7 @@ final supportedThemesProvider = StateProvider<List<ThemeMode>>(
4257

4358
final themeModeProvider = StateNotifierProvider<ThemeModeNotifier, ThemeMode>(
4459
(ref) => ThemeModeNotifier(
45-
ref.watch(prefProvider),
46-
ref.read(supportedThemesProvider)),
60+
ref.watch(prefProvider), ref.read(supportedThemesProvider)),
4761
);
4862

4963
class ThemeModeNotifier extends StateNotifier<ThemeMode> {

lib/app/views/main_page.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,25 @@
1616

1717
import 'package:flutter/material.dart';
1818
import 'package:flutter_riverpod/flutter_riverpod.dart';
19+
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
20+
import 'package:logging/logging.dart';
1921
import 'package:yubico_authenticator/cancellation_exception.dart';
2022
import 'package:yubico_authenticator/core/state.dart';
2123

2224
import '../../fido/views/fido_screen.dart';
2325
import '../../oath/models.dart';
2426
import '../../oath/views/add_account_page.dart';
2527
import '../../oath/views/oath_screen.dart';
28+
import '../../version.dart';
29+
import '../logging.dart';
2630
import '../message.dart';
2731
import '../models.dart';
2832
import '../state.dart';
2933
import 'device_error_screen.dart';
3034
import 'message_page.dart';
3135

36+
final _log = Logger('app.views.main_page');
37+
3238
class MainPage extends ConsumerWidget {
3339
const MainPage({super.key});
3440

@@ -40,6 +46,35 @@ class MainPage extends ConsumerWidget {
4046
next?.call(context);
4147
},
4248
);
49+
50+
final appError = ref.watch(applicationError);
51+
if (appError != null) {
52+
return MessagePage(
53+
header: 'An unrecoverable error has occured',
54+
message: appError,
55+
actions: [
56+
ActionChip(
57+
avatar: const Icon(Icons.copy),
58+
label: Text(AppLocalizations.of(context)!.general_copy_log),
59+
onPressed: () async {
60+
_log.info('Copying log to clipboard ($version)...');
61+
final logs = await ref.read(logLevelProvider.notifier).getLogs();
62+
var clipboard = ref.read(clipboardProvider);
63+
await clipboard.setText(logs.join('\n'));
64+
if (!clipboard.platformGivesFeedback()) {
65+
await ref.read(withContextProvider)(
66+
(context) async {
67+
showMessage(context,
68+
AppLocalizations.of(context)!.general_log_copied);
69+
},
70+
);
71+
}
72+
},
73+
),
74+
],
75+
);
76+
}
77+
4378
// If the current device changes, we need to pop any open dialogs.
4479
ref.listen<AsyncValue<YubiKeyData>>(currentDeviceDataProvider, (_, __) {
4580
Navigator.of(context).popUntil((route) {

lib/desktop/init.dart

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,11 @@ Future<Widget> initialize(List<String> argv) async {
102102
final rpc = RpcSession(exe!);
103103
await rpc.initialize();
104104
_log.info('Helper process started', exe);
105-
rpc.setLogLevel(Logger.root.level);
105+
106+
// Set the initial logging level. As this is the first message to the RPC,
107+
// it also serves to check that the Helper is functioning correctly.
108+
// The future will be awaited further down.
109+
final initRpcLogFuture = rpc.setLogLevel(Logger.root.level);
106110

107111
_initLicenses();
108112

@@ -131,7 +135,8 @@ Future<Widget> initialize(List<String> argv) async {
131135
fingerprintProvider.overrideWithProvider(desktopFingerprintProvider),
132136
credentialProvider.overrideWithProvider(desktopCredentialProvider),
133137
clipboardProvider.overrideWithProvider(desktopClipboardProvider),
134-
supportedThemesProvider.overrideWithProvider(desktopSupportedThemesProvider)
138+
supportedThemesProvider
139+
.overrideWithProvider(desktopSupportedThemesProvider)
135140
],
136141
child: YubicoAuthenticatorApp(
137142
page: Consumer(
@@ -141,6 +146,17 @@ Future<Widget> initialize(List<String> argv) async {
141146
rpc.setLogLevel(level);
142147
});
143148

149+
// Ensure the initial log level was successfully set within 5s, or
150+
// assume the Helper isn't functional.
151+
initRpcLogFuture.timeout(const Duration(seconds: 5)).onError(
152+
(error, stackTrace) {
153+
_log.error('Helper is not responsive.');
154+
ref
155+
.read(applicationError.notifier)
156+
.setApplicationError('Helper subprocess failed to start');
157+
},
158+
);
159+
144160
return const MainPage();
145161
}),
146162
),
@@ -175,10 +191,12 @@ void _initLogging(List<String> argv) {
175191

176192
void _initLicenses() async {
177193
LicenseRegistry.addLicense(() async* {
178-
final python = await rootBundle.loadString('assets/licenses/raw/python.txt');
194+
final python =
195+
await rootBundle.loadString('assets/licenses/raw/python.txt');
179196
yield LicenseEntryWithLineBreaks(['Python'], python);
180197

181-
final zxingcpp = await rootBundle.loadString('assets/licenses/raw/apache-2.0.txt');
198+
final zxingcpp =
199+
await rootBundle.loadString('assets/licenses/raw/apache-2.0.txt');
182200
yield LicenseEntryWithLineBreaks(['zxing-cpp'], zxingcpp);
183201

184202
final helper = await rootBundle.loadStructuredData<List>(

lib/desktop/rpc.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ class RpcSession {
121121
);
122122
}
123123
} catch (e) {
124-
_log.error(e.toString(), entry);
124+
_log.error(entry);
125125
}
126126
}
127127

@@ -235,7 +235,7 @@ class RpcSession {
235235
return request.completer.future;
236236
}
237237

238-
void setLogLevel(Level level) async {
238+
Future<void> setLogLevel(Level level) async {
239239
final name = Levels.LEVELS
240240
.firstWhere((e) => level.value <= e.value, orElse: () => Level.OFF)
241241
.name

0 commit comments

Comments
 (0)