ente/lib/ui/two_factor_setup_page.dart
Neeraj Gupta 4d2d9a090f Revert "Make scan qr code as default option during 2FA setup"
On mobile, entering the code would be easiest.

This reverts commit a015cf2152.
2021-07-07 11:12:47 +05:30

291 lines
8.3 KiB
Dart

import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_sodium/flutter_sodium.dart';
import 'package:logging/logging.dart';
import 'package:photos/core/configuration.dart';
import 'package:photos/services/user_service.dart';
import 'package:photos/ui/common_elements.dart';
import 'package:photos/ui/lifecycle_event_handler.dart';
import 'package:photos/ui/recovery_key_dialog.dart';
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;
TwoFactorSetupPage(this.secretCode, this.qrCode, {Key key}) : super(key: key);
@override
_TwoFactorSetupPageState createState() => _TwoFactorSetupPageState();
}
class _TwoFactorSetupPageState extends State<TwoFactorSetupPage>
with SingleTickerProviderStateMixin {
TabController _tabController;
final _pinController = TextEditingController();
final _pinPutDecoration = BoxDecoration(
border: Border.all(color: Color.fromRGBO(45, 194, 98, 1.0)),
borderRadius: BorderRadius.circular(15.0),
);
String _code = "";
ImageProvider _imageProvider;
LifecycleEventHandler _lifecycleEventHandler;
@override
void initState() {
_tabController = new TabController(length: 2, vsync: this);
_imageProvider = Image.memory(
Sodium.base642bin(widget.qrCode),
height: 180,
width: 180,
).image;
_lifecycleEventHandler = LifecycleEventHandler(
resumeCallBack: () async {
if (mounted) {
final data = await Clipboard.getData(Clipboard.kTextPlain);
if (data != null && data.text != null && data.text.length == 6) {
_pinController.text = data.text;
}
}
},
);
WidgetsBinding.instance.addObserver(_lifecycleEventHandler);
super.initState();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(_lifecycleEventHandler);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"two-factor setup",
),
),
body: _getBody(),
);
}
Widget _getBody() {
return SingleChildScrollView(
reverse: true,
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 360,
child: Column(
children: [
TabBar(
labelColor: Theme.of(context).buttonColor,
unselectedLabelColor: Colors.grey,
tabs: [
Tab(
text: "enter code",
),
Tab(
text: "scan code",
)
],
controller: _tabController,
indicatorSize: TabBarIndicatorSize.tab,
),
Expanded(
child: TabBarView(
children: [
_getSecretCode(),
_getBarCode(),
],
controller: _tabController,
),
),
],
),
),
Divider(
height: 1,
thickness: 1,
color: Theme.of(context).accentColor,
),
_getVerificationWidget(),
],
),
),
);
}
Widget _getSecretCode() {
return GestureDetector(
onTap: () async {
await Clipboard.setData(new ClipboardData(text: widget.secretCode));
showToast("code copied to clipboard");
},
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(padding: EdgeInsets.all(12)),
Text(
"copy-paste this code\nto your authenticator app",
style: TextStyle(
height: 1.4,
fontSize: 16,
),
textAlign: TextAlign.center,
),
Padding(padding: EdgeInsets.all(16)),
Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Container(
padding: EdgeInsets.all(16),
child: Center(
child: Text(
widget.secretCode,
style: TextStyle(
fontSize: 15,
fontFeatures: [FontFeature.tabularFigures()],
color: Colors.white.withOpacity(0.7),
),
),
),
color: Colors.white.withOpacity(0.1),
),
),
Padding(padding: EdgeInsets.all(6)),
Text(
"tap to copy",
style: TextStyle(color: Colors.white.withOpacity(0.5)),
)
],
),
),
);
}
Widget _getBarCode() {
return Container(
child: Center(
child: Column(
children: [
Padding(padding: EdgeInsets.all(12)),
Text(
"scan this barcode with\nyour authenticator app",
style: TextStyle(
height: 1.4,
fontSize: 16,
),
textAlign: TextAlign.center,
),
Padding(padding: EdgeInsets.all(12)),
Image(
image: _imageProvider,
height: 180,
width: 180,
),
],
),
),
);
}
Widget _getVerificationWidget() {
return Column(
children: [
Padding(padding: EdgeInsets.all(12)),
Text(
"enter the 6-digit code from\nyour authenticator app",
style: TextStyle(
height: 1.4,
fontSize: 16,
),
textAlign: TextAlign.center,
),
Padding(padding: EdgeInsets.all(16)),
Padding(
padding: const EdgeInsets.fromLTRB(40, 0, 40, 0),
child: PinPut(
fieldsCount: 6,
onSubmit: (String code) {
_enableTwoFactor(code);
},
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(
color: Color.fromRGBO(45, 194, 98, 0.5),
),
),
inputDecoration: InputDecoration(
focusedBorder: InputBorder.none,
border: InputBorder.none,
counterText: '',
),
),
),
Padding(padding: EdgeInsets.all(24)),
Container(
width: 180,
height: 50,
child: button(
"confirm",
fontSize: 18,
padding: EdgeInsets.all(0),
onPressed: _code.length == 6
? () async {
_enableTwoFactor(_code);
}
: null,
),
),
Padding(padding: EdgeInsets.only(bottom: 24)),
],
);
}
Future<void> _enableTwoFactor(String code) async {
final success = await UserService.instance
.enableTwoFactor(context, widget.secretCode, code);
if (success) {
_showSuccessDialog();
}
}
void _showSuccessDialog() {
final recoveryKey = Sodium.bin2hex(Configuration.instance.getRecoveryKey());
showDialog(
context: context,
builder: (BuildContext context) {
return RecoveryKeyDialog(
recoveryKey,
"ok",
() {},
title: "⚡ setup complete",
text: "save your recovery key if you haven't already",
subText:
"this can be used to recover your account if you lose your second factor",
);
},
barrierColor: Colors.black.withOpacity(0.85),
);
}
}