Allow users to register via the app

This commit is contained in:
Roland Geider
2021-02-11 12:14:15 +01:00
parent c0759c913e
commit e9c9ae93d6
11 changed files with 161 additions and 68 deletions

View File

@@ -25,12 +25,35 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
// Keys for the android play store
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('app/key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
// Key for wger.de REST API
def wgerProperties = new Properties()
def localMapsPropertiesFile = rootProject.file('app/wger.properties')
if (localMapsPropertiesFile.exists()) {
project.logger.info('Load maps properties from local file')
localMapsPropertiesFile.withReader('UTF-8') { reader ->
wgerProperties.load(reader)
}
} else {
project.logger.info('Load maps properties from environment')
try {
wgerProperties['WGER_API_KEY'] = System.getenv('WGER_API_KEY')
} catch(NullPointerException e) {
project.logger.warn('Failed to load WGER_API_KEY from environment.', e)
}
}
def wgerApiKey = wgerProperties.getProperty('WGER_API_KEY')
if(wgerApiKey == null){
wgerApiKey = ""
project.logger.error('Wger Api Key not configured. Set it in `app/wger.properties` or in the environment variable `WGER_API_KEY`')
}
android {
compileSdkVersion 29
@@ -49,6 +72,7 @@ android {
targetSdkVersion 29
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
manifestPlaceholders = [WGER_API_KEY: wgerApiKey]
}
signingConfigs {

View File

@@ -11,6 +11,9 @@
android:name="io.flutter.app.FlutterApplication"
android:label="wger"
android:icon="@mipmap/ic_launcher">
<meta-data android:name="wger.api_key" android:value="${WGER_API_KEY}" />
<activity
android:name=".MainActivity"
android:launchMode="singleTop"

View File

@@ -49,7 +49,6 @@ void showHttpExceptionErrorDialog(WgerHttpException exception, BuildContext cont
log(exception.toString());
log('-------------------');
//Navigator.of(context).pop();
List<Widget> errorList = [];
for (var key in exception.errors.keys) {
// Error headers
@@ -59,6 +58,7 @@ void showHttpExceptionErrorDialog(WgerHttpException exception, BuildContext cont
for (var value in exception.errors[key]) {
errorList.add(Text(value));
}
errorList.add(SizedBox(height: 8));
}
showDialog(
context: context,

View File

@@ -24,14 +24,14 @@ class WgerHttpException implements Exception {
/// Custom http exception.
/// Expects the response body of the REST call and will try to parse it to
/// JSON. Will use the response as-is if it fails.
WgerHttpException(String responseBody) {
WgerHttpException(dynamic responseBody) {
if (responseBody == null) {
errors = {'unknown_error': 'An unknown error occurred, no further information available'};
} else {
try {
errors = json.decode(responseBody);
} catch (e) {
errors = {'error': responseBody};
errors = responseBody;
}
}
this.errors = errors;

View File

@@ -19,8 +19,11 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:android_metadata/android_metadata.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
import 'package:package_info/package_info.dart';
@@ -71,7 +74,46 @@ class Auth with ChangeNotifier {
applicationVersion = packageInfo;
}
Future<void> _authenticate(String username, String password, String serverUrl) async {
/// Registers a new user
Future<void> register({String username, String password, String email, String serverUrl}) async {
var url = '$serverUrl/api/v2/register/';
Map<String, String> metadata = Map();
// Read the api key from the manifest file
try {
metadata = await AndroidMetadata.metaDataAsMap;
} on PlatformException {
throw Exception('An error occurred reading the API key');
}
// Register
try {
Map<String, String> data = {'username': username, 'password': password};
if (email != '') {
data['email'] = email;
}
final response = await http.post(
url,
headers: {
HttpHeaders.contentTypeHeader: 'application/json; charset=UTF-8',
HttpHeaders.authorizationHeader: "Token ${metadata['wger.api_key']}"
},
body: json.encode(data),
);
final responseData = json.decode(response.body);
if (response.statusCode >= 400) {
throw WgerHttpException(responseData);
}
login(username, password, serverUrl);
} catch (error) {
throw error;
}
}
/// Authenticates a user
Future<void> login(String username, String password, String serverUrl) async {
var url = '$serverUrl/api/v2/login/';
try {
@@ -80,7 +122,7 @@ class Auth with ChangeNotifier {
final response = await http.post(
url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
HttpHeaders.contentTypeHeader: 'application/json; charset=UTF-8',
},
body: json.encode({'username': username, 'password': password}),
);
@@ -120,14 +162,6 @@ class Auth with ChangeNotifier {
}
}
Future<void> signUp(String email, String password) async {
return _authenticate(email, password, 'signUp');
}
Future<void> signIn(String username, String password, String serverUrl) async {
return _authenticate(username, password, serverUrl);
}
Future<bool> tryAutoLogin() async {
final prefs = await SharedPreferences.getInstance();
if (!prefs.containsKey('userData')) {

View File

@@ -57,7 +57,9 @@ class Nutrition extends WgerBaseProvider with ChangeNotifier {
/// Returns the current active nutritional plan. At the moment this is just
/// the latest, but this might change in the future.
NutritionalPlan get currentPlan {
return _plans.first;
if (_plans.length > 0) {
return _plans.first;
}
}
NutritionalPlan findById(int id) {

View File

@@ -104,7 +104,9 @@ class WorkoutPlans extends WgerBaseProvider with ChangeNotifier {
/// Returns the current active workout plan. At the moment this is just
/// the latest, but this might change in the future.
WorkoutPlan get activePlan {
return _workoutPlans.first;
if (_workoutPlans.length > 0) {
return _workoutPlans.first;
}
}
/*

View File

@@ -19,8 +19,8 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wger/helpers/ui.dart';
import 'package:wger/models/http_exception.dart';
import '../models/http_exception.dart';
import '../providers/auth.dart';
enum AuthMode {
@@ -116,18 +116,22 @@ class _AuthCardState extends State<AuthCard> {
_isLoading = true;
});
try {
// Login existing user
if (_authMode == AuthMode.Login) {
// Login existing user
await Provider.of<Auth>(context, listen: false).signIn(
await Provider.of<Auth>(context, listen: false).login(
_authData['username'],
_authData['password'],
_authData['serverUrl'],
);
} else {
// Register new user
// await Provider.of<Auth>(context, listen: false)
// .register(_authData['email'], _authData['password']);
// Register new user
} else {
await Provider.of<Auth>(context, listen: false).register(
username: _authData['username'],
password: _authData['password'],
email: _authData['email'],
serverUrl: _authData['serverUrl'],
);
}
} on WgerHttpException catch (error) {
showHttpExceptionErrorDialog(error, context);
@@ -197,8 +201,10 @@ class _AuthCardState extends State<AuthCard> {
controller: _emailController,
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
// Email is not required
validator: (value) {
if (value.isEmpty || !value.contains('@')) {
if (value.isNotEmpty && !value.contains('@')) {
return 'Invalid email!';
}
return null;

View File

@@ -83,7 +83,11 @@ class _DashboardNutritionWidgetState extends State<DashboardNutritionWidget> {
)
],
)
: Text('You have no nutritional plans'),
: Container(
alignment: Alignment.center,
height: 150,
child: Text('You have no nutritional plans'),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
@@ -140,7 +144,11 @@ class _DashboardWeightWidgetState extends State<DashboardWeightWidget> {
height: 180,
child: WeightChartWidget(weightEntriesData.items),
)
: Text('You have no weight entries'),
: Container(
alignment: Alignment.center,
height: 150,
child: Text('You have no weight entries'),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
@@ -199,51 +207,57 @@ class _DashboardWorkoutWidgetState extends State<DashboardWorkoutWidget> {
style: Theme.of(context).textTheme.headline4,
),
_workoutPlan != null
? Column(children: [
Text(
DateFormat.yMd().format(_workoutPlan.creationDate),
style: Theme.of(context).textTheme.headline6,
),
TextButton(
child: Text(
_workoutPlan.description,
style: TextStyle(fontSize: 20),
? Column(
children: [
Text(
DateFormat.yMd().format(_workoutPlan.creationDate),
style: Theme.of(context).textTheme.headline6,
),
onPressed: () {
return Navigator.of(context)
.pushNamed(WorkoutPlanScreen.routeName, arguments: _workoutPlan);
},
),
..._workoutPlan.days.map((workoutDay) {
return Column(children: [
const SizedBox(height: 10),
showDetail == true
? Text(
workoutDay.description,
style: TextStyle(fontWeight: FontWeight.bold),
)
: Text(workoutDay.description),
if (showDetail)
...workoutDay.sets
.map((set) => Text(set.exercisesObj.map((e) => e.name).join(',')))
.toList(),
]);
}).toList(),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
TextButton(
child: Text(AppLocalizations.of(context).toggleDetails),
onPressed: () {
setState(() {
showDetail = !showDetail;
});
},
TextButton(
child: Text(
_workoutPlan.description,
style: TextStyle(fontSize: 20),
),
],
)
])
: Text('you have no workouts'),
onPressed: () {
return Navigator.of(context)
.pushNamed(WorkoutPlanScreen.routeName, arguments: _workoutPlan);
},
),
..._workoutPlan.days.map((workoutDay) {
return Column(children: [
const SizedBox(height: 10),
showDetail == true
? Text(
workoutDay.description,
style: TextStyle(fontWeight: FontWeight.bold),
)
: Text(workoutDay.description),
if (showDetail)
...workoutDay.sets
.map((set) => Text(set.exercisesObj.map((e) => e.name).join(',')))
.toList(),
]);
}).toList(),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
TextButton(
child: Text(AppLocalizations.of(context).toggleDetails),
onPressed: () {
setState(() {
showDetail = !showDetail;
});
},
),
],
)
],
)
: Container(
alignment: Alignment.center,
height: 150,
child: Text('you have no workouts'),
),
],
),
);

View File

@@ -15,6 +15,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.39.17"
android_metadata:
dependency: "direct main"
description:
name: android_metadata
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
archive:
dependency: transitive
description:

View File

@@ -38,6 +38,7 @@ dependencies:
flutter_typeahead: ^2.0.0
table_calendar: ^2.3.3
package_info: ^0.4.3+2
android_metadata: ^0.1.3
dev_dependencies:
flutter_test: