actions-test/lib/screens/auth.dart
Jakob Meier 1af8d6f068
Migrated from localstore to shared preferences
(only for user, server and theme)

This was done, because localstore is somewhat inconsistent
in terms of events on different platforms.
Also storing user, server and theme using shared-preferences
should fit into flutters ecosystem a little better
2023-03-29 18:27:05 +02:00

283 lines
12 KiB
Dart

import 'package:flutter/material.dart';
import 'package:outbag_app/backend/crypto.dart';
import 'package:outbag_app/backend/request.dart';
import 'package:outbag_app/backend/user.dart';
import 'package:outbag_app/tools/fetch_wrapper.dart';
import 'package:outbag_app/tools/snackbar.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../backend/resolve_url.dart';
enum Mode {
signin,
signup,
signupOTA,
}
class AuthPage extends StatefulWidget {
final Mode mode;
Function refresh;
AuthPage({super.key, required this.mode, required this.refresh});
@override
State<StatefulWidget> createState() => _AuthPageState();
}
class _AuthPageState extends State<AuthPage> {
final TextEditingController _ctrServer = TextEditingController();
final TextEditingController _ctrUsername = TextEditingController();
final TextEditingController _ctrPassword = TextEditingController();
final TextEditingController _ctrPasswordRpt = TextEditingController();
final TextEditingController _ctrOTA = TextEditingController();
bool showSpinner = false;
@override
Widget build(BuildContext context) {
String modeName = AppLocalizations.of(context)!.signIn;
if (widget.mode != Mode.signin) {
modeName = AppLocalizations.of(context)!.signUp;
}
String modeDescription = AppLocalizations.of(context)!.logIntoAccount;
if (widget.mode != Mode.signin) {
modeDescription = AppLocalizations.of(context)!.createNewAccount;
}
final textTheme = Theme.of(context)
.textTheme
.apply(displayColor: Theme.of(context).colorScheme.onSurface);
return showSpinner
? Scaffold(
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(),
Text(AppLocalizations.of(context)!.loading,
style: textTheme.titleLarge),
])))
: Scaffold(
appBar: AppBar(
title: Text(modeName),
),
body: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 400),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8),
child: TextField(
controller: _ctrServer,
keyboardType: TextInputType.url,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.dns),
labelText: AppLocalizations.of(context)!
.inputServerLabel,
hintText:
AppLocalizations.of(context)!.inputServerHint,
helperText:
AppLocalizations.of(context)!.inputServerHelp,
border: const OutlineInputBorder(),
),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: TextField(
controller: _ctrUsername,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.person),
labelText: AppLocalizations.of(context)!
.inputUsernameLabel,
hintText: AppLocalizations.of(context)!
.inputUsernameHint,
helperText: AppLocalizations.of(context)!
.inputUsernameHelp,
border: const OutlineInputBorder(),
),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: TextField(
controller: _ctrPassword,
keyboardType: TextInputType.visiblePassword,
obscureText: true,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.lock),
labelText: AppLocalizations.of(context)!
.inputPasswordLabel,
hintText: AppLocalizations.of(context)!
.inputPasswordHint,
helperText: AppLocalizations.of(context)!
.inputPasswordHelp,
border: const OutlineInputBorder(),
),
),
),
// ONLY SIGNUP
...((widget.mode != Mode.signin)
? [
Padding(
padding: const EdgeInsets.all(8),
child: TextField(
controller: _ctrPasswordRpt,
keyboardType: TextInputType.visiblePassword,
obscureText: true,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.lock),
labelText: AppLocalizations.of(context)!
.inputPasswordRepeatLabel,
hintText: AppLocalizations.of(context)!
.inputPasswordRepeatHint,
helperText: AppLocalizations.of(context)!
.inputPasswordRepeatHelp,
border: const OutlineInputBorder(),
),
),
)
]
: []),
// ONLY SIGNUP OTA
...((widget.mode == Mode.signupOTA)
? [
Padding(
padding: const EdgeInsets.all(8),
child: TextField(
controller: _ctrOTA,
keyboardType: TextInputType.visiblePassword,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.key),
labelText: AppLocalizations.of(context)!
.inputOTALabel,
hintText: AppLocalizations.of(context)!
.inputOTAHint,
helperText: AppLocalizations.of(context)!
.inputOTAHelp,
border: const OutlineInputBorder(),
),
),
)
]
: []),
],
))),
floatingActionButton: FloatingActionButton.extended(
onPressed: () async {
setState(() {
showSpinner = true;
});
final scaffMgr = ScaffoldMessenger.of(context);
// verify that both passwords are the same
if (widget.mode != Mode.signin) {
if (_ctrPassword.text != _ctrPasswordRpt.text) {
setState(() {
showSpinner = false;
});
showSimpleSnackbar(scaffMgr,
text: AppLocalizations.of(context)!
.errorPasswordsDoNotMatch,
action: AppLocalizations.of(context)!.dismiss);
_ctrPasswordRpt.clear();
return;
}
}
// password has to be at least 6 characters long
if (_ctrPassword.text.length < 6) {
setState(() {
showSpinner = false;
});
showSimpleSnackbar(scaffMgr,
text: AppLocalizations.of(context)!.errorPasswordLength,
action: AppLocalizations.of(context)!.dismiss);
_ctrPasswordRpt.clear();
return;
}
// resolve homeserver url
OutbagServer server;
try {
server = await getOutbagServerUrl(_ctrServer.text);
} catch (e) {
// unable to find outbag server
// at given server address
// stop authentification
setState(() {
showSpinner = false;
});
showSimpleSnackbar(scaffMgr,
text: AppLocalizations.of(context)!
.errorInvalidServer(_ctrServer.text),
action: AppLocalizations.of(context)!.dismiss);
return;
}
// hash password
final password = hashPassword(_ctrPassword.text);
doNetworkRequest(scaffMgr, req: () {
if (widget.mode == Mode.signin) {
return postUnauthorized(
target: server,
path: 'signin',
body: {
'name': _ctrUsername.text,
'server': server.tag,
'accountKey': password
});
} else if (widget.mode == Mode.signup) {
return postUnauthorized(
target: server,
path: 'signup',
body: {
'name': _ctrUsername.text,
'server': server.tag,
'accountKey': password
});
} else {
// signup OTA
return postUnauthorized(
target: server,
path: 'signupOTA',
body: {
'name': _ctrUsername.text,
'server': server.tag,
'accountKey': password,
'OTA': _ctrOTA.text
});
}
}, onOK: (body) async {
// authorize user
await User(
username: _ctrUsername.text,
password: password,
server: server)
.toDisk();
widget.refresh();
}, after: () {
setState(() {
showSpinner = false;
});
});
},
label: Text(modeName),
icon: const Icon(Icons.check),
tooltip: modeDescription,
),
);
}
}