List edit #21

Merged
JonasFranz merged 8 commits from feature/task-edit into master 2019-03-11 20:29:16 +00:00
9 changed files with 187 additions and 51 deletions

View File

@ -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<TaskList> update(TaskList tl) {
return client
.put('/lists/${tl.id}', body: tl.toJSON())
.post('/lists/${tl.id}', body: tl.toJSON())
.then((map) => TaskList.fromJson(map));
}
}

View File

@ -46,7 +46,11 @@ class TaskTileState extends State<TaskTile> {
_currentTask.description == null || _currentTask.description.isEmpty
? null
: Text(_currentTask.description),
trailing: IconButton(icon: Icon(Icons.settings), onPressed: null),
trailing: IconButton(
icon: Icon(Icons.settings),

Unrelated change

Unrelated change
onPressed: () {
null;
}),
);
}
return CheckboxListTile(

View File

@ -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';

42
lib/models/list.dart Normal file
View File

@ -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<Task> tasks;
TaskList(
{@required this.id,
@required this.title,
this.description,
this.owner,
this.created,
this.updated,
this.tasks});
TaskList.fromJson(Map<String, dynamic> 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<dynamic>)
?.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,

Wrong indention, make format

Wrong indention, make format
"updated": this.updated?.millisecondsSinceEpoch,
};
}
}

View File

@ -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<Task> tasks;
TaskList(
{@required this.id,
@required this.title,
this.description,
this.owner,
this.created,
this.updated,
@required this.tasks});
TaskList.fromJson(Map<String, dynamic> 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<dynamic>)
?.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()
};
}
}

View File

@ -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<StatefulWidget> createState() => _ListEditPageState();
}
class _ListEditPageState extends State<ListEditPage> {
final _formKey = GlobalKey<FormState>();
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: <Widget>[
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?)
Review

That's not possible. You may add a function like copyWith(...) as helper. But this may be to big for the problem.

That's not possible. You may add a function like copyWith(...) as helper. But this may be to big for the problem.
Review

I see, thanks!

I see, thanks!
Review

I see, thanks!

I see, thanks!
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),
),
);
});
}
}

catch won't work here. You should use .catch instead

catch won't work here. You should use .catch instead

If I do this, is the whole try catch block still nessecary?

If I do this, is the whole `try` `catch` block still nessecary?

No, it's not since the that stuff is asynchron and will finish after the function already returned.

No, it's not since the that stuff is asynchron and will finish after the function already returned.
Review

Done.

Done.

View File

@ -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<ListPage> {
TaskList _items;
TaskList _list;
List<Task> _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<ListPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: new Text(_items.title),
title: new Text(_list.title),
actions: <Widget>[
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<ListPage> {
}
List<Widget> _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<ListPage> {
.then((tasks) {
setState(() {
_loading = false;
_items = tasks;
_list = tasks;
});
});
}
@ -104,9 +110,9 @@ class _ListPageState extends State<ListPage> {
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))));

View File

@ -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';

View File

@ -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';