Skip to content

Commit

Permalink
fix: add biometry for modal, autofocus on sercure text field (#498)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrey Malochka <andrey.molocko2018@gmail.com>
  • Loading branch information
AndreyMolochko and Andrey Malochka authored Sep 13, 2024
1 parent c8345b6 commit 0a94d92
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import 'package:app/generated/generated.dart';
import 'package:elementary/elementary.dart';
import 'package:elementary_helper/elementary_helper.dart';
import 'package:flutter/cupertino.dart';
import 'package:local_auth/local_auth.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:nekoton_repository/nekoton_repository.dart';
import 'package:ui_components_lib/v2/ui_components_lib_v2.dart';
import 'package:ui_components_lib/v2/widgets/modals/primary_bottom_sheet.dart';
Expand Down Expand Up @@ -61,6 +63,7 @@ class ContentConfirmAction extends ElementaryWidget<ConfirmActionWidgetModel> {
SecureTextField(
textEditingController: wm.passwordController,
validator: (_) => data?.error,
isAutofocus: true,
hintText: LocaleKeys.enterYourPassword.tr(),
),
const SizedBox(height: DimensSizeV2.d28),
Expand All @@ -70,6 +73,36 @@ class ContentConfirmAction extends ElementaryWidget<ConfirmActionWidgetModel> {
isLoading: data?.isLoading ?? false,
onPressed: wm.onClickConfirm,
),
StateNotifierBuilder(
listenableState: wm.availableBiometry,
builder: (_, value) {
if (value?.contains(BiometricType.face) ?? false) {
return Padding(
padding: const EdgeInsets.only(top: DimensSizeV2.d8),
child: PrimaryButton(
buttonShape: ButtonShape.pill,
title: LocaleKeys.useFaceID.tr(),
icon: LucideIcons.scanFace,
onPressed: wm.onUseBiometry,
),
);
}

if (value?.contains(BiometricType.fingerprint) ?? false) {
return Padding(
padding: const EdgeInsets.only(top: DimensSizeV2.d8),
child: PrimaryButton(
buttonShape: ButtonShape.pill,
title: LocaleKeys.useFingerprint.tr(),
icon: LucideIcons.fingerprint,
onPressed: wm.onUseBiometry,
),
);
}

return const SizedBox.shrink();
},
),
],
);
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import 'package:app/app/service/biometry_service.dart';
import 'package:app/app/service/current_seed_service.dart';
import 'package:app/app/service/messenger/message.dart';
import 'package:app/app/service/messenger/service/messenger_service.dart';
import 'package:app/generated/generated.dart';
import 'package:elementary/elementary.dart';
import 'package:local_auth/local_auth.dart';
import 'package:nekoton_repository/nekoton_repository.dart' hide Message;
import 'package:ui_components_lib/ui_components_lib.dart';

class ConfirmActionModel extends ElementaryModel {
ConfirmActionModel(
ErrorHandler errorHandler,
this.account,
this.biometryService,
this.nekotonRepository,
this.currentSeedService,
this.messengerService,
) : super(errorHandler: errorHandler);

final NekotonRepository nekotonRepository;
final BiometryService biometryService;
final CurrentSeedService currentSeedService;
final MessengerService messengerService;
final KeyAccount? account;
Expand All @@ -24,6 +29,30 @@ class ConfirmActionModel extends ElementaryModel {
Seed? findSeed(PublicKey publicKey) =>
nekotonRepository.seedList.findSeed(publicKey);

Future<List<BiometricType>> getAvailableBiometry(PublicKey publicKey) async {
final isBiometryEnabled = biometryService.enabled;
final hasKeyPassword = await biometryService.hasKeyPassword(publicKey);

if (isBiometryEnabled && hasKeyPassword) {
return biometryService.getAvailableBiometry();
}

return [];
}

Future<String?> requestBiometry(PublicKey publicKey) async {
try {
final password = await biometryService.getKeyPassword(
publicKey: publicKey,
localizedReason: LocaleKeys.biometryAuthReason.tr(),
);

return password;
} catch (_) {
return null;
}
}

void showValidateError(String message) {
messengerService.show(
Message.error(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import 'package:app/feature/wallet/wallet_backup/confirm_action/confirm_action_d
import 'package:app/feature/wallet/wallet_backup/confirm_action/confirm_action_model.dart';
import 'package:app/feature/wallet/wallet_backup/manual_backup/manual_back_up_dialog.dart';
import 'package:app/generated/generated.dart';
import 'package:elementary_helper/elementary_helper.dart';
import 'package:flutter/widgets.dart';
import 'package:local_auth/local_auth.dart';
import 'package:nekoton_repository/nekoton_repository.dart';
import 'package:ui_components_lib/v2/ui_components_lib_v2.dart';

Expand All @@ -27,6 +29,7 @@ ConfirmActionWidgetModel defaultConfirmActionWidgetModelFactory(
inject(),
inject(),
inject(),
inject(),
),
);
}
Expand All @@ -44,6 +47,11 @@ class ConfirmActionWidgetModel

KeyAccount? get account => model.account;

ListenableState<List<BiometricType>> get availableBiometry =>
_availableBiometry;

late final _availableBiometry = createNotifier<List<BiometricType>>();

late final screenState = createEntityNotifier<ConfirmActionData>()
..loading(ConfirmActionData());

Expand All @@ -52,6 +60,7 @@ class ConfirmActionWidgetModel
@override
void initWidgetModel() {
passwordController.addListener(_resetError);
_getAvailableBiometry();
super.initWidgetModel();
}

Expand All @@ -63,6 +72,26 @@ class ConfirmActionWidgetModel
}
}

Future<void> onUseBiometry() async {
//final publicKey = _currentAccount.value?.publicKey;
final publicKey = model.currentSeed?.publicKey;
if (publicKey != null) {
final password = await model.requestBiometry(publicKey);

if (password != null) {
await _export(publicKey, password);
}
}
}

Future<void> _getAvailableBiometry() async {
final publicKey = model.currentSeed?.publicKey;
if (publicKey != null) {
final available = await model.getAvailableBiometry(publicKey);
_availableBiometry.accept(available);
}
}

void _resetError() {
screenState.content(ConfirmActionData());
}
Expand Down

0 comments on commit 0a94d92

Please sign in to comment.