Fix parsing of user model if email is not present Use user avatar hash instead of calculating it from the email Format Replace GravatarImageProvider Set min sdk version to 19 Change target api version to 28 Limit drone pipeline execution to master or pr Remove drone debug Use username instead of id Format "Fix" clone permissions Drone debug Fix drone permissions with different flutter build docker image Switch CI build image Bump Gradle sdk version Fix formatting Update packages for support for androidX Update gitignore AndroidX Make GravatarImageProvider work again Co-authored-by: kolaente <k@knt.li> Reviewed-on: #38
This commit is contained in:
parent
144f55af6d
commit
0f23c4d0f3
47
.drone.yml
47
.drone.yml
|
@ -2,16 +2,27 @@ kind: pipeline
|
||||||
name: testing
|
name: testing
|
||||||
|
|
||||||
workspace:
|
workspace:
|
||||||
base: /app
|
base: /home/cirrus/app
|
||||||
|
|
||||||
clone:
|
clone:
|
||||||
depth: 50
|
depth: 50
|
||||||
|
|
||||||
|
# Only run on prs or pushes to master
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
include:
|
||||||
|
- master
|
||||||
|
event:
|
||||||
|
include:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: build
|
||||||
image: vikunja/flutter-build
|
image: cirrusci/flutter:stable
|
||||||
pull: true
|
pull: true
|
||||||
commands:
|
commands:
|
||||||
|
- sudo chown cirrus . -R # The clone step clones everything as root, this is our "fix" until we find a better solution
|
||||||
- flutter packages get
|
- flutter packages get
|
||||||
- make format-check
|
- make format-check
|
||||||
- make build-debug
|
- make build-debug
|
||||||
|
@ -32,12 +43,12 @@ depends_on:
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
branch:
|
branch:
|
||||||
- master
|
- master
|
||||||
event:
|
event:
|
||||||
- push
|
- push
|
||||||
|
|
||||||
workspace:
|
workspace:
|
||||||
base: /app
|
base: /home/cirrus/app
|
||||||
|
|
||||||
clone:
|
clone:
|
||||||
depth: 50
|
depth: 50
|
||||||
|
@ -45,13 +56,14 @@ clone:
|
||||||
steps:
|
steps:
|
||||||
# Because drone separates the pipelines, we have to add the build step to this pipeline. This is double code, we should change it at some point if possible.
|
# Because drone separates the pipelines, we have to add the build step to this pipeline. This is double code, we should change it at some point if possible.
|
||||||
- name: build
|
- name: build
|
||||||
image: vikunja/flutter-build
|
image: cirrusci/flutter:stable
|
||||||
pull: true
|
pull: true
|
||||||
commands:
|
commands:
|
||||||
- flutter packages get
|
- sudo chown cirrus . -R # The clone step clones everything as root, this is our "fix" until we find a better solution
|
||||||
- make build-all
|
- flutter packages get
|
||||||
- mkdir apks
|
- make build-all
|
||||||
- mv build/app/outputs/apk/*/*/*.apk apks
|
- mkdir apks
|
||||||
|
- mv build/app/outputs/apk/*/*/*.apk apks
|
||||||
|
|
||||||
# Push the releases to our pseudo-s3-bucket
|
# Push the releases to our pseudo-s3-bucket
|
||||||
- name: release
|
- name: release
|
||||||
|
@ -78,10 +90,10 @@ depends_on:
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
event:
|
event:
|
||||||
- tag
|
- tag
|
||||||
|
|
||||||
workspace:
|
workspace:
|
||||||
base: /app
|
base: /home/cirrus/app
|
||||||
|
|
||||||
clone:
|
clone:
|
||||||
depth: 50
|
depth: 50
|
||||||
|
@ -89,13 +101,14 @@ clone:
|
||||||
steps:
|
steps:
|
||||||
# Because drone separates the pipelines, we have to add the build step to this pipeline. This is double code, we should change it at some point if possible.
|
# Because drone separates the pipelines, we have to add the build step to this pipeline. This is double code, we should change it at some point if possible.
|
||||||
- name: build
|
- name: build
|
||||||
image: vikunja/flutter-build
|
image: cirrusci/flutter:stable
|
||||||
pull: true
|
pull: true
|
||||||
commands:
|
commands:
|
||||||
- flutter packages get
|
- sudo chown cirrus . -R # The clone step clones everything as root, this is our "fix" until we find a better solution
|
||||||
- make build-all
|
- flutter packages get
|
||||||
- mkdir apks
|
- make build-all
|
||||||
- mv build/app/outputs/apk/*/*/*.apk apks
|
- mkdir apks
|
||||||
|
- mv build/app/outputs/apk/*/*/*.apk apks
|
||||||
# Push the releases to our pseudo-s3-bucket
|
# Push the releases to our pseudo-s3-bucket
|
||||||
- name: release
|
- name: release
|
||||||
image: plugins/s3:1
|
image: plugins/s3:1
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
.pub/
|
.pub/
|
||||||
build/
|
build/
|
||||||
!pubspec.lock
|
!pubspec.lock
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
|
||||||
# Android related
|
# Android related
|
||||||
**/android/**/gradle-wrapper.jar
|
**/android/**/gradle-wrapper.jar
|
||||||
|
|
|
@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion 28
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
|
@ -37,14 +37,12 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
|
||||||
applicationId "io.vikunja.app"
|
applicationId "io.vikunja.app"
|
||||||
minSdkVersion 18
|
minSdkVersion 19
|
||||||
targetSdkVersion 27
|
targetSdkVersion 28
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorDimensions "deploy"
|
flavorDimensions "deploy"
|
||||||
|
|
|
@ -21,8 +21,6 @@ allprojects {
|
||||||
rootProject.buildDir = '../build'
|
rootProject.buildDir = '../build'
|
||||||
subprojects {
|
subprojects {
|
||||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||||
}
|
|
||||||
subprojects {
|
|
||||||
project.evaluationDependsOn(':app')
|
project.evaluationDependsOn(':app')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,4 @@
|
||||||
org.gradle.jvmargs=-Xmx1536M
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
|
android.enableR8=true
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
include ':app'
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# This is a generated file; do not edit or check into version control.
|
||||||
|
export "FLUTTER_ROOT=/nix/store/nyvp7jf7sfxsbc6jsm1y4fc18631ap26-flutter-stable-1.12.13+hotfix.5-unwrapped"
|
||||||
|
export "FLUTTER_APPLICATION_PATH=/home/konrad/www/vikunja/app2"
|
||||||
|
export "FLUTTER_TARGET=lib/main.dart"
|
||||||
|
export "FLUTTER_BUILD_DIR=build"
|
||||||
|
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
|
||||||
|
export "FLUTTER_FRAMEWORK_DIR=/home/konrad/.cache/flutter/artifacts/engine/ios"
|
||||||
|
export "FLUTTER_BUILD_NAME=0.1.0"
|
||||||
|
export "FLUTTER_BUILD_NUMBER=0.1.0"
|
|
@ -1,9 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:crypto/crypto.dart';
|
|
||||||
|
|
||||||
class GravatarImageProvider extends NetworkImage {
|
|
||||||
GravatarImageProvider(String email)
|
|
||||||
: super("https://secure.gravatar.com/avatar/" +
|
|
||||||
md5.convert(email.trim().toLowerCase().codeUnits).toString());
|
|
||||||
}
|
|
|
@ -120,9 +120,9 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loadedCurrentUser = User(int.tryParse(currentUser), "", "");
|
loadedCurrentUser = User(int.tryParse(currentUser), "", "", "");
|
||||||
} catch (otherExceptions) {
|
} catch (otherExceptions) {
|
||||||
loadedCurrentUser = User(int.tryParse(currentUser), "", "");
|
loadedCurrentUser = User(int.tryParse(currentUser), "", "", "");
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_client = client;
|
_client = client;
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
class User {
|
class User {
|
||||||
final int id;
|
final int id;
|
||||||
final String email, username;
|
final String email, username, avatarHash;
|
||||||
|
|
||||||
User(this.id, this.email, this.username);
|
User(this.id, this.email, this.username, this.avatarHash);
|
||||||
User.fromJson(Map<String, dynamic> json)
|
User.fromJson(Map<String, dynamic> json)
|
||||||
: id = json['id'],
|
: id = json['id'],
|
||||||
email = json['email'],
|
email = json.containsKey('email') ? json['email'] : '',
|
||||||
username = json['username'];
|
username = json['username'],
|
||||||
|
avatarHash = json['avatarUrl'];
|
||||||
|
|
||||||
toJSON() => {"id": this.id, "email": this.email, "username": this.username};
|
toJSON() => {"id": this.id, "email": this.email, "username": this.username};
|
||||||
|
|
||||||
|
String avatarUrl() {
|
||||||
|
return "https://secure.gravatar.com/avatar/" + this.avatarHash;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserTokenPair {
|
class UserTokenPair {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:after_layout/after_layout.dart';
|
import 'package:after_layout/after_layout.dart';
|
||||||
|
|
||||||
import 'package:vikunja_app/components/AddDialog.dart';
|
import 'package:vikunja_app/components/AddDialog.dart';
|
||||||
import 'package:vikunja_app/components/GravatarImage.dart';
|
|
||||||
import 'package:vikunja_app/pages/namespace/namespace.dart';
|
import 'package:vikunja_app/pages/namespace/namespace.dart';
|
||||||
import 'package:vikunja_app/pages/namespace/namespace_edit.dart';
|
import 'package:vikunja_app/pages/namespace/namespace_edit.dart';
|
||||||
import 'package:vikunja_app/pages/placeholder.dart';
|
import 'package:vikunja_app/pages/placeholder.dart';
|
||||||
|
@ -90,8 +89,10 @@ class HomePageState extends State<HomePage> with AfterLayoutMixin<HomePage> {
|
||||||
drawer: new Drawer(
|
drawer: new Drawer(
|
||||||
child: new Column(children: <Widget>[
|
child: new Column(children: <Widget>[
|
||||||
new UserAccountsDrawerHeader(
|
new UserAccountsDrawerHeader(
|
||||||
accountEmail: currentUser == null ? null : Text(currentUser.email),
|
accountEmail:
|
||||||
accountName: currentUser == null ? null : Text(currentUser.username),
|
currentUser?.email == null ? null : Text(currentUser.email),
|
||||||
|
accountName:
|
||||||
|
currentUser?.username == null ? null : Text(currentUser.username),
|
||||||
onDetailsPressed: () {
|
onDetailsPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_showUserDetails = !_showUserDetails;
|
_showUserDetails = !_showUserDetails;
|
||||||
|
@ -100,7 +101,8 @@ class HomePageState extends State<HomePage> with AfterLayoutMixin<HomePage> {
|
||||||
currentAccountPicture: currentUser == null
|
currentAccountPicture: currentUser == null
|
||||||
? null
|
? null
|
||||||
: CircleAvatar(
|
: CircleAvatar(
|
||||||
backgroundImage: GravatarImageProvider(currentUser.username)),
|
backgroundImage: NetworkImage(currentUser.avatarUrl()),
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
image: AssetImage("assets/graphics/hypnotize.png"),
|
image: AssetImage("assets/graphics/hypnotize.png"),
|
||||||
|
@ -118,10 +120,10 @@ class HomePageState extends State<HomePage> with AfterLayoutMixin<HomePage> {
|
||||||
alignment: FractionalOffset.bottomCenter,
|
alignment: FractionalOffset.bottomCenter,
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) => ListTile(
|
builder: (context) => ListTile(
|
||||||
leading: const Icon(Icons.add),
|
leading: const Icon(Icons.add),
|
||||||
title: const Text('Add namespace...'),
|
title: const Text('Add namespace...'),
|
||||||
onTap: () => _addNamespaceDialog(context),
|
onTap: () => _addNamespaceDialog(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
])),
|
])),
|
||||||
|
|
|
@ -26,68 +26,68 @@ class _ListEditPageState extends State<ListEditPage> {
|
||||||
),
|
),
|
||||||
body: Builder(
|
body: Builder(
|
||||||
builder: (BuildContext context) => SafeArea(
|
builder: (BuildContext context) => SafeArea(
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 10.0),
|
padding: EdgeInsets.symmetric(vertical: 10.0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
initialValue: widget.list.title,
|
initialValue: widget.list.title,
|
||||||
onSaved: (title) => _title = title,
|
onSaved: (title) => _title = title,
|
||||||
validator: (title) {
|
validator: (title) {
|
||||||
if (title.length < 3 || title.length > 250) {
|
if (title.length < 3 || title.length > 250) {
|
||||||
return 'The title needs to have between 3 and 250 characters.';
|
return 'The title needs to have between 3 and 250 characters.';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
decoration: new InputDecoration(
|
decoration: new InputDecoration(
|
||||||
labelText: 'Title',
|
labelText: 'Title',
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Padding(
|
),
|
||||||
padding: EdgeInsets.symmetric(vertical: 10.0),
|
),
|
||||||
child: TextFormField(
|
Padding(
|
||||||
maxLines: null,
|
padding: EdgeInsets.symmetric(vertical: 10.0),
|
||||||
keyboardType: TextInputType.multiline,
|
child: TextFormField(
|
||||||
initialValue: widget.list.description,
|
maxLines: null,
|
||||||
onSaved: (description) => _description = description,
|
keyboardType: TextInputType.multiline,
|
||||||
validator: (description) {
|
initialValue: widget.list.description,
|
||||||
if (description.length > 1000) {
|
onSaved: (description) => _description = description,
|
||||||
return 'The description can have a maximum of 1000 characters.';
|
validator: (description) {
|
||||||
}
|
if (description.length > 1000) {
|
||||||
return null;
|
return 'The description can have a maximum of 1000 characters.';
|
||||||
},
|
}
|
||||||
decoration: new InputDecoration(
|
return null;
|
||||||
labelText: 'Description',
|
},
|
||||||
border: OutlineInputBorder(),
|
decoration: new InputDecoration(
|
||||||
),
|
labelText: 'Description',
|
||||||
),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
Builder(
|
),
|
||||||
builder: (context) => Padding(
|
),
|
||||||
padding: EdgeInsets.symmetric(vertical: 10.0),
|
Builder(
|
||||||
child: FancyButton(
|
builder: (context) => Padding(
|
||||||
onPressed: !_loading
|
padding: EdgeInsets.symmetric(vertical: 10.0),
|
||||||
? () {
|
child: FancyButton(
|
||||||
if (_formKey.currentState.validate()) {
|
onPressed: !_loading
|
||||||
Form.of(context).save();
|
? () {
|
||||||
_saveList(context);
|
if (_formKey.currentState.validate()) {
|
||||||
}
|
Form.of(context).save();
|
||||||
}
|
_saveList(context);
|
||||||
: null,
|
}
|
||||||
child: _loading
|
}
|
||||||
? CircularProgressIndicator()
|
: null,
|
||||||
: VikunjaButtonText('Save'),
|
child: _loading
|
||||||
))),
|
? CircularProgressIndicator()
|
||||||
]),
|
: VikunjaButtonText('Save'),
|
||||||
),
|
))),
|
||||||
),
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,68 +26,68 @@ class _NamespaceEditPageState extends State<NamespaceEditPage> {
|
||||||
),
|
),
|
||||||
body: Builder(
|
body: Builder(
|
||||||
builder: (BuildContext context) => SafeArea(
|
builder: (BuildContext context) => SafeArea(
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 10.0),
|
padding: EdgeInsets.symmetric(vertical: 10.0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
initialValue: widget.namespace.name,
|
initialValue: widget.namespace.name,
|
||||||
onSaved: (name) => _name = name,
|
onSaved: (name) => _name = name,
|
||||||
validator: (name) {
|
validator: (name) {
|
||||||
if (name.length < 3 || name.length > 250) {
|
if (name.length < 3 || name.length > 250) {
|
||||||
return 'The name needs to have between 3 and 250 characters.';
|
return 'The name needs to have between 3 and 250 characters.';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
decoration: new InputDecoration(
|
decoration: new InputDecoration(
|
||||||
labelText: 'Name',
|
labelText: 'Name',
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Padding(
|
),
|
||||||
padding: EdgeInsets.symmetric(vertical: 10.0),
|
),
|
||||||
child: TextFormField(
|
Padding(
|
||||||
maxLines: null,
|
padding: EdgeInsets.symmetric(vertical: 10.0),
|
||||||
keyboardType: TextInputType.multiline,
|
child: TextFormField(
|
||||||
initialValue: widget.namespace.description,
|
maxLines: null,
|
||||||
onSaved: (description) => _description = description,
|
keyboardType: TextInputType.multiline,
|
||||||
validator: (description) {
|
initialValue: widget.namespace.description,
|
||||||
if (description.length > 1000) {
|
onSaved: (description) => _description = description,
|
||||||
return 'The description can have a maximum of 1000 characters.';
|
validator: (description) {
|
||||||
}
|
if (description.length > 1000) {
|
||||||
return null;
|
return 'The description can have a maximum of 1000 characters.';
|
||||||
},
|
}
|
||||||
decoration: new InputDecoration(
|
return null;
|
||||||
labelText: 'Description',
|
},
|
||||||
border: OutlineInputBorder(),
|
decoration: new InputDecoration(
|
||||||
),
|
labelText: 'Description',
|
||||||
),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
Builder(
|
),
|
||||||
builder: (context) => Padding(
|
),
|
||||||
padding: EdgeInsets.symmetric(vertical: 10.0),
|
Builder(
|
||||||
child: FancyButton(
|
builder: (context) => Padding(
|
||||||
onPressed: !_loading
|
padding: EdgeInsets.symmetric(vertical: 10.0),
|
||||||
? () {
|
child: FancyButton(
|
||||||
if (_formKey.currentState.validate()) {
|
onPressed: !_loading
|
||||||
Form.of(context).save();
|
? () {
|
||||||
_saveNamespace(context);
|
if (_formKey.currentState.validate()) {
|
||||||
}
|
Form.of(context).save();
|
||||||
}
|
_saveNamespace(context);
|
||||||
: null,
|
}
|
||||||
child: _loading
|
}
|
||||||
? CircularProgressIndicator()
|
: null,
|
||||||
: VikunjaButtonText('Save'),
|
child: _loading
|
||||||
))),
|
? CircularProgressIndicator()
|
||||||
]),
|
: VikunjaButtonText('Save'),
|
||||||
),
|
))),
|
||||||
),
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,83 +24,81 @@ class _LoginPageState extends State<LoginPage> {
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (BuildContext context) => Form(
|
builder: (BuildContext context) => Form(
|
||||||
autovalidate: true,
|
autovalidate: true,
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 30),
|
padding: EdgeInsets.symmetric(vertical: 30),
|
||||||
child: Image(
|
child: Image(
|
||||||
image: AssetImage('assets/vikunja_logo_full.png'),
|
image: AssetImage('assets/vikunja_logo_full.png'),
|
||||||
height: 85.0,
|
height: 85.0,
|
||||||
semanticLabel: 'Vikunja Logo',
|
semanticLabel: 'Vikunja Logo',
|
||||||
),
|
),
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: vStandardVerticalPadding,
|
|
||||||
child: TextFormField(
|
|
||||||
enabled: !_loading,
|
|
||||||
onSaved: (serverAddress) => _server = serverAddress,
|
|
||||||
validator: (address) {
|
|
||||||
return isUrl(address) ? null : 'Invalid URL';
|
|
||||||
},
|
|
||||||
decoration: new InputDecoration(
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
labelText: 'Server Address'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: vStandardVerticalPadding,
|
|
||||||
child: TextFormField(
|
|
||||||
enabled: !_loading,
|
|
||||||
onSaved: (username) => _username = username,
|
|
||||||
decoration: new InputDecoration(
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
labelText: 'Username'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: vStandardVerticalPadding,
|
|
||||||
child: TextFormField(
|
|
||||||
enabled: !_loading,
|
|
||||||
onSaved: (password) => _password = password,
|
|
||||||
decoration: new InputDecoration(
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
labelText: 'Password'),
|
|
||||||
obscureText: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Builder(
|
|
||||||
builder: (context) => FancyButton(
|
|
||||||
onPressed: !_loading
|
|
||||||
? () {
|
|
||||||
if (_formKey.currentState
|
|
||||||
.validate()) {
|
|
||||||
Form.of(context).save();
|
|
||||||
_loginUser(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
child: _loading
|
|
||||||
? CircularProgressIndicator()
|
|
||||||
: VikunjaButtonText('Login'),
|
|
||||||
)),
|
|
||||||
Builder(
|
|
||||||
builder: (context) => FancyButton(
|
|
||||||
onPressed: () => Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
RegisterPage())),
|
|
||||||
child: VikunjaButtonText('Register'),
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
|
padding: vStandardVerticalPadding,
|
||||||
|
child: TextFormField(
|
||||||
|
enabled: !_loading,
|
||||||
|
onSaved: (serverAddress) => _server = serverAddress,
|
||||||
|
validator: (address) {
|
||||||
|
return isUrl(address) ? null : 'Invalid URL';
|
||||||
|
},
|
||||||
|
decoration: new InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
labelText: 'Server Address'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: vStandardVerticalPadding,
|
||||||
|
child: TextFormField(
|
||||||
|
enabled: !_loading,
|
||||||
|
onSaved: (username) => _username = username,
|
||||||
|
decoration: new InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
labelText: 'Username'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: vStandardVerticalPadding,
|
||||||
|
child: TextFormField(
|
||||||
|
enabled: !_loading,
|
||||||
|
onSaved: (password) => _password = password,
|
||||||
|
decoration: new InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
labelText: 'Password'),
|
||||||
|
obscureText: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Builder(
|
||||||
|
builder: (context) => FancyButton(
|
||||||
|
onPressed: !_loading
|
||||||
|
? () {
|
||||||
|
if (_formKey.currentState.validate()) {
|
||||||
|
Form.of(context).save();
|
||||||
|
_loginUser(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: _loading
|
||||||
|
? CircularProgressIndicator()
|
||||||
|
: VikunjaButtonText('Login'),
|
||||||
|
)),
|
||||||
|
Builder(
|
||||||
|
builder: (context) => FancyButton(
|
||||||
|
onPressed: () => Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => RegisterPage())),
|
||||||
|
child: VikunjaButtonText('Register'),
|
||||||
|
)),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -24,103 +24,102 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||||
),
|
),
|
||||||
body: Builder(
|
body: Builder(
|
||||||
builder: (BuildContext context) => SafeArea(
|
builder: (BuildContext context) => SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
bottom: false,
|
bottom: false,
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Padding(
|
Padding(
|
||||||
padding: vStandardVerticalPadding,
|
padding: vStandardVerticalPadding,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
onSaved: (serverAddress) => _server = serverAddress,
|
onSaved: (serverAddress) => _server = serverAddress,
|
||||||
validator: (address) {
|
validator: (address) {
|
||||||
return isUrl(address) ? null : 'Invalid URL';
|
return isUrl(address) ? null : 'Invalid URL';
|
||||||
},
|
},
|
||||||
decoration: new InputDecoration(
|
decoration: new InputDecoration(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
labelText: 'Server Address'),
|
labelText: 'Server Address'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: vStandardVerticalPadding,
|
padding: vStandardVerticalPadding,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
onSaved: (username) => _username = username.trim(),
|
onSaved: (username) => _username = username.trim(),
|
||||||
validator: (username) {
|
validator: (username) {
|
||||||
return username.trim().isNotEmpty
|
return username.trim().isNotEmpty
|
||||||
? null
|
? null
|
||||||
: 'Please specify a username';
|
: 'Please specify a username';
|
||||||
},
|
},
|
||||||
decoration: new InputDecoration(
|
decoration: new InputDecoration(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
labelText: 'Username'),
|
labelText: 'Username'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: vStandardVerticalPadding,
|
padding: vStandardVerticalPadding,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
onSaved: (email) => _email = email,
|
onSaved: (email) => _email = email,
|
||||||
validator: (email) {
|
validator: (email) {
|
||||||
return isEmail(email)
|
return isEmail(email)
|
||||||
? null
|
? null
|
||||||
: 'Email adress is invalid';
|
: 'Email adress is invalid';
|
||||||
},
|
},
|
||||||
decoration: new InputDecoration(
|
decoration: new InputDecoration(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
labelText: 'Email Address'),
|
labelText: 'Email Address'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: vStandardVerticalPadding,
|
padding: vStandardVerticalPadding,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: passwordController,
|
controller: passwordController,
|
||||||
onSaved: (password) => _password = password,
|
onSaved: (password) => _password = password,
|
||||||
validator: (password) {
|
validator: (password) {
|
||||||
return password.length >= 8
|
return password.length >= 8
|
||||||
? null
|
? null
|
||||||
: 'Please use at least 8 characters';
|
: 'Please use at least 8 characters';
|
||||||
},
|
},
|
||||||
decoration: new InputDecoration(
|
decoration: new InputDecoration(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
labelText: 'Password'),
|
labelText: 'Password'),
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: vStandardVerticalPadding,
|
padding: vStandardVerticalPadding,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
validator: (password) {
|
validator: (password) {
|
||||||
return passwordController.text == password
|
return passwordController.text == password
|
||||||
? null
|
? null
|
||||||
: 'Passwords don\'t match.';
|
: 'Passwords don\'t match.';
|
||||||
},
|
},
|
||||||
decoration: new InputDecoration(
|
decoration: new InputDecoration(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
labelText: 'Repeat Password'),
|
labelText: 'Repeat Password'),
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) => FancyButton(
|
builder: (context) => FancyButton(
|
||||||
onPressed: !_loading
|
onPressed: !_loading
|
||||||
? () {
|
? () {
|
||||||
if (_formKey.currentState
|
if (_formKey.currentState.validate()) {
|
||||||
.validate()) {
|
Form.of(context).save();
|
||||||
Form.of(context).save();
|
_registerUser(context);
|
||||||
_registerUser(context);
|
} else {
|
||||||
} else {
|
print("awhat");
|
||||||
print("awhat");
|
}
|
||||||
}
|
}
|
||||||
}
|
: null,
|
||||||
: null,
|
child: _loading
|
||||||
child: _loading
|
? CircularProgressIndicator()
|
||||||
? CircularProgressIndicator()
|
: VikunjaButtonText('Register'),
|
||||||
: VikunjaButtonText('Register'),
|
)),
|
||||||
)),
|
],
|
||||||
],
|
)),
|
||||||
)),
|
),
|
||||||
),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import 'package:vikunja_app/models/user.dart';
|
||||||
import 'package:vikunja_app/service/services.dart';
|
import 'package:vikunja_app/service/services.dart';
|
||||||
|
|
||||||
// Data for mocked services
|
// Data for mocked services
|
||||||
var _users = {1: User(1, 'test@testuser.org', 'test1')};
|
var _users = {1: User(1, 'test@testuser.org', 'test1', '')};
|
||||||
|
|
||||||
var _namespaces = {
|
var _namespaces = {
|
||||||
1: Namespace(
|
1: Namespace(
|
||||||
|
|
48
pubspec.lock
48
pubspec.lock
|
@ -1,5 +1,5 @@
|
||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://www.dartlang.org/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
after_layout:
|
after_layout:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
|
@ -7,35 +7,35 @@ packages:
|
||||||
name: after_layout
|
name: after_layout
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.7"
|
version: "1.0.7+2"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.8"
|
version: "2.0.11"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.1"
|
version: "1.5.2"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.8"
|
version: "2.4.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.5"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -63,14 +63,14 @@ packages:
|
||||||
name: crypto
|
name: crypto
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.6"
|
version: "2.1.3"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cupertino_icons
|
name: cupertino_icons
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.2"
|
version: "0.1.3"
|
||||||
dart_config:
|
dart_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -98,7 +98,7 @@ packages:
|
||||||
name: flutter_secure_storage
|
name: flutter_secure_storage
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.3.1+1"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -110,7 +110,7 @@ packages:
|
||||||
name: http
|
name: http
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.0"
|
version: "0.12.0+3"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -124,49 +124,49 @@ packages:
|
||||||
name: image
|
name: image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.7"
|
version: "2.1.4"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.3+1"
|
version: "0.12.6"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.6"
|
version: "1.1.8"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.2"
|
version: "1.6.4"
|
||||||
pedantic:
|
pedantic:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pedantic
|
name: pedantic
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.8.0+1"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: petitparser
|
name: petitparser
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.4.0"
|
||||||
quiver:
|
quiver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: quiver
|
name: quiver
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.5"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -178,7 +178,7 @@ packages:
|
||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.4"
|
version: "1.5.5"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -192,14 +192,14 @@ packages:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.8"
|
version: "2.0.0"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: string_scanner
|
name: string_scanner
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.5"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -213,7 +213,7 @@ packages:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.2"
|
version: "0.2.11"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -234,13 +234,13 @@ packages:
|
||||||
name: xml
|
name: xml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.1"
|
version: "3.5.0"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: yaml
|
name: yaml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.15"
|
version: "2.2.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.1.0 <3.0.0"
|
dart: ">=2.4.0 <3.0.0"
|
||||||
|
|
11
pubspec.yaml
11
pubspec.yaml
|
@ -4,17 +4,14 @@ description: Vikunja as Flutter cross platform app
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.0.0-dev.63.0 <3.0.0"
|
sdk: ">=2.1.0 <3.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
cupertino_icons: ^0.1.3
|
||||||
# The following adds the Cupertino Icons font to your application.
|
flutter_secure_storage: 3.3.1+1
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
http: 0.12.0+3
|
||||||
cupertino_icons: ^0.1.2
|
|
||||||
flutter_secure_storage: 3.1.1
|
|
||||||
http: 0.12.0
|
|
||||||
after_layout: ^1.0.7
|
after_layout: ^1.0.7
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|
Reference in New Issue