diff --git a/lib/api/list_implementation.dart b/lib/api/list_implementation.dart index eff3121..38228d7 100644 --- a/lib/api/list_implementation.dart +++ b/lib/api/list_implementation.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:vikunja_app/api/client.dart'; import 'package:vikunja_app/api/service.dart'; -import 'package:vikunja_app/models/task.dart'; +import 'package:vikunja_app/models/list.dart'; import 'package:vikunja_app/service/services.dart'; class ListAPIService extends APIService implements ListService { @@ -40,7 +40,7 @@ class ListAPIService extends APIService implements ListService { @override Future update(TaskList tl) { return client - .put('/lists/${tl.id}', body: tl.toJSON()) + .post('/lists/${tl.id}', body: tl.toJSON()) .then((map) => TaskList.fromJson(map)); } } diff --git a/lib/components/TaskTile.dart b/lib/components/TaskTile.dart index 470f437..348e6e2 100644 --- a/lib/components/TaskTile.dart +++ b/lib/components/TaskTile.dart @@ -46,7 +46,11 @@ class TaskTileState extends State { _currentTask.description == null || _currentTask.description.isEmpty ? null : Text(_currentTask.description), - trailing: IconButton(icon: Icon(Icons.settings), onPressed: null), + trailing: IconButton( + icon: Icon(Icons.settings), + onPressed: () { + null; + }), ); } return CheckboxListTile( diff --git a/lib/fragments/namespace.dart b/lib/fragments/namespace.dart index afde431..ccdbcac 100644 --- a/lib/fragments/namespace.dart +++ b/lib/fragments/namespace.dart @@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:vikunja_app/components/AddDialog.dart'; import 'package:vikunja_app/global.dart'; +import 'package:vikunja_app/models/list.dart'; import 'package:vikunja_app/models/namespace.dart'; import 'package:vikunja_app/models/task.dart'; import 'package:vikunja_app/pages/list_page.dart'; diff --git a/lib/models/list.dart b/lib/models/list.dart new file mode 100644 index 0000000..38ef0af --- /dev/null +++ b/lib/models/list.dart @@ -0,0 +1,42 @@ +import 'package:meta/meta.dart'; +import 'package:vikunja_app/models/task.dart'; +import 'package:vikunja_app/models/user.dart'; + +class TaskList { + final int id; + final String title, description; + final User owner; + final DateTime created, updated; + final List tasks; + + TaskList( + {@required this.id, + @required this.title, + this.description, + this.owner, + this.created, + this.updated, + this.tasks}); + + TaskList.fromJson(Map json) + : id = json['id'], + owner = User.fromJson(json['owner']), + description = json['description'], + title = json['title'], + updated = DateTime.fromMillisecondsSinceEpoch(json['updated']), + created = DateTime.fromMillisecondsSinceEpoch(json['created']), + tasks = (json['tasks'] as List) + ?.map((taskJson) => Task.fromJson(taskJson)) + ?.toList(); + + toJSON() { + return { + "id": this.id, + "title": this.title, + "description": this.description, + "owner": this.owner?.toJSON(), + "created": this.created?.millisecondsSinceEpoch, + "updated": this.updated?.millisecondsSinceEpoch, + }; + } +} diff --git a/lib/models/task.dart b/lib/models/task.dart index c96a583..897f63e 100644 --- a/lib/models/task.dart +++ b/lib/models/task.dart @@ -46,41 +46,3 @@ class Task { 'createdBy': owner?.toJSON() }; } - -class TaskList { - final int id; - final String title, description; - final User owner; - final DateTime created, updated; - final List tasks; - - TaskList( - {@required this.id, - @required this.title, - this.description, - this.owner, - this.created, - this.updated, - @required this.tasks}); - - TaskList.fromJson(Map json) - : id = json['id'], - owner = User.fromJson(json['owner']), - description = json['description'], - title = json['title'], - updated = DateTime.fromMillisecondsSinceEpoch(json['updated']), - created = DateTime.fromMillisecondsSinceEpoch(json['created']), - tasks = (json['tasks'] as List) - ?.map((taskJson) => Task.fromJson(taskJson)) - ?.toList(); - - toJSON() { - return { - "created": this.created?.millisecondsSinceEpoch, - "updated": this.updated?.millisecondsSinceEpoch, - "id": this.id, - "title": this.title, - "owner": this.owner?.toJSON() - }; - } -} diff --git a/lib/pages/list_edit_page.dart b/lib/pages/list_edit_page.dart new file mode 100644 index 0000000..c81ffc9 --- /dev/null +++ b/lib/pages/list_edit_page.dart @@ -0,0 +1,119 @@ +import 'package:flutter/material.dart'; +import 'package:vikunja_app/global.dart'; +import 'package:vikunja_app/models/list.dart'; + +class ListEditPage extends StatefulWidget { + final TaskList list; + + ListEditPage({this.list}) : super(key: Key(list.toString())); + + @override + State createState() => _ListEditPageState(); +} + +class _ListEditPageState extends State { + final _formKey = GlobalKey(); + bool _loading = false; + String _title, _description; + + @override + Widget build(BuildContext ctx) { + return Scaffold( + appBar: AppBar( + title: Text('Edit List'), + ), + body: Builder( + builder: (BuildContext context) => SafeArea( + child: Form( + key: _formKey, + child: ListView( + padding: const EdgeInsets.symmetric(horizontal: 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'), + ), + ), + 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'), + ), + ), + Builder( + builder: (context) => RaisedButton( + padding: + EdgeInsets.symmetric(vertical: 10.0), + onPressed: !_loading + ? () { + if (_formKey.currentState + .validate()) { + Form.of(context) + .save(); // Why does this not work? + _saveList(context); + } else { + print( + "sdf"); // TODO: handle error + } + } + : null, + child: _loading + ? CircularProgressIndicator() + : Text('Save'), + )), + ])), + ))); + } + + _saveList(BuildContext context) async { + 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); + + VikunjaGlobal.of(context).listService.update(updatedList) + .then((_) { + setState(() => _loading = false); + final scaffold = Scaffold.of(context); + scaffold.showSnackBar(SnackBar( + content: Text('The list was updated successfully!'), + )); + }) + .catchError((err) { + setState(() => _loading = false); + final scaffold = Scaffold.of(context); + scaffold.showSnackBar( + SnackBar( + content: Text('Something went wrong: ' + err.toString()), + action: SnackBarAction( + label: 'CLOSE', onPressed: scaffold.hideCurrentSnackBar), + ), + ); + }); + } +} diff --git a/lib/pages/list_page.dart b/lib/pages/list_page.dart index 4ff9a96..4a5a57e 100644 --- a/lib/pages/list_page.dart +++ b/lib/pages/list_page.dart @@ -4,7 +4,9 @@ import 'package:flutter/material.dart'; import 'package:vikunja_app/components/AddDialog.dart'; import 'package:vikunja_app/components/TaskTile.dart'; import 'package:vikunja_app/global.dart'; +import 'package:vikunja_app/models/list.dart'; import 'package:vikunja_app/models/task.dart'; +import 'package:vikunja_app/pages/list_edit_page.dart'; class ListPage extends StatefulWidget { final TaskList taskList; @@ -16,13 +18,13 @@ class ListPage extends StatefulWidget { } class _ListPageState extends State { - TaskList _items; + TaskList _list; List _loadingTasks = []; bool _loading = true; @override void initState() { - _items = TaskList( + _list = TaskList( id: widget.taskList.id, title: widget.taskList.title, tasks: []); super.initState(); } @@ -37,12 +39,16 @@ class _ListPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: new Text(_items.title), + title: new Text(_list.title), actions: [ IconButton( - icon: Icon(Icons.edit), - onPressed: () => {/* TODO add edit list functionality */}, - ) + icon: Icon(Icons.edit), + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ListEditPage( + list: _list, + )))) ], ), body: !this._loading @@ -62,7 +68,7 @@ class _ListPageState extends State { } List _listTasks() { - var tasks = (_items?.tasks?.map(_buildTile) ?? []).toList(); + var tasks = (_list?.tasks?.map(_buildTile) ?? []).toList(); tasks.addAll(_loadingTasks.map(_buildLoadingTile)); return tasks; } @@ -85,7 +91,7 @@ class _ListPageState extends State { .then((tasks) { setState(() { _loading = false; - _items = tasks; + _list = tasks; }); }); } @@ -104,9 +110,9 @@ class _ListPageState extends State { var newTask = Task(id: null, text: name, owner: globalState.currentUser, done: false); setState(() => _loadingTasks.add(newTask)); - globalState.taskService.add(_items.id, newTask).then((task) { + globalState.taskService.add(_list.id, newTask).then((task) { setState(() { - _items.tasks.add(task); + _list.tasks.add(task); }); }).then((_) => _updateList() .then((_) => setState(() => _loadingTasks.remove(newTask)))); diff --git a/lib/service/mocked_services.dart b/lib/service/mocked_services.dart index df52b6b..35d5fbb 100644 --- a/lib/service/mocked_services.dart +++ b/lib/service/mocked_services.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:vikunja_app/models/list.dart'; import 'package:vikunja_app/models/namespace.dart'; import 'package:vikunja_app/models/task.dart'; import 'package:vikunja_app/models/user.dart'; diff --git a/lib/service/services.dart b/lib/service/services.dart index be9f7b8..54f8c51 100644 --- a/lib/service/services.dart +++ b/lib/service/services.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:vikunja_app/models/list.dart'; import 'package:vikunja_app/models/namespace.dart'; import 'package:vikunja_app/models/task.dart'; import 'package:vikunja_app/models/user.dart';