ente/lib/ui/account/two_factor_setup_page.dart

289 lines
8.3 KiB
Dart
Raw Normal View History

import 'dart:async';
2021-06-29 09:48:01 +00:00
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:photos/core/configuration.dart';
2022-07-12 06:30:02 +00:00
import 'package:photos/ente_theme_data.dart';
2021-06-29 09:48:01 +00:00
import 'package:photos/services/user_service.dart';
import 'package:photos/ui/account/recovery_key_page.dart';
2021-06-29 10:31:27 +00:00
import 'package:photos/ui/lifecycle_event_handler.dart';
2023-02-03 04:41:45 +00:00
import 'package:photos/utils/crypto_util.dart';
2022-04-06 17:51:08 +00:00
import 'package:photos/utils/navigation_util.dart';
2021-06-29 09:48:01 +00:00
import 'package:photos/utils/toast_util.dart';
import 'package:pinput/pin_put/pin_put.dart';
class TwoFactorSetupPage extends StatefulWidget {
final String secretCode;
final String qrCode;
final Completer completer;
2021-06-29 09:48:01 +00:00
const TwoFactorSetupPage(
this.secretCode,
this.qrCode,
this.completer, {
2022-12-28 06:30:48 +00:00
Key? key,
}) : super(key: key);
2021-06-29 09:48:01 +00:00
@override
2022-07-03 09:45:00 +00:00
State<TwoFactorSetupPage> createState() => _TwoFactorSetupPageState();
2021-06-29 09:48:01 +00:00
}
class _TwoFactorSetupPageState extends State<TwoFactorSetupPage>
with SingleTickerProviderStateMixin {
2022-12-28 06:30:48 +00:00
late TabController _tabController;
2021-06-29 09:48:01 +00:00
final _pinController = TextEditingController();
final _pinPutDecoration = BoxDecoration(
2022-07-04 06:02:17 +00:00
border: Border.all(color: const Color.fromRGBO(45, 194, 98, 1.0)),
2021-06-29 09:48:01 +00:00
borderRadius: BorderRadius.circular(15.0),
);
String _code = "";
2022-12-28 06:30:48 +00:00
late ImageProvider _imageProvider;
late LifecycleEventHandler _lifecycleEventHandler;
2021-06-29 09:48:01 +00:00
@override
void initState() {
_tabController = TabController(length: 2, vsync: this);
2021-06-29 10:10:53 +00:00
_imageProvider = Image.memory(
2023-02-03 04:41:45 +00:00
CryptoUtil.base642bin(widget.qrCode),
2021-06-29 10:10:53 +00:00
height: 180,
width: 180,
).image;
2021-06-29 10:31:27 +00:00
_lifecycleEventHandler = LifecycleEventHandler(
resumeCallBack: () async {
if (mounted) {
final data = await Clipboard.getData(Clipboard.kTextPlain);
2022-12-28 06:30:48 +00:00
if (data != null && data.text != null && data.text!.length == 6) {
_pinController.text = data.text!;
2021-06-29 10:31:27 +00:00
}
}
},
);
WidgetsBinding.instance.addObserver(_lifecycleEventHandler);
2021-06-29 09:48:01 +00:00
super.initState();
}
2021-06-29 10:31:27 +00:00
@override
void dispose() {
WidgetsBinding.instance.removeObserver(_lifecycleEventHandler);
widget.completer.isCompleted ? null : widget.completer.complete();
2021-06-29 10:31:27 +00:00
super.dispose();
}
2021-06-29 09:48:01 +00:00
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
2022-07-04 06:02:17 +00:00
title: const Text(
2022-04-28 05:49:23 +00:00
"Two-factor setup",
2021-06-29 09:48:01 +00:00
),
),
body: _getBody(),
);
}
Widget _getBody() {
return SingleChildScrollView(
reverse: true,
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
2021-06-29 09:48:01 +00:00
height: 360,
child: Column(
children: [
TabBar(
2022-07-12 06:30:02 +00:00
labelColor: Theme.of(context).colorScheme.greenAlternative,
2021-06-29 09:48:01 +00:00
unselectedLabelColor: Colors.grey,
2022-04-28 05:49:23 +00:00
tabs: const [
2021-06-29 09:48:01 +00:00
Tab(
2022-04-28 05:49:23 +00:00
text: "Enter code",
2021-06-29 09:48:01 +00:00
),
Tab(
2022-04-28 05:49:23 +00:00
text: "Scan code",
2021-06-29 09:48:01 +00:00
)
],
controller: _tabController,
indicatorSize: TabBarIndicatorSize.tab,
),
Expanded(
child: TabBarView(
2022-07-03 09:45:00 +00:00
controller: _tabController,
2021-06-29 09:48:01 +00:00
children: [
_getSecretCode(),
_getBarCode(),
2021-06-29 09:48:01 +00:00
],
),
),
],
),
),
Divider(
height: 1,
thickness: 1,
2022-08-29 14:43:31 +00:00
color: Theme.of(context).colorScheme.secondary,
2021-06-29 09:48:01 +00:00
),
_getVerificationWidget(),
],
),
),
);
}
Widget _getSecretCode() {
2022-08-29 14:43:31 +00:00
final Color textColor = Theme.of(context).colorScheme.onSurface;
2021-06-29 09:48:01 +00:00
return GestureDetector(
onTap: () async {
await Clipboard.setData(ClipboardData(text: widget.secretCode));
showShortToast(context, "Code copied to clipboard");
2021-06-29 09:48:01 +00:00
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
2022-07-04 06:02:17 +00:00
const Padding(padding: EdgeInsets.all(12)),
const Text(
"Copy-paste this code\nto your authenticator app",
style: TextStyle(
height: 1.4,
fontSize: 16,
2021-06-29 09:48:01 +00:00
),
textAlign: TextAlign.center,
),
2022-07-04 06:02:17 +00:00
const Padding(padding: EdgeInsets.all(16)),
Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Container(
2022-07-04 06:02:17 +00:00
padding: const EdgeInsets.all(16),
2022-07-03 09:45:00 +00:00
color: textColor.withOpacity(0.1),
child: Center(
child: Text(
widget.secretCode,
style: TextStyle(
fontSize: 15,
fontFeatures: const [FontFeature.tabularFigures()],
color: textColor.withOpacity(0.7),
2021-06-29 09:48:01 +00:00
),
),
),
),
),
2022-07-04 06:02:17 +00:00
const Padding(padding: EdgeInsets.all(6)),
Text(
"tap to copy",
style: TextStyle(color: textColor.withOpacity(0.5)),
)
],
2021-06-29 09:48:01 +00:00
),
);
}
Widget _getBarCode() {
return Center(
child: Column(
children: [
2022-07-04 06:02:17 +00:00
const Padding(padding: EdgeInsets.all(12)),
const Text(
"Scan this barcode with\nyour authenticator app",
style: TextStyle(
height: 1.4,
fontSize: 16,
2021-06-29 09:48:01 +00:00
),
textAlign: TextAlign.center,
),
2022-07-04 06:02:17 +00:00
const Padding(padding: EdgeInsets.all(12)),
Image(
image: _imageProvider,
height: 180,
width: 180,
),
],
2021-06-29 09:48:01 +00:00
),
);
}
Widget _getVerificationWidget() {
return Column(
children: [
2022-07-04 06:02:17 +00:00
const Padding(padding: EdgeInsets.all(12)),
const Text(
2022-04-28 05:49:23 +00:00
"Enter the 6-digit code from\nyour authenticator app",
2021-06-29 09:48:01 +00:00
style: TextStyle(
height: 1.4,
fontSize: 16,
),
textAlign: TextAlign.center,
),
2022-07-04 06:02:17 +00:00
const Padding(padding: EdgeInsets.all(16)),
2021-06-29 09:48:01 +00:00
Padding(
padding: const EdgeInsets.fromLTRB(40, 0, 40, 0),
child: PinPut(
fieldsCount: 6,
onSubmit: (String code) {
_enableTwoFactor(code);
2021-06-29 09:48:01 +00:00
},
onChanged: (String pin) {
setState(() {
_code = pin;
});
},
controller: _pinController,
submittedFieldDecoration: _pinPutDecoration.copyWith(
borderRadius: BorderRadius.circular(20.0),
),
selectedFieldDecoration: _pinPutDecoration,
followingFieldDecoration: _pinPutDecoration.copyWith(
borderRadius: BorderRadius.circular(5.0),
border: Border.all(
2022-07-04 06:02:17 +00:00
color: const Color.fromRGBO(45, 194, 98, 0.5),
2021-06-29 09:48:01 +00:00
),
),
2022-07-04 06:02:17 +00:00
inputDecoration: const InputDecoration(
2021-06-29 09:48:01 +00:00
focusedBorder: InputBorder.none,
border: InputBorder.none,
counterText: '',
),
),
),
2022-07-04 06:02:17 +00:00
const Padding(padding: EdgeInsets.all(24)),
OutlinedButton(
onPressed: _code.length == 6
? () async {
_enableTwoFactor(_code);
}
: null,
2022-07-04 06:02:17 +00:00
child: const Text("Confirm"),
2021-06-29 09:48:01 +00:00
),
2022-07-04 06:02:17 +00:00
const Padding(padding: EdgeInsets.only(bottom: 24)),
2021-06-29 09:48:01 +00:00
],
);
}
Future<void> _enableTwoFactor(String code) async {
2022-07-03 09:49:33 +00:00
final success = await UserService.instance
.enableTwoFactor(context, widget.secretCode, code);
if (success) {
2022-04-06 17:51:08 +00:00
_showSuccessPage();
}
}
2022-04-06 17:51:08 +00:00
void _showSuccessPage() {
2023-02-03 04:41:45 +00:00
final recoveryKey =
CryptoUtil.bin2hex(Configuration.instance.getRecoveryKey());
2022-04-06 17:51:08 +00:00
routeToPage(
2022-06-11 08:23:52 +00:00
context,
RecoveryKeyPage(
recoveryKey,
"OK",
showAppBar: true,
onDone: () {},
2022-11-13 08:07:06 +00:00
title: "Setup complete",
text: "Save your recovery key if you haven't already",
2022-07-03 09:49:33 +00:00
subText:
2022-11-13 08:07:06 +00:00
"This can be used to recover your account if you lose your second factor",
2022-06-11 08:23:52 +00:00
),
);
2021-06-29 09:48:01 +00:00
}
}