yubioath-flutter/lib/app/views/main_page.dart

186 lines
6.7 KiB
Dart
Raw Normal View History

2022-10-04 13:12:54 +03:00
/*
2023-01-20 15:21:30 +03:00
* Copyright (C) 2022-2023 Yubico.
2022-10-04 13:12:54 +03:00
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
2021-11-24 10:44:28 +03:00
import 'package:flutter/material.dart';
2023-02-28 13:34:29 +03:00
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
2023-10-18 16:34:31 +03:00
import 'package:flutter_riverpod/flutter_riverpod.dart';
2024-03-08 11:30:47 +03:00
import 'package:material_symbols_icons/symbols.dart';
2023-10-18 16:34:31 +03:00
2023-02-28 13:34:29 +03:00
import '../../android/app_methods.dart';
import '../../android/state.dart';
2022-12-20 16:14:14 +03:00
import '../../core/state.dart';
2024-01-22 18:14:14 +03:00
import '../../fido/views/fingerprints_screen.dart';
import '../../fido/views/passkeys_screen.dart';
import '../../fido/views/webauthn_page.dart';
2024-01-26 12:36:10 +03:00
import '../../management/views/management_screen.dart';
2021-11-24 10:44:28 +03:00
import '../../oath/views/oath_screen.dart';
2024-02-05 19:07:35 +03:00
import '../../oath/views/utils.dart';
2023-12-13 21:35:17 +03:00
import '../../otp/views/otp_screen.dart';
2023-04-27 10:13:38 +03:00
import '../../piv/views/piv_screen.dart';
2024-01-26 12:36:10 +03:00
import '../message.dart';
2022-11-08 13:50:36 +03:00
import '../models.dart';
import '../state.dart';
import 'device_error_screen.dart';
import 'message_page.dart';
2021-11-24 10:44:28 +03:00
class MainPage extends ConsumerWidget {
2022-05-12 10:56:55 +03:00
const MainPage({super.key});
2021-11-24 10:44:28 +03:00
@override
Widget build(BuildContext context, WidgetRef ref) {
2023-02-28 13:34:29 +03:00
final l10n = AppLocalizations.of(context)!;
ref.listen<Function(BuildContext)?>(
contextConsumer,
(previous, next) {
next?.call(context);
},
);
2023-02-08 19:12:49 +03:00
if (isAndroid) {
2023-02-28 13:34:29 +03:00
isNfcEnabled().then((value) =>
ref.read(androidNfcStateProvider.notifier).setNfcEnabled(value));
2023-02-08 19:12:49 +03:00
}
// If the current device changes, we need to pop any open dialogs.
ref.listen<AsyncValue<YubiKeyData>>(currentDeviceDataProvider, (_, __) {
Navigator.of(context).popUntil((route) {
return route.isFirst ||
[
'device_picker',
'settings',
'about',
'licenses',
2022-09-13 13:55:17 +03:00
'user_interaction_prompt',
'oath_add_account',
'oath_icon_pack_dialog',
'android_qr_scanner_view',
].contains(route.settings.name);
});
});
final deviceNode = ref.watch(currentDeviceProvider);
2022-11-07 17:44:35 +03:00
final noKeyImage = Image.asset(
2023-12-20 17:44:43 +03:00
'assets/graphics/no-key.png',
2022-11-07 17:44:35 +03:00
filterQuality: FilterQuality.medium,
scale: 2,
2023-12-20 17:09:31 +03:00
color: Theme.of(context).colorScheme.primary,
2022-11-07 17:44:35 +03:00
);
if (deviceNode == null) {
if (isAndroid) {
2023-02-08 19:12:49 +03:00
var hasNfcSupport = ref.watch(androidNfcSupportProvider);
var isNfcEnabled = ref.watch(androidNfcStateProvider);
return MessagePage(
centered: true,
2022-11-07 17:44:35 +03:00
graphic: noKeyImage,
2024-02-01 11:01:20 +03:00
header: hasNfcSupport && isNfcEnabled
2023-02-28 17:02:12 +03:00
? l10n.l_insert_or_tap_yk
: l10n.l_insert_yk,
actionsBuilder: (context, expanded) => [
2023-02-09 12:30:07 +03:00
if (hasNfcSupport && !isNfcEnabled)
ElevatedButton.icon(
label: Text(l10n.s_enable_nfc),
2024-03-08 11:30:47 +03:00
icon: const Icon(Symbols.contactless),
2023-02-09 12:30:07 +03:00
onPressed: () async {
await openNfcSettings();
})
],
actionButtonBuilder: (context) => IconButton(
2024-03-08 11:30:47 +03:00
icon: const Icon(Symbols.person_add_alt),
tooltip: l10n.s_add_account,
onPressed: () async {
2024-02-05 19:07:35 +03:00
await addOathAccount(context, ref);
},
2022-09-13 13:55:17 +03:00
),
);
} else {
2022-11-07 17:44:35 +03:00
return MessagePage(
centered: true,
2023-12-20 17:09:31 +03:00
delayedContent: false,
2022-11-07 17:44:35 +03:00
graphic: noKeyImage,
2023-12-20 17:09:31 +03:00
header: l10n.l_insert_yk,
2022-11-07 17:44:35 +03:00
);
}
} else {
return ref.watch(currentDeviceDataProvider).when(
data: (data) {
final app = ref.watch(currentAppProvider);
final capabilities = app.capabilities;
2023-01-20 17:34:24 +03:00
if (data.info.supportedCapabilities.isEmpty &&
2023-01-20 15:21:30 +03:00
data.name == 'Unrecognized device') {
2023-02-28 13:34:29 +03:00
return MessagePage(
2024-01-26 12:36:10 +03:00
centered: true,
graphic: Icon(
2024-03-08 11:30:47 +03:00
Symbols.help,
2024-01-26 12:36:10 +03:00
size: 96,
color: Theme.of(context).colorScheme.error,
),
header: l10n.s_yk_not_recognized,
2023-01-20 15:21:30 +03:00
);
} else if (app.getAvailability(data) ==
Availability.unsupported) {
2022-08-04 09:24:12 +03:00
return MessagePage(
2024-01-26 12:36:10 +03:00
title: app.getDisplayName(l10n),
2024-01-26 13:59:37 +03:00
capabilities: capabilities,
header: l10n.s_app_not_supported,
2024-01-26 13:59:37 +03:00
message: l10n.l_app_not_supported_on_yk(capabilities
.map((c) => c.getDisplayName(l10n))
.join(',')),
2023-02-09 12:14:31 +03:00
);
2022-08-04 09:24:12 +03:00
} else if (app.getAvailability(data) != Availability.enabled) {
return MessagePage(
2024-01-26 12:36:10 +03:00
title: app.getDisplayName(l10n),
2024-01-26 13:59:37 +03:00
capabilities: capabilities,
header: l10n.s_app_disabled,
2024-01-26 13:59:37 +03:00
message: l10n.l_app_disabled_desc(capabilities
.map((c) => c.getDisplayName(l10n))
.join(',')),
actionsBuilder: (context, expanded) => [
2024-01-26 12:36:10 +03:00
ActionChip(
label: Text(data.info.version.major > 4
? l10n.s_toggle_applications
: l10n.s_toggle_interfaces),
onPressed: () async {
await showBlurDialog(
context: context,
builder: (context) => ManagementScreen(data),
);
},
2024-03-08 11:30:47 +03:00
avatar: const Icon(Symbols.construction),
2024-01-26 12:36:10 +03:00
)
],
2023-02-09 12:14:31 +03:00
);
}
2023-05-22 12:52:49 +03:00
return switch (app) {
Application.accounts => OathScreen(data.node.path),
2024-01-22 18:14:14 +03:00
Application.webauthn => const WebAuthnScreen(),
Application.passkeys => PasskeysScreen(data),
Application.fingerprints => FingerprintsScreen(data),
Application.certificates => PivScreen(data.node.path),
Application.slots => OtpScreen(data.node.path),
2023-05-22 12:52:49 +03:00
_ => MessagePage(
header: l10n.s_app_not_supported,
2023-02-28 17:02:12 +03:00
message: l10n.l_app_not_supported_desc,
2023-05-22 12:52:49 +03:00
),
};
},
loading: () => DeviceErrorScreen(deviceNode),
error: (error, _) => DeviceErrorScreen(deviceNode, error: error),
);
2021-11-24 10:44:28 +03:00
}
}
}