mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
Allow users to register via the app
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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')) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user