diff --git a/.drone.yml b/.drone.yml index 89cbe4d..caa2d57 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,58 +1,117 @@ +kind: pipeline +name: testing + workspace: base: /app clone: - git: - image: plugins/git - depth: 50 - tags: true + depth: 50 -pipeline: - build: - image: jonasfranz/flutter:master - pull: true - commands: - - flutter packages get - - make format-check - - make build-all - - mkdir apks - - mv build/app/outputs/apk/*/*/*.apk apks - when: - event: [ push, tag ] +steps: + - name: build + image: adamantium/flutter + pull: true + commands: + - apt-get update && apt-get install make # We do this here to not have to deal with updating the image itself + - flutter packages get + - make format-check + - make build-debug - test: - image: jonasfranz/flutter:master - pull: true - commands: - - make test - when: - event: [ push, tag, pull_request ] - - # Push the releases to our pseudo-s3-bucket - release: + - name: test + image: adamantium/flutter + pull: true + commands: + - apt-get update && apt-get install make # We do this here to not have to deal with updating the image itself + - flutter packages get + - make test + +--- +kind: pipeline +name: release-latest + +depends_on: + - testing + +trigger: + branch: + - master + event: + - push + +workspace: + base: /app + +clone: + depth: 50 + +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: adamantium/flutter + pull: true + commands: + - apt-get update && apt-get install make # We do this here to not have to deal with updating the image itself + - 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 pull: true - secrets: [ aws_access_key_id, aws_secret_access_key ] - bucket: vikunja-app - endpoint: https://storage.kolaente.de - path_style: true - strip_prefix: apks/ - source: apks/* - target: /${DRONE_TAG##v} - when: - event: [ tag ] + settings: + bucket: vikunja-app + access_key: + from_secret: aws_access_key_id + secret_key: + from_secret: aws_secret_access_key + endpoint: https://storage.kolaente.de + path_style: true + strip_prefix: apks/ + source: apks/* + target: /master - # Push the releases to our pseudo-s3-bucket - release: +--- +kind: pipeline +name: release-version + +depends_on: + - testing + +trigger: + event: + - tag + +workspace: + base: /app + +clone: + depth: 50 + +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: adamantium/flutter + pull: true + commands: + - apt-get update && apt-get install make # We do this here to not have to deal with updating the image itself + - 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 pull: true - secrets: [ aws_access_key_id, aws_secret_access_key ] - bucket: vikunja-app - endpoint: https://storage.kolaente.de - path_style: true - strip_prefix: apks/ - source: apks/* - target: /master - when: - event: [ push ] - branch: [ master ] + settings: + bucket: vikunja-app + access_key: + from_secret: aws_access_key_id + secret_key: + from_secret: aws_secret_access_key + endpoint: https://storage.kolaente.de + path_style: true + strip_prefix: apks/ + source: apks/* + target: /${DRONE_TAG##v} diff --git a/.gitignore b/.gitignore index 5be27ae..816ad73 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ .pub-cache/ .pub/ build/ +!pubspec.lock # Android related **/android/**/gradle-wrapper.jar diff --git a/README.md b/README.md index 42d99af..af1e2eb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Vikunja Cross-Plattform app -[![Build Status](https://drone.kolaente.de/api/badges/vikunja/app/status.svg)](https://drone.kolaente.de/vikunja/app) +[![Build Status](https://drone1.kolaente.de/api/badges/vikunja/app/status.svg)](https://drone1.kolaente.de/vikunja/app) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Download](https://img.shields.io/badge/download-v0.1-brightgreen.svg)](https://storage.kolaente.de/minio/vikunja-app/) diff --git a/lib/fragments/namespace.dart b/lib/fragments/namespace.dart index f77d68a..64817c7 100644 --- a/lib/fragments/namespace.dart +++ b/lib/fragments/namespace.dart @@ -57,8 +57,8 @@ class _NamespaceFragmentState extends State { : Center(child: CircularProgressIndicator()), floatingActionButton: Builder( builder: (context) => FloatingActionButton( - onPressed: () => _addListDialog(context), child: const Icon(Icons.add)) - ), + onPressed: () => _addListDialog(context), + child: const Icon(Icons.add))), ); } @@ -102,16 +102,16 @@ class _NamespaceFragmentState extends State { _addList(String name, BuildContext context) { VikunjaGlobal.of(context) - .listService - .create(widget.namespace.id, TaskList(id: null, title: name, tasks: [])) - .then((_) { - setState(() {}); - _updateLists(); - Scaffold.of(context).showSnackBar( - SnackBar( - content: Text('The list was successfully created!'), - ), - ); + .listService + .create(widget.namespace.id, TaskList(id: null, title: name, tasks: [])) + .then((_) { + setState(() {}); + _updateLists(); + Scaffold.of(context).showSnackBar( + SnackBar( + content: Text('The list was successfully created!'), + ), + ); }); } } diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index cb001d3..55562bb 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -51,10 +51,10 @@ class HomePageState extends State { .namespaceService .create(Namespace(id: null, name: name)) .then((_) { - _updateNamespaces(); - Scaffold.of(context).showSnackBar(SnackBar( - content: Text('The namespace was created successfully!'), - )); + _updateNamespaces(); + Scaffold.of(context).showSnackBar(SnackBar( + content: Text('The namespace was created successfully!'), + )); }); } @@ -120,10 +120,10 @@ class HomePageState extends State { 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_edit_page.dart b/lib/pages/list_edit_page.dart index 683d9ad..7b0682f 100644 --- a/lib/pages/list_edit_page.dart +++ b/lib/pages/list_edit_page.dart @@ -26,71 +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(), - ), - ), - ), - 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'), - ) - ) - ), - ] + 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'), + ))), + ]), + ), ), - ), - ), ), ); } @@ -99,25 +96,24 @@ class _ListEditPageState extends State { setState(() => _loading = true); // FIXME: is there a way we can update the list without creating a new list object? // aka updating the existing list we got from context (setters?) - TaskList updatedList = TaskList( - id: widget.list.id, title: _title, description: _description); + TaskList updatedList = + TaskList(id: widget.list.id, title: _title, description: _description); - VikunjaGlobal.of(context).listService.update(updatedList) - .then((_) { - setState(() => _loading = false); - Scaffold.of(context).showSnackBar(SnackBar( - content: Text('The list was updated successfully!'), - )); - }) - .catchError((err) { - setState(() => _loading = false); - Scaffold.of(context).showSnackBar( - SnackBar( - content: Text('Something went wrong: ' + err.toString()), - action: SnackBarAction( - label: 'CLOSE', onPressed: Scaffold.of(context).hideCurrentSnackBar), - ), - ); - }); + VikunjaGlobal.of(context).listService.update(updatedList).then((_) { + setState(() => _loading = false); + Scaffold.of(context).showSnackBar(SnackBar( + content: Text('The list was updated successfully!'), + )); + }).catchError((err) { + setState(() => _loading = false); + Scaffold.of(context).showSnackBar( + SnackBar( + content: Text('Something went wrong: ' + err.toString()), + action: SnackBarAction( + label: 'CLOSE', + onPressed: Scaffold.of(context).hideCurrentSnackBar), + ), + ); + }); } } diff --git a/lib/pages/list_page.dart b/lib/pages/list_page.dart index 1f63cd5..a99efb9 100644 --- a/lib/pages/list_page.dart +++ b/lib/pages/list_page.dart @@ -38,34 +38,34 @@ class _ListPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: new Text(_list.title), - actions: [ - IconButton( - icon: Icon(Icons.edit), - onPressed: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ListEditPage( - list: _list, - )))) - ], - ), - body: !this._loading - ? RefreshIndicator( - child: ListView( - padding: EdgeInsets.symmetric(vertical: 8.0), - children: - ListTile.divideTiles(context: context, tiles: _listTasks()) - .toList(), - ), - onRefresh: _loadList, - ) - : Center(child: CircularProgressIndicator()), - floatingActionButton: Builder( - builder: (context) => FloatingActionButton( - onPressed: () => _addItemDialog(context), child: Icon(Icons.add)), - )); + appBar: AppBar( + title: new Text(_list.title), + actions: [ + IconButton( + icon: Icon(Icons.edit), + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ListEditPage( + list: _list, + )))) + ], + ), + body: !this._loading + ? RefreshIndicator( + child: ListView( + padding: EdgeInsets.symmetric(vertical: 8.0), + children: ListTile.divideTiles( + context: context, tiles: _listTasks()) + .toList(), + ), + onRefresh: _loadList, + ) + : Center(child: CircularProgressIndicator()), + floatingActionButton: Builder( + builder: (context) => FloatingActionButton( + onPressed: () => _addItemDialog(context), child: Icon(Icons.add)), + )); } List _listTasks() { diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index ab718b0..a7f119d 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -24,86 +24,83 @@ 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', - ), + 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'), + )), + ], ), - 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/register_page.dart b/lib/pages/register_page.dart index 3e5c201..7d48285 100644 --- a/lib/pages/register_page.dart +++ b/lib/pages/register_page.dart @@ -137,7 +137,9 @@ class _RegisterPageState extends State { showDialog( context: context, builder: (context) => new AlertDialog( - title: Text('Registration failed! Please check your server url and credentials. ' + ex.toString()), + title: Text( + 'Registration failed! Please check your server url and credentials. ' + + ex.toString()), actions: [ FlatButton( onPressed: () => Navigator.pop(context), diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..8024014 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,232 @@ +# Generated by pub +# See https://www.dartlang.org/tools/pub/glossary#lockfile +packages: + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.1" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.14.11" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" + dart_config: + dependency: transitive + description: + path: "." + ref: HEAD + resolved-ref: a7ed88a4793e094a4d5d5c2d88a89e55510accde + url: "https://github.com/MarkOSullivan94/dart_config.git" + source: git + version: "0.5.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.1" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: "direct main" + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.0" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.3" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.3+1" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.2" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.3" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.8" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.1" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.15" +sdks: + dart: ">=2.0.0 <3.0.0"