List edit #21
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
||||
onPressed: () {
|
||||
null;
|
||||
}),
|
||||
);
|
||||
}
|
||||
return CheckboxListTile(
|
||||
|
|
|
@ -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
42
lib/models/list.dart
Normal 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,
|
||||
JonasFranz
commented
Wrong indention, make format Wrong indention, make format
|
||||
"updated": this.updated?.millisecondsSinceEpoch,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
119
lib/pages/list_edit_page.dart
Normal file
119
lib/pages/list_edit_page.dart
Normal 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?)
|
||||
JonasFranz
commented
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.
konrad
commented
I see, thanks! I see, thanks!
konrad
commented
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),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
JonasFranz
commented
catch won't work here. You should use .catch instead catch won't work here. You should use .catch instead
konrad
commented
If I do this, is the whole If I do this, is the whole `try` `catch` block still nessecary?
JonasFranz
commented
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.
konrad
commented
Done. Done.
|
|
@ -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))));
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
Reference in New Issue
Block a user
Unrelated change