From 339d3582037cbf3e167c558c214f2352aedbabc4 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 16:18:36 +0100 Subject: [PATCH 01/20] Make GravatarImageProvider work again --- android/gradle.properties | 1 + lib/components/GravatarImage.dart | 48 +++++++++++++++++++++++++++---- pubspec.lock | 38 ++++++++++++------------ pubspec.yaml | 2 +- 4 files changed, 63 insertions(+), 26 deletions(-) diff --git a/android/gradle.properties b/android/gradle.properties index 8bd86f6..7be3d8b 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/lib/components/GravatarImage.dart b/lib/components/GravatarImage.dart index a3f4efc..a1d2e8e 100644 --- a/lib/components/GravatarImage.dart +++ b/lib/components/GravatarImage.dart @@ -1,9 +1,45 @@ -import 'package:flutter/material.dart'; - +import 'package:flutter/painting.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()); +class GravatarImageProvider implements NetworkImage { + + String _email; + + GravatarImageProvider(this._email); + + @override + String get url { + return "https://secure.gravatar.com/avatar/" + + md5.convert(_email.trim().toLowerCase().codeUnits).toString(); + } + + @override + Map get headers { + // We don't need special headers for our request, so we can just return null here + return null; + } + + @override + ImageStreamCompleter load(NetworkImage key, DecoderCallback decode) { + return load(key, decode); + } + + @override + Future obtainKey(ImageConfiguration configuration) { + return obtainKey(configuration); + } + + @override + // Returning null here will set this to 1.0 + double get scale => null; + + @override + Future evict({ImageCache cache, ImageConfiguration configuration = ImageConfiguration.empty}) { + return evict(cache: cache,configuration: configuration); + } + + @override + ImageStream resolve(ImageConfiguration configuration) { + return resolve(configuration); + } } diff --git a/pubspec.lock b/pubspec.lock index 3e86bc3..6b0e247 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,5 +1,5 @@ # Generated by pub -# See https://www.dartlang.org/tools/pub/glossary#lockfile +# See https://dart.dev/tools/pub/glossary#lockfile packages: after_layout: dependency: "direct main" @@ -14,28 +14,28 @@ packages: name: archive url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" + version: "2.0.11" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "1.5.1" + version: "1.5.2" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" + version: "2.4.0" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "1.0.5" charcode: dependency: transitive description: @@ -63,7 +63,7 @@ packages: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.0.6" + version: "2.1.3" cupertino_icons: dependency: "direct main" description: @@ -124,49 +124,49 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.0.7" + version: "2.1.4" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.3+1" + version: "0.12.6" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.1.6" + version: "1.1.8" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.6.2" + version: "1.6.4" pedantic: dependency: transitive description: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.4.0" + version: "1.8.0+1" petitparser: dependency: transitive description: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.4.0" quiver: dependency: transitive description: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.5" sky_engine: dependency: transitive description: flutter @@ -178,7 +178,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.5.4" + version: "1.5.5" stack_trace: dependency: transitive description: @@ -192,14 +192,14 @@ packages: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "1.6.8" + version: "2.0.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "1.0.5" term_glyph: dependency: transitive description: @@ -213,7 +213,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.2" + version: "0.2.11" typed_data: dependency: transitive description: @@ -234,7 +234,7 @@ packages: name: xml url: "https://pub.dartlang.org" source: hosted - version: "3.3.1" + version: "3.5.0" yaml: dependency: transitive description: @@ -243,4 +243,4 @@ packages: source: hosted version: "2.1.15" sdks: - dart: ">=2.1.0 <3.0.0" + dart: ">=2.4.0 <3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index f5b5eb4..2985407 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ description: Vikunja as Flutter cross platform app version: 0.1.0 environment: - sdk: ">=2.0.0-dev.63.0 <3.0.0" + sdk: ">=2.1.0 <3.0.0" dependencies: flutter: -- 2.40.1 From f40982d926ed095b63c294e25a5ffd55236a6f6f Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 16:31:58 +0100 Subject: [PATCH 02/20] AndroidX --- android/gradle.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/gradle.properties b/android/gradle.properties index 7be3d8b..38c8d45 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,2 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true -- 2.40.1 From 0816cbae05106f09611d7b155f825676848f2671 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 16:37:40 +0100 Subject: [PATCH 03/20] Update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 816ad73..b0658dd 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ .pub/ build/ !pubspec.lock +.flutter-plugins-dependencies # Android related **/android/**/gradle-wrapper.jar -- 2.40.1 From 70308a4a7cd76e134eeeadc8d90d6025c2efdf79 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 16:38:10 +0100 Subject: [PATCH 04/20] Update packages for support for androidX --- android/settings_aar.gradle | 1 + ios/Flutter/flutter_export_environment.sh | 10 ++++++++++ pubspec.lock | 10 +++++----- pubspec.yaml | 6 +++--- 4 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 android/settings_aar.gradle create mode 100755 ios/Flutter/flutter_export_environment.sh diff --git a/android/settings_aar.gradle b/android/settings_aar.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/android/settings_aar.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/ios/Flutter/flutter_export_environment.sh b/ios/Flutter/flutter_export_environment.sh new file mode 100755 index 0000000..ba8483e --- /dev/null +++ b/ios/Flutter/flutter_export_environment.sh @@ -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" diff --git a/pubspec.lock b/pubspec.lock index 6b0e247..a781b71 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,7 +7,7 @@ packages: name: after_layout url: "https://pub.dartlang.org" source: hosted - version: "1.0.7" + version: "1.0.7+2" archive: dependency: transitive description: @@ -70,7 +70,7 @@ packages: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "0.1.2" + version: "0.1.3" dart_config: dependency: transitive description: @@ -98,7 +98,7 @@ packages: name: flutter_secure_storage url: "https://pub.dartlang.org" source: hosted - version: "3.1.1" + version: "3.3.1+1" flutter_test: dependency: "direct dev" description: flutter @@ -110,7 +110,7 @@ packages: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.12.0" + version: "0.12.0+3" http_parser: dependency: transitive description: @@ -241,6 +241,6 @@ packages: name: yaml url: "https://pub.dartlang.org" source: hosted - version: "2.1.15" + version: "2.2.0" sdks: dart: ">=2.4.0 <3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 2985407..f1567e1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,9 +12,9 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 - flutter_secure_storage: 3.1.1 - http: 0.12.0 + cupertino_icons: ^0.1.3 + flutter_secure_storage: 3.3.1+1 + http: 0.12.0+3 after_layout: ^1.0.7 dev_dependencies: -- 2.40.1 From 40882370dd2517242117b59b1986bfcb0ffe085b Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 16:40:27 +0100 Subject: [PATCH 05/20] Fix formatting --- lib/components/GravatarImage.dart | 7 +- lib/pages/home.dart | 8 +- lib/pages/list/list_edit.dart | 120 +++++++-------- lib/pages/namespace/namespace_edit.dart | 120 +++++++-------- lib/pages/user/login.dart | 148 +++++++++--------- lib/pages/user/register.dart | 193 ++++++++++++------------ pubspec.yaml | 3 - 7 files changed, 297 insertions(+), 302 deletions(-) diff --git a/lib/components/GravatarImage.dart b/lib/components/GravatarImage.dart index a1d2e8e..b7c7c96 100644 --- a/lib/components/GravatarImage.dart +++ b/lib/components/GravatarImage.dart @@ -2,7 +2,6 @@ import 'package:flutter/painting.dart'; import 'package:crypto/crypto.dart'; class GravatarImageProvider implements NetworkImage { - String _email; GravatarImageProvider(this._email); @@ -34,8 +33,10 @@ class GravatarImageProvider implements NetworkImage { double get scale => null; @override - Future evict({ImageCache cache, ImageConfiguration configuration = ImageConfiguration.empty}) { - return evict(cache: cache,configuration: configuration); + Future evict( + {ImageCache cache, + ImageConfiguration configuration = ImageConfiguration.empty}) { + return evict(cache: cache, configuration: configuration); } @override diff --git a/lib/pages/home.dart b/lib/pages/home.dart index 54210cc..b2c26d3 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -118,10 +118,10 @@ class HomePageState extends State with AfterLayoutMixin { alignment: FractionalOffset.bottomCenter, child: Builder( builder: (context) => ListTile( - leading: const Icon(Icons.add), - title: const Text('Add namespace...'), - onTap: () => _addNamespaceDialog(context), - ), + leading: const Icon(Icons.add), + title: const Text('Add namespace...'), + onTap: () => _addNamespaceDialog(context), + ), ), ), ])), diff --git a/lib/pages/list/list_edit.dart b/lib/pages/list/list_edit.dart index 7b0682f..b151e96 100644 --- a/lib/pages/list/list_edit.dart +++ b/lib/pages/list/list_edit.dart @@ -26,68 +26,68 @@ class _ListEditPageState extends State { ), body: Builder( builder: (BuildContext context) => SafeArea( - child: Form( - key: _formKey, - child: ListView( - padding: const EdgeInsets.all(16.0), - children: [ - Padding( - padding: EdgeInsets.symmetric(vertical: 10.0), - child: TextFormField( - maxLines: null, - keyboardType: TextInputType.multiline, - initialValue: widget.list.title, - onSaved: (title) => _title = title, - validator: (title) { - if (title.length < 3 || title.length > 250) { - return 'The title needs to have between 3 and 250 characters.'; - } - return null; - }, - decoration: new InputDecoration( - labelText: 'Title', - border: OutlineInputBorder(), - ), - ), + child: Form( + key: _formKey, + child: ListView( + padding: const EdgeInsets.all(16.0), + children: [ + Padding( + padding: EdgeInsets.symmetric(vertical: 10.0), + child: TextFormField( + maxLines: null, + keyboardType: TextInputType.multiline, + initialValue: widget.list.title, + onSaved: (title) => _title = title, + validator: (title) { + if (title.length < 3 || title.length > 250) { + return 'The title needs to have between 3 and 250 characters.'; + } + return null; + }, + decoration: new InputDecoration( + labelText: 'Title', + border: OutlineInputBorder(), ), - Padding( - padding: EdgeInsets.symmetric(vertical: 10.0), - child: TextFormField( - maxLines: null, - keyboardType: TextInputType.multiline, - initialValue: widget.list.description, - onSaved: (description) => _description = description, - validator: (description) { - if (description.length > 1000) { - return 'The description can have a maximum of 1000 characters.'; - } - return null; - }, - decoration: new InputDecoration( - labelText: 'Description', - border: OutlineInputBorder(), - ), - ), + ), + ), + Padding( + padding: EdgeInsets.symmetric(vertical: 10.0), + child: TextFormField( + maxLines: null, + keyboardType: TextInputType.multiline, + initialValue: widget.list.description, + onSaved: (description) => _description = description, + validator: (description) { + if (description.length > 1000) { + return 'The description can have a maximum of 1000 characters.'; + } + return null; + }, + decoration: new InputDecoration( + labelText: 'Description', + border: OutlineInputBorder(), ), - Builder( - builder: (context) => Padding( - padding: EdgeInsets.symmetric(vertical: 10.0), - child: FancyButton( - onPressed: !_loading - ? () { - if (_formKey.currentState.validate()) { - Form.of(context).save(); - _saveList(context); - } - } - : null, - child: _loading - ? CircularProgressIndicator() - : VikunjaButtonText('Save'), - ))), - ]), - ), - ), + ), + ), + Builder( + builder: (context) => Padding( + padding: EdgeInsets.symmetric(vertical: 10.0), + child: FancyButton( + onPressed: !_loading + ? () { + if (_formKey.currentState.validate()) { + Form.of(context).save(); + _saveList(context); + } + } + : null, + child: _loading + ? CircularProgressIndicator() + : VikunjaButtonText('Save'), + ))), + ]), + ), + ), ), ); } diff --git a/lib/pages/namespace/namespace_edit.dart b/lib/pages/namespace/namespace_edit.dart index d27cca1..a9d21cd 100644 --- a/lib/pages/namespace/namespace_edit.dart +++ b/lib/pages/namespace/namespace_edit.dart @@ -26,68 +26,68 @@ class _NamespaceEditPageState extends State { ), body: Builder( builder: (BuildContext context) => SafeArea( - child: Form( - key: _formKey, - child: ListView( - padding: const EdgeInsets.all(16.0), - children: [ - Padding( - padding: EdgeInsets.symmetric(vertical: 10.0), - child: TextFormField( - maxLines: null, - keyboardType: TextInputType.multiline, - initialValue: widget.namespace.name, - onSaved: (name) => _name = name, - validator: (name) { - if (name.length < 3 || name.length > 250) { - return 'The name needs to have between 3 and 250 characters.'; - } - return null; - }, - decoration: new InputDecoration( - labelText: 'Name', - border: OutlineInputBorder(), - ), - ), + child: Form( + key: _formKey, + child: ListView( + padding: const EdgeInsets.all(16.0), + children: [ + Padding( + padding: EdgeInsets.symmetric(vertical: 10.0), + child: TextFormField( + maxLines: null, + keyboardType: TextInputType.multiline, + initialValue: widget.namespace.name, + onSaved: (name) => _name = name, + validator: (name) { + if (name.length < 3 || name.length > 250) { + return 'The name needs to have between 3 and 250 characters.'; + } + return null; + }, + decoration: new InputDecoration( + labelText: 'Name', + border: OutlineInputBorder(), ), - Padding( - padding: EdgeInsets.symmetric(vertical: 10.0), - child: TextFormField( - maxLines: null, - keyboardType: TextInputType.multiline, - initialValue: widget.namespace.description, - onSaved: (description) => _description = description, - validator: (description) { - if (description.length > 1000) { - return 'The description can have a maximum of 1000 characters.'; - } - return null; - }, - decoration: new InputDecoration( - labelText: 'Description', - border: OutlineInputBorder(), - ), - ), + ), + ), + Padding( + padding: EdgeInsets.symmetric(vertical: 10.0), + child: TextFormField( + maxLines: null, + keyboardType: TextInputType.multiline, + initialValue: widget.namespace.description, + onSaved: (description) => _description = description, + validator: (description) { + if (description.length > 1000) { + return 'The description can have a maximum of 1000 characters.'; + } + return null; + }, + decoration: new InputDecoration( + labelText: 'Description', + border: OutlineInputBorder(), ), - Builder( - builder: (context) => Padding( - padding: EdgeInsets.symmetric(vertical: 10.0), - child: FancyButton( - onPressed: !_loading - ? () { - if (_formKey.currentState.validate()) { - Form.of(context).save(); - _saveNamespace(context); - } - } - : null, - child: _loading - ? CircularProgressIndicator() - : VikunjaButtonText('Save'), - ))), - ]), - ), - ), + ), + ), + Builder( + builder: (context) => Padding( + padding: EdgeInsets.symmetric(vertical: 10.0), + child: FancyButton( + onPressed: !_loading + ? () { + if (_formKey.currentState.validate()) { + Form.of(context).save(); + _saveNamespace(context); + } + } + : null, + child: _loading + ? CircularProgressIndicator() + : VikunjaButtonText('Save'), + ))), + ]), + ), + ), ), ); } diff --git a/lib/pages/user/login.dart b/lib/pages/user/login.dart index 411d92f..aca7e76 100644 --- a/lib/pages/user/login.dart +++ b/lib/pages/user/login.dart @@ -24,83 +24,81 @@ class _LoginPageState extends State { padding: const EdgeInsets.all(16.0), child: Builder( builder: (BuildContext context) => Form( - autovalidate: true, - key: _formKey, - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: EdgeInsets.symmetric(vertical: 30), - child: Image( - image: AssetImage('assets/vikunja_logo_full.png'), - height: 85.0, - 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'), - )), - ], + autovalidate: true, + key: _formKey, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.symmetric(vertical: 30), + child: Image( + image: AssetImage('assets/vikunja_logo_full.png'), + height: 85.0, + 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'), + )), + ], ), + ), + ), ), ), ), diff --git a/lib/pages/user/register.dart b/lib/pages/user/register.dart index 7d48285..fb9c0a7 100644 --- a/lib/pages/user/register.dart +++ b/lib/pages/user/register.dart @@ -24,103 +24,102 @@ class _RegisterPageState extends State { ), body: Builder( builder: (BuildContext context) => SafeArea( - top: false, - bottom: false, - child: Form( - key: _formKey, - child: ListView( - padding: const EdgeInsets.all(16), - children: [ - Padding( - padding: vStandardVerticalPadding, - child: TextFormField( - 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( - onSaved: (username) => _username = username.trim(), - validator: (username) { - return username.trim().isNotEmpty - ? null - : 'Please specify a username'; - }, - decoration: new InputDecoration( - border: OutlineInputBorder(), - labelText: 'Username'), - ), - ), - Padding( - padding: vStandardVerticalPadding, - child: TextFormField( - onSaved: (email) => _email = email, - validator: (email) { - return isEmail(email) - ? null - : 'Email adress is invalid'; - }, - decoration: new InputDecoration( - border: OutlineInputBorder(), - labelText: 'Email Address'), - ), - ), - Padding( - padding: vStandardVerticalPadding, - child: TextFormField( - controller: passwordController, - onSaved: (password) => _password = password, - validator: (password) { - return password.length >= 8 - ? null - : 'Please use at least 8 characters'; - }, - decoration: new InputDecoration( - border: OutlineInputBorder(), - labelText: 'Password'), - obscureText: true, - ), - ), - Padding( - padding: vStandardVerticalPadding, - child: TextFormField( - validator: (password) { - return passwordController.text == password - ? null - : 'Passwords don\'t match.'; - }, - decoration: new InputDecoration( - border: OutlineInputBorder(), - labelText: 'Repeat Password'), - obscureText: true, - ), - ), - Builder( - builder: (context) => FancyButton( - onPressed: !_loading - ? () { - if (_formKey.currentState - .validate()) { - Form.of(context).save(); - _registerUser(context); - } else { - print("awhat"); - } - } - : null, - child: _loading - ? CircularProgressIndicator() - : VikunjaButtonText('Register'), - )), - ], - )), - ), + top: false, + bottom: false, + child: Form( + key: _formKey, + child: ListView( + padding: const EdgeInsets.all(16), + children: [ + Padding( + padding: vStandardVerticalPadding, + child: TextFormField( + 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( + onSaved: (username) => _username = username.trim(), + validator: (username) { + return username.trim().isNotEmpty + ? null + : 'Please specify a username'; + }, + decoration: new InputDecoration( + border: OutlineInputBorder(), + labelText: 'Username'), + ), + ), + Padding( + padding: vStandardVerticalPadding, + child: TextFormField( + onSaved: (email) => _email = email, + validator: (email) { + return isEmail(email) + ? null + : 'Email adress is invalid'; + }, + decoration: new InputDecoration( + border: OutlineInputBorder(), + labelText: 'Email Address'), + ), + ), + Padding( + padding: vStandardVerticalPadding, + child: TextFormField( + controller: passwordController, + onSaved: (password) => _password = password, + validator: (password) { + return password.length >= 8 + ? null + : 'Please use at least 8 characters'; + }, + decoration: new InputDecoration( + border: OutlineInputBorder(), + labelText: 'Password'), + obscureText: true, + ), + ), + Padding( + padding: vStandardVerticalPadding, + child: TextFormField( + validator: (password) { + return passwordController.text == password + ? null + : 'Passwords don\'t match.'; + }, + decoration: new InputDecoration( + border: OutlineInputBorder(), + labelText: 'Repeat Password'), + obscureText: true, + ), + ), + Builder( + builder: (context) => FancyButton( + onPressed: !_loading + ? () { + if (_formKey.currentState.validate()) { + Form.of(context).save(); + _registerUser(context); + } else { + print("awhat"); + } + } + : null, + child: _loading + ? CircularProgressIndicator() + : VikunjaButtonText('Register'), + )), + ], + )), + ), )); } diff --git a/pubspec.yaml b/pubspec.yaml index f1567e1..067ec71 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,9 +9,6 @@ environment: dependencies: flutter: sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.3 flutter_secure_storage: 3.3.1+1 http: 0.12.0+3 -- 2.40.1 From 76bcb206c4c65d4eaa474a4cd8c95cabd5cd8a4b Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 18:18:22 +0100 Subject: [PATCH 06/20] Bump Gradle sdk version --- android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index f8df111..04e4c08 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 27 + compileSdkVersion 28 sourceSets { main.java.srcDirs += 'src/main/kotlin' -- 2.40.1 From 74ffc99ceafe788c197d3001a762f6ef245f8b4c Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 18:21:07 +0100 Subject: [PATCH 07/20] Switch CI build image --- .drone.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index b42ef20..5fa04e1 100644 --- a/.drone.yml +++ b/.drone.yml @@ -9,7 +9,7 @@ clone: steps: - name: build - image: vikunja/flutter-build + image: cirrusci/flutter:stable pull: true commands: - flutter packages get @@ -45,7 +45,7 @@ clone: 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. - name: build - image: vikunja/flutter-build + image: cirrusci/flutter:stable pull: true commands: - flutter packages get @@ -89,7 +89,7 @@ clone: 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. - name: build - image: vikunja/flutter-build + image: cirrusci/flutter:stable pull: true commands: - flutter packages get -- 2.40.1 From 0512e93c794f21945df132c0e0928afd052587e7 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 18:28:52 +0100 Subject: [PATCH 08/20] Fix drone permissions with different flutter build docker image --- .drone.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index 5fa04e1..c413bdd 100644 --- a/.drone.yml +++ b/.drone.yml @@ -2,7 +2,7 @@ kind: pipeline name: testing workspace: - base: /app + base: /home/cirrus/app clone: depth: 50 @@ -37,7 +37,7 @@ trigger: - push workspace: - base: /app + base: /home/cirrus/app clone: depth: 50 @@ -81,7 +81,7 @@ trigger: - tag workspace: - base: /app + base: /home/cirrus/app clone: depth: 50 -- 2.40.1 From 41a9255e86ed8a58a9957c65eeb3b876483dba5d Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 18:30:16 +0100 Subject: [PATCH 09/20] Drone debug --- .drone.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.drone.yml b/.drone.yml index c413bdd..5a4be6e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -12,6 +12,8 @@ steps: image: cirrusci/flutter:stable pull: true commands: + - pwd + - ls -hal - flutter packages get - make format-check - make build-debug -- 2.40.1 From 39f140aafac960b2d82f3c2a520da97d5fdc551a Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 18:38:18 +0100 Subject: [PATCH 10/20] "Fix" clone permissions --- .drone.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.drone.yml b/.drone.yml index 5a4be6e..21fade3 100644 --- a/.drone.yml +++ b/.drone.yml @@ -12,6 +12,7 @@ steps: image: cirrusci/flutter:stable pull: true commands: + - sudo chown 1000:1000 . -R # The clone step clones everything as root, this is our "fix" until we find a better solution - pwd - ls -hal - flutter packages get -- 2.40.1 From 21ed3141da2f0cfb94f7048f386cd6aeecbcb956 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 18:38:43 +0100 Subject: [PATCH 11/20] Format --- .drone.yml | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/.drone.yml b/.drone.yml index 21fade3..790e018 100644 --- a/.drone.yml +++ b/.drone.yml @@ -35,9 +35,9 @@ depends_on: trigger: branch: - - master + - master event: - - push + - push workspace: base: /home/cirrus/app @@ -51,10 +51,11 @@ steps: image: cirrusci/flutter:stable pull: true commands: - - flutter packages get - - make build-all - - mkdir apks - - mv build/app/outputs/apk/*/*/*.apk apks + - sudo chown 1000:1000 . -R # The clone step clones everything as root, this is our "fix" until we find a better solution + - flutter packages get + - make build-all + - mkdir apks + - mv build/app/outputs/apk/*/*/*.apk apks # Push the releases to our pseudo-s3-bucket - name: release @@ -81,7 +82,7 @@ depends_on: trigger: event: - - tag + - tag workspace: base: /home/cirrus/app @@ -95,10 +96,11 @@ steps: image: cirrusci/flutter:stable pull: true commands: - - flutter packages get - - make build-all - - mkdir apks - - mv build/app/outputs/apk/*/*/*.apk apks + - sudo chown 1000:1000 . -R # The clone step clones everything as root, this is our "fix" until we find a better solution + - flutter packages get + - make build-all + - mkdir apks + - mv build/app/outputs/apk/*/*/*.apk apks # Push the releases to our pseudo-s3-bucket - name: release image: plugins/s3:1 -- 2.40.1 From 936c057d3f2b306e9ef13fcd269de87d80d1f488 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 18:40:06 +0100 Subject: [PATCH 12/20] Use username instead of id --- .drone.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index 790e018..b4f5d54 100644 --- a/.drone.yml +++ b/.drone.yml @@ -12,7 +12,7 @@ steps: image: cirrusci/flutter:stable pull: true commands: - - sudo chown 1000:1000 . -R # The clone step clones everything as root, this is our "fix" until we find a better solution + - sudo chown cirrus . -R # The clone step clones everything as root, this is our "fix" until we find a better solution - pwd - ls -hal - flutter packages get @@ -51,7 +51,7 @@ steps: image: cirrusci/flutter:stable pull: true commands: - - sudo chown 1000:1000 . -R # The clone step clones everything as root, this is our "fix" until we find a better solution + - sudo chown cirrus . -R # The clone step clones everything as root, this is our "fix" until we find a better solution - flutter packages get - make build-all - mkdir apks @@ -96,7 +96,7 @@ steps: image: cirrusci/flutter:stable pull: true commands: - - sudo chown 1000:1000 . -R # The clone step clones everything as root, this is our "fix" until we find a better solution + - sudo chown cirrus . -R # The clone step clones everything as root, this is our "fix" until we find a better solution - flutter packages get - make build-all - mkdir apks -- 2.40.1 From 1a0ae9108cd94e868e94115389a093567d6f8ef5 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 18:40:47 +0100 Subject: [PATCH 13/20] Remove drone debug --- .drone.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index b4f5d54..27e9f05 100644 --- a/.drone.yml +++ b/.drone.yml @@ -13,8 +13,6 @@ steps: pull: true commands: - sudo chown cirrus . -R # The clone step clones everything as root, this is our "fix" until we find a better solution - - pwd - - ls -hal - flutter packages get - make format-check - make build-debug -- 2.40.1 From 9f8cf403cf7b9e3e99b53b4156fc8d116a4fd54a Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 18:44:04 +0100 Subject: [PATCH 14/20] Limit drone pipeline execution to master or pr --- .drone.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.drone.yml b/.drone.yml index 27e9f05..9422ffe 100644 --- a/.drone.yml +++ b/.drone.yml @@ -7,6 +7,16 @@ workspace: clone: depth: 50 +# Only run on prs or pushes to master +trigger: + branch: + include: + - master + event: + include: + - push + - pull_request + steps: - name: build image: cirrusci/flutter:stable -- 2.40.1 From e63a361e2cccfe154da3fe7b010810aadbb7e33c Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 19:15:08 +0100 Subject: [PATCH 15/20] Change target api version to 28 --- android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 04e4c08..084ea59 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -40,7 +40,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "io.vikunja.app" minSdkVersion 18 - targetSdkVersion 27 + targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" -- 2.40.1 From 73adcfc90350004b13758987683da587f5bddcd4 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 19:41:42 +0100 Subject: [PATCH 16/20] Set min sdk version to 19 --- android/app/build.gradle | 4 +--- android/build.gradle | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 084ea59..9e084af 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -37,14 +37,12 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "io.vikunja.app" - minSdkVersion 18 + minSdkVersion 19 targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - } flavorDimensions "deploy" diff --git a/android/build.gradle b/android/build.gradle index 830798d..cfc1d0e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -21,8 +21,6 @@ allprojects { rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { project.evaluationDependsOn(':app') } -- 2.40.1 From 3d8a0b5ccddcc7cf70ba7c46cf42f6faf663172e Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 22:09:23 +0100 Subject: [PATCH 17/20] Replace GravatarImageProvider --- lib/components/GravatarImage.dart | 46 ------------------------------- lib/models/user.dart | 7 +++++ lib/pages/home.dart | 8 +++--- 3 files changed, 11 insertions(+), 50 deletions(-) delete mode 100644 lib/components/GravatarImage.dart diff --git a/lib/components/GravatarImage.dart b/lib/components/GravatarImage.dart deleted file mode 100644 index b7c7c96..0000000 --- a/lib/components/GravatarImage.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/painting.dart'; -import 'package:crypto/crypto.dart'; - -class GravatarImageProvider implements NetworkImage { - String _email; - - GravatarImageProvider(this._email); - - @override - String get url { - return "https://secure.gravatar.com/avatar/" + - md5.convert(_email.trim().toLowerCase().codeUnits).toString(); - } - - @override - Map get headers { - // We don't need special headers for our request, so we can just return null here - return null; - } - - @override - ImageStreamCompleter load(NetworkImage key, DecoderCallback decode) { - return load(key, decode); - } - - @override - Future obtainKey(ImageConfiguration configuration) { - return obtainKey(configuration); - } - - @override - // Returning null here will set this to 1.0 - double get scale => null; - - @override - Future evict( - {ImageCache cache, - ImageConfiguration configuration = ImageConfiguration.empty}) { - return evict(cache: cache, configuration: configuration); - } - - @override - ImageStream resolve(ImageConfiguration configuration) { - return resolve(configuration); - } -} diff --git a/lib/models/user.dart b/lib/models/user.dart index 464d897..aad6fd1 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -1,3 +1,5 @@ +import 'package:crypto/crypto.dart'; + class User { final int id; final String email, username; @@ -9,6 +11,11 @@ class User { username = json['username']; toJSON() => {"id": this.id, "email": this.email, "username": this.username}; + + String avatarUrl() { + return "https://secure.gravatar.com/avatar/" + + md5.convert((this.email ?? "").trim().toLowerCase().codeUnits).toString(); + } } class UserTokenPair { diff --git a/lib/pages/home.dart b/lib/pages/home.dart index b2c26d3..c1092b0 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:after_layout/after_layout.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_edit.dart'; import 'package:vikunja_app/pages/placeholder.dart'; @@ -90,8 +89,8 @@ class HomePageState extends State with AfterLayoutMixin { drawer: new Drawer( child: new Column(children: [ new UserAccountsDrawerHeader( - accountEmail: currentUser == null ? null : Text(currentUser.email), - accountName: currentUser == null ? null : Text(currentUser.username), + accountEmail: currentUser?.email == null ? null : Text(currentUser.email), + accountName: currentUser?.username == null ? null : Text(currentUser.username), onDetailsPressed: () { setState(() { _showUserDetails = !_showUserDetails; @@ -100,7 +99,8 @@ class HomePageState extends State with AfterLayoutMixin { currentAccountPicture: currentUser == null ? null : CircleAvatar( - backgroundImage: GravatarImageProvider(currentUser.username)), + backgroundImage: NetworkImage(currentUser.avatarUrl()), + ), decoration: BoxDecoration( image: DecorationImage( image: AssetImage("assets/graphics/hypnotize.png"), -- 2.40.1 From 31361ae1f45609ef914301817c80de11db89dc38 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 1 Jan 2020 22:09:56 +0100 Subject: [PATCH 18/20] Format --- lib/models/user.dart | 4 +++- lib/pages/home.dart | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/models/user.dart b/lib/models/user.dart index aad6fd1..183cade 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -14,7 +14,9 @@ class User { String avatarUrl() { return "https://secure.gravatar.com/avatar/" + - md5.convert((this.email ?? "").trim().toLowerCase().codeUnits).toString(); + md5 + .convert((this.email ?? "").trim().toLowerCase().codeUnits) + .toString(); } } diff --git a/lib/pages/home.dart b/lib/pages/home.dart index c1092b0..e37c07e 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -89,8 +89,10 @@ class HomePageState extends State with AfterLayoutMixin { drawer: new Drawer( child: new Column(children: [ new UserAccountsDrawerHeader( - accountEmail: currentUser?.email == null ? null : Text(currentUser.email), - accountName: currentUser?.username == null ? null : Text(currentUser.username), + accountEmail: + currentUser?.email == null ? null : Text(currentUser.email), + accountName: + currentUser?.username == null ? null : Text(currentUser.username), onDetailsPressed: () { setState(() { _showUserDetails = !_showUserDetails; @@ -100,7 +102,7 @@ class HomePageState extends State with AfterLayoutMixin { ? null : CircleAvatar( backgroundImage: NetworkImage(currentUser.avatarUrl()), - ), + ), decoration: BoxDecoration( image: DecorationImage( image: AssetImage("assets/graphics/hypnotize.png"), -- 2.40.1 From 4d61a6e91881e8fb5db40db5e0e8775c2a43522e Mon Sep 17 00:00:00 2001 From: kolaente Date: Thu, 9 Jan 2020 19:02:33 +0100 Subject: [PATCH 19/20] Use user avatar hash instead of calculating it from the email --- lib/global.dart | 4 ++-- lib/models/user.dart | 14 +++++--------- lib/service/mocked_services.dart | 2 +- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/global.dart b/lib/global.dart index 061ae99..0f8081a 100644 --- a/lib/global.dart +++ b/lib/global.dart @@ -120,9 +120,9 @@ class VikunjaGlobalState extends State { }); return; } - loadedCurrentUser = User(int.tryParse(currentUser), "", ""); + loadedCurrentUser = User(int.tryParse(currentUser), "", "", ""); } catch (otherExceptions) { - loadedCurrentUser = User(int.tryParse(currentUser), "", ""); + loadedCurrentUser = User(int.tryParse(currentUser), "", "", ""); } setState(() { _client = client; diff --git a/lib/models/user.dart b/lib/models/user.dart index 183cade..a414850 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -1,22 +1,18 @@ -import 'package:crypto/crypto.dart'; - class User { 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 json) : id = json['id'], email = json['email'], - username = json['username']; + username = json['username'], + avatarHash = json['avatarUrl']; toJSON() => {"id": this.id, "email": this.email, "username": this.username}; String avatarUrl() { - return "https://secure.gravatar.com/avatar/" + - md5 - .convert((this.email ?? "").trim().toLowerCase().codeUnits) - .toString(); + return "https://secure.gravatar.com/avatar/" + this.avatarHash; } } diff --git a/lib/service/mocked_services.dart b/lib/service/mocked_services.dart index 35d5fbb..98913e1 100644 --- a/lib/service/mocked_services.dart +++ b/lib/service/mocked_services.dart @@ -7,7 +7,7 @@ import 'package:vikunja_app/models/user.dart'; import 'package:vikunja_app/service/services.dart'; // Data for mocked services -var _users = {1: User(1, 'test@testuser.org', 'test1')}; +var _users = {1: User(1, 'test@testuser.org', 'test1', '')}; var _namespaces = { 1: Namespace( -- 2.40.1 From 7619eee570c77fe6309bf7f282a59c663bc1c885 Mon Sep 17 00:00:00 2001 From: kolaente Date: Thu, 9 Jan 2020 19:34:38 +0100 Subject: [PATCH 20/20] Fix parsing of user model if email is not present --- lib/models/user.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/models/user.dart b/lib/models/user.dart index a414850..17f7639 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -5,7 +5,7 @@ class User { User(this.id, this.email, this.username, this.avatarHash); User.fromJson(Map json) : id = json['id'], - email = json['email'], + email = json.containsKey('email') ? json['email'] : '', username = json['username'], avatarHash = json['avatarUrl']; -- 2.40.1