Add screen to let a returning user enter her existing passphrase
This commit is contained in:
parent
9220f2bef8
commit
7a34da5588
5 changed files with 147 additions and 30 deletions
|
@ -134,6 +134,6 @@ class Configuration {
|
|||
}
|
||||
|
||||
bool hasConfiguredAccount() {
|
||||
return getEndpoint() != null && getToken() != null;
|
||||
return getToken() != null && getKey() != null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,14 @@ import 'dart:async';
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/events/user_authenticated_event.dart';
|
||||
import 'package:photos/file_repository.dart';
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
import 'package:photos/ui/email_entry_page.dart';
|
||||
import 'package:photos/ui/ott_verification_page.dart';
|
||||
import 'package:photos/ui/passphrase_entry_page.dart';
|
||||
import 'package:photos/ui/passphrase_reentry_page.dart';
|
||||
import 'package:photos/ui/share_folder_widget.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/file_util.dart';
|
||||
|
@ -40,14 +44,25 @@ class GalleryAppBarWidget extends StatefulWidget
|
|||
}
|
||||
|
||||
class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
||||
StreamSubscription _userAuthEventSubscription;
|
||||
@override
|
||||
void initState() {
|
||||
widget.selectedFiles.addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
_userAuthEventSubscription =
|
||||
Bus.instance.on<UserAuthenticatedEvent>().listen((event) {
|
||||
setState(() {});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_userAuthEventSubscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.selectedFiles.files.isEmpty) {
|
||||
|
@ -73,11 +88,28 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
List<Widget> actions = List<Widget>();
|
||||
if (!Configuration.instance.hasConfiguredAccount()) {
|
||||
actions.add(IconButton(
|
||||
icon: Icon(Configuration.instance.hasConfiguredAccount()
|
||||
? Icons.sync_problem
|
||||
: Icons.sync_disabled),
|
||||
icon: Icon(Icons.sync_disabled),
|
||||
onPressed: () {
|
||||
_navigateToSignInPage(context);
|
||||
var page;
|
||||
if (Configuration.instance.getToken() == null) {
|
||||
page = EmailEntryPage();
|
||||
} else {
|
||||
// No key
|
||||
if (Configuration.instance.getEncryptedKey() != null) {
|
||||
// Yet to decrypt the key
|
||||
page = PassphraseReentryPage();
|
||||
} else {
|
||||
// Never had a key
|
||||
page = PassphraseEntryPage();
|
||||
}
|
||||
}
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return page;
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
));
|
||||
} else if (widget.type == GalleryAppBarType.local_folder &&
|
||||
|
@ -169,14 +201,4 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
void _clearSelectedFiles() {
|
||||
widget.selectedFiles.clearAll();
|
||||
}
|
||||
|
||||
void _navigateToSignInPage(BuildContext context) {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return EmailEntryPage();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ class _PassphraseEntryPageState extends State<PassphraseEntryPage> {
|
|||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: Icon(Icons.lock),
|
||||
title: Text("Encryption Passphrase"),
|
||||
),
|
||||
body: _getBody(),
|
||||
|
|
94
lib/ui/passphrase_reentry_page.dart
Normal file
94
lib/ui/passphrase_reentry_page.dart
Normal file
|
@ -0,0 +1,94 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/events/user_authenticated_event.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
|
||||
class PassphraseReentryPage extends StatefulWidget {
|
||||
PassphraseReentryPage({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_PassphraseReentryPageState createState() => _PassphraseReentryPageState();
|
||||
}
|
||||
|
||||
class _PassphraseReentryPageState extends State<PassphraseReentryPage> {
|
||||
final _passphraseController = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: Icon(Icons.lock),
|
||||
title: Text(
|
||||
"Encryption Passphrase",
|
||||
),
|
||||
),
|
||||
body: _getBody(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _getBody() {
|
||||
return SingleChildScrollView(
|
||||
child: Container(
|
||||
padding: EdgeInsets.fromLTRB(16, 40, 16, 16),
|
||||
child: Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
"assets/vault.svg",
|
||||
width: 196,
|
||||
height: 196,
|
||||
),
|
||||
Padding(padding: EdgeInsets.all(20)),
|
||||
Text(
|
||||
"Please enter your passphrase.",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Padding(padding: EdgeInsets.all(12)),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
hintText: "that thing you promised to never forget",
|
||||
contentPadding: EdgeInsets.all(20),
|
||||
),
|
||||
controller: _passphraseController,
|
||||
autofocus: false,
|
||||
autocorrect: false,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
onChanged: (_) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
Padding(padding: EdgeInsets.all(12)),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: RaisedButton(
|
||||
onPressed: _passphraseController.text.isNotEmpty
|
||||
? () async {
|
||||
final dialog =
|
||||
createProgressDialog(context, "Please wait...");
|
||||
await dialog.show();
|
||||
await Configuration.instance
|
||||
.decryptEncryptedKey(_passphraseController.text);
|
||||
await dialog.hide();
|
||||
Bus.instance.fire(UserAuthenticatedEvent());
|
||||
Navigator.of(context)
|
||||
.popUntil((route) => route.isFirst);
|
||||
}
|
||||
: null,
|
||||
padding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
|
||||
child: Text("Set Passphrase"),
|
||||
color: Theme.of(context).buttonColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18.0),
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -8,7 +8,9 @@ import 'package:photos/core/event_bus.dart';
|
|||
import 'package:photos/events/user_authenticated_event.dart';
|
||||
import 'package:photos/ui/ott_verification_page.dart';
|
||||
import 'package:photos/ui/passphrase_entry_page.dart';
|
||||
import 'package:photos/ui/passphrase_reentry_page.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/toast_util.dart';
|
||||
|
||||
class UserAuthenticator {
|
||||
final _dio = Dio();
|
||||
|
@ -29,11 +31,9 @@ class UserAuthenticator {
|
|||
},
|
||||
).catchError((e) async {
|
||||
_logger.severe(e);
|
||||
await dialog.hide();
|
||||
showGenericErrorDialog(context);
|
||||
}).then((response) async {
|
||||
await dialog.hide();
|
||||
if (response.statusCode == 200) {
|
||||
if (response != null && response.statusCode == 200) {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
|
@ -58,25 +58,25 @@ class UserAuthenticator {
|
|||
},
|
||||
).catchError((e) async {
|
||||
_logger.severe(e);
|
||||
await dialog.hide();
|
||||
showGenericErrorDialog(context);
|
||||
}).then((response) async {
|
||||
await dialog.hide();
|
||||
if (response != null && response.statusCode == 200) {
|
||||
_saveConfiguration(response);
|
||||
showToast("Email verification successful!");
|
||||
var page;
|
||||
if (Configuration.instance.getEncryptedKey() != null) {
|
||||
// TODO: Passphrase re-enter to decrypt
|
||||
Bus.instance.fire(UserAuthenticatedEvent());
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
page = PassphraseReentryPage();
|
||||
} else {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return PassphraseEntryPage();
|
||||
},
|
||||
),
|
||||
);
|
||||
page = PassphraseEntryPage();
|
||||
}
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return page;
|
||||
},
|
||||
),
|
||||
(route) => route.isFirst,
|
||||
);
|
||||
} else {
|
||||
showErrorDialog(
|
||||
context, "Oops.", "Verification failed, please try again.");
|
||||
|
|
Loading…
Add table
Reference in a new issue