Add working login implementation

This commit is contained in:
Jonas Franz 2018-09-17 15:35:57 +02:00
parent 4bfd13536e
commit 1994892b63
No known key found for this signature in database
GPG Key ID: C8287A01D593AC1D
10 changed files with 235 additions and 37 deletions

View File

@ -1 +1,2 @@
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View File

@ -1 +1,2 @@
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

View File

@ -19,6 +19,7 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
ACA854A11123D371B9168194 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C102A622A93B95B5704BDD24 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -53,6 +54,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C102A622A93B95B5704BDD24 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -62,12 +64,28 @@
files = (
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
ACA854A11123D371B9168194 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
4D8888AA13EBD37D6777D23F /* Frameworks */ = {
isa = PBXGroup;
children = (
C102A622A93B95B5704BDD24 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
7CACC4C503C5D851EB73C215 /* Pods */ = {
isa = PBXGroup;
children = (
);
name = Pods;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@ -88,6 +106,8 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
7CACC4C503C5D851EB73C215 /* Pods */,
4D8888AA13EBD37D6777D23F /* Frameworks */,
);
sourceTree = "<group>";
};
@ -129,12 +149,15 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
3185B6CCDCA9C2025E57C488 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
85082CB7F9EE2F3E7985BDB9 /* [CP] Embed Pods Frameworks */,
7C22F5DE30AEBAB42040EB3F /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -156,6 +179,7 @@
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = Z48VLBM2R7;
LastSwiftMigration = 0910;
};
};
@ -195,6 +219,24 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3185B6CCDCA9C2025E57C488 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -209,6 +251,41 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
};
7C22F5DE30AEBAB42040EB3F /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
85082CB7F9EE2F3E7985BDB9 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework",
"${BUILT_PRODUCTS_DIR}/flutter_secure_storage/flutter_secure_storage.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -367,6 +444,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = Z48VLBM2R7;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -395,6 +473,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = Z48VLBM2R7;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -439,4 +518,4 @@
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
}

View File

@ -4,4 +4,7 @@
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@ -1,7 +1,17 @@
class Client {
final String _token;
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
Client(this._token);
class Client {
final JsonDecoder _decoder = new JsonDecoder();
final JsonEncoder _encoder = new JsonEncoder();
final String _token;
final String _base;
String get base => _base;
Client(this._token, String base)
: _base = base.endsWith('/api/v1') ? base : '$base/api/v1';
bool operator ==(dynamic otherClient) {
return otherClient._token == _token;
@ -9,4 +19,43 @@ class Client {
@override
int get hashCode => _token.hashCode;
get _headers => {
'Authorization': _token != null ? 'Bearer $_token' : '',
'Content-Type': 'application/json'
};
Future<dynamic> get(String url) {
return http
.get('${this.base}$url', headers: _headers)
.then(_handleResponse);
}
Future<dynamic> post(String url, {dynamic body}) {
return http
.post('${this.base}$url',
headers: _headers, body: _encoder.convert(body))
.then(_handleResponse);
}
dynamic _handleResponse(http.Response response) {
if (response.statusCode < 200 ||
response.statusCode > 400 ||
json == null) {
throw new ApiException(
response.statusCode, response.request.url.toString());
}
return _decoder.convert(response.body);
}
}
class ApiException implements Exception {
final int errorCode;
final String path;
ApiException(this.errorCode, this.path);
@override
String toString() {
return "Can't fetch data from server. (Error-Code: $errorCode)";
}
}

View File

@ -0,0 +1,27 @@
import 'dart:async';
import 'package:fluttering_vikunja/api/client.dart';
import 'package:fluttering_vikunja/models/user.dart';
import 'package:fluttering_vikunja/service/services.dart';
class UserAPIService implements UserService {
final Client _client;
UserAPIService(this._client);
@override
Future<UserTokenPair> login(String username, password) async {
var token = await _client.post('/login', body: {
'username': username,
'password': password
}).then((map) => map['token']);
return UserAPIService(Client(token, _client.base))
.getCurrentUser()
.then((user) => UserTokenPair(user, token));
}
@override
Future<User> getCurrentUser() {
return _client.get('/user').then((map) => User.fromJson(map));
}
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:fluttering_vikunja/api/client.dart';
import 'package:fluttering_vikunja/api/user_implementation.dart';
import 'package:fluttering_vikunja/managers/user.dart';
import 'package:fluttering_vikunja/models/user.dart';
import 'package:fluttering_vikunja/service/mocked_services.dart';
@ -33,7 +34,8 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
Client get client => _client;
UserManager get userManager => new UserManager(_storage);
UserService get userService => new MockedUserService();
UserService get userService => new UserAPIService(_client);
UserService newLoginService(base) => new UserAPIService(Client(null, base));
@override
void initState() {
@ -41,7 +43,7 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
_loadCurrentUser();
}
void changeUser(User newUser, {String token}) async {
void changeUser(User newUser, {String token, String base}) async {
setState(() {
_loading = true;
});
@ -51,27 +53,43 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
// Write new token to secure storage
await _storage.write(key: newUser.id.toString(), value: token);
}
if (base == null) {
base = await _storage.read(key: "${newUser.id.toString()}_base");
} else {
// Write new base to secure storage
await _storage.write(key: "${newUser.id.toString()}_base", value: base);
}
// Set current user in storage
await _storage.write(key: 'currentUser', value: newUser.id.toString());
setState(() {
_currentUser = newUser;
_client = Client(token);
_client = Client(token, base);
_loading = false;
});
}
void _loadCurrentUser() async {
var currentUser = await _storage.read(key: 'currentUser');
var token;
var loadedCurrentUser;
if (currentUser != null) {
token = await _storage.read(key: currentUser);
loadedCurrentUser = await userService.get(int.tryParse(currentUser));
if (currentUser == null) {
setState(() {
_loading = false;
});
return;
}
var token = await _storage.read(key: currentUser);
var base = await _storage.read(key: '${currentUser}_base');
if (token == null || base == null) {
setState(() {
_loading = false;
});
return;
}
setState(() {
_client = Client(token, base);
});
var loadedCurrentUser = await userService.getCurrentUser();
setState(() {
_currentUser = loadedCurrentUser;
_client = token != null ? Client(token) : null;
_loading = false;
});
}

View File

@ -68,20 +68,23 @@ class _LoginPageState extends State<LoginPage> {
obscureText: true,
),
),
ButtonTheme(
height: _loading ? 55.0 : 36.0,
child: RaisedButton(
onPressed: !_loading
? () {
if (_formKey.currentState.validate()) {
_loginUser(context);
}
}
: null,
child: _loading
? CircularProgressIndicator()
: Text('Login'),
))
Builder(
builder: (context) => ButtonTheme(
height: _loading ? 55.0 : 36.0,
child: RaisedButton(
onPressed: !_loading
? () {
if (_formKey.currentState
.validate()) {
Form.of(context).save();
_loginUser(context);
}
}
: null,
child: _loading
? CircularProgressIndicator()
: Text('Login'),
))),
],
)),
),
@ -90,11 +93,28 @@ class _LoginPageState extends State<LoginPage> {
_loginUser(BuildContext context) async {
setState(() => _loading = true);
var vGlobal = VikunjaGlobal.of(context);
var newUser = await vGlobal.userService.login(_username, _password);
vGlobal.changeUser(newUser.user, token: newUser.token);
setState(() {
_loading = false;
});
try {
var vGlobal = VikunjaGlobal.of(context);
var newUser =
await vGlobal.newLoginService(_server).login(_username, _password);
vGlobal.changeUser(newUser.user, token: newUser.token, base: _server);
} catch (ex) {
print(ex);
showDialog(
context: context,
builder: (context) => new AlertDialog(
title:
const Text('Login failed! Please check you credentials.'),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.pop(context),
child: const Text('CLOSE'))
],
));
} finally {
setState(() {
_loading = false;
});
}
}
}

View File

@ -137,7 +137,7 @@ class MockedUserService implements UserService {
}
@override
Future<User> get(int userId) {
return Future.value(_users[userId]);
Future<User> getCurrentUser() {
return Future.value(_users[1]);
}
}

View File

@ -28,5 +28,5 @@ abstract class TaskService {
abstract class UserService {
Future<UserTokenPair> login(String username, password);
Future<User> get(int userId);
Future<User> getCurrentUser();
}