parent
d30ea0f91c
commit
c285120034
|
@ -2,7 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:vikunja_app/api/client.dart';
|
import 'package:vikunja_app/api/client.dart';
|
||||||
import 'package:vikunja_app/api/service.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';
|
import 'package:vikunja_app/service/services.dart';
|
||||||
|
|
||||||
class ListAPIService extends APIService implements ListService {
|
class ListAPIService extends APIService implements ListService {
|
||||||
|
@ -40,7 +40,7 @@ class ListAPIService extends APIService implements ListService {
|
||||||
@override
|
@override
|
||||||
Future<TaskList> update(TaskList tl) {
|
Future<TaskList> update(TaskList tl) {
|
||||||
return client
|
return client
|
||||||
.put('/lists/${tl.id}', body: tl.toJSON())
|
.post('/lists/${tl.id}', body: tl.toJSON())
|
||||||
.then((map) => TaskList.fromJson(map));
|
.then((map) => TaskList.fromJson(map));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,11 @@ class TaskTileState extends State<TaskTile> {
|
||||||
_currentTask.description == null || _currentTask.description.isEmpty
|
_currentTask.description == null || _currentTask.description.isEmpty
|
||||||
? null
|
? null
|
||||||
: Text(_currentTask.description),
|
: Text(_currentTask.description),
|
||||||
trailing: IconButton(icon: Icon(Icons.settings), onPressed: null),
|
trailing: IconButton(
|
||||||
|
icon: Icon(Icons.settings),
|
||||||
|
onPressed: () {
|
||||||
|
null;
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return CheckboxListTile(
|
return CheckboxListTile(
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:vikunja_app/components/AddDialog.dart';
|
import 'package:vikunja_app/components/AddDialog.dart';
|
||||||
import 'package:vikunja_app/global.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/namespace.dart';
|
||||||
import 'package:vikunja_app/models/task.dart';
|
import 'package:vikunja_app/models/task.dart';
|
||||||
import 'package:vikunja_app/pages/list_page.dart';
|
import 'package:vikunja_app/pages/list_page.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<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,
|
||||||
|
"updated": this.updated?.millisecondsSinceEpoch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,41 +46,3 @@ class Task {
|
||||||
'createdBy': owner?.toJSON()
|
'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()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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?)
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,9 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:vikunja_app/components/AddDialog.dart';
|
import 'package:vikunja_app/components/AddDialog.dart';
|
||||||
import 'package:vikunja_app/components/TaskTile.dart';
|
import 'package:vikunja_app/components/TaskTile.dart';
|
||||||
import 'package:vikunja_app/global.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/models/task.dart';
|
||||||
|
import 'package:vikunja_app/pages/list_edit_page.dart';
|
||||||
|
|
||||||
class ListPage extends StatefulWidget {
|
class ListPage extends StatefulWidget {
|
||||||
final TaskList taskList;
|
final TaskList taskList;
|
||||||
|
@ -16,13 +18,13 @@ class ListPage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ListPageState extends State<ListPage> {
|
class _ListPageState extends State<ListPage> {
|
||||||
TaskList _items;
|
TaskList _list;
|
||||||
List<Task> _loadingTasks = [];
|
List<Task> _loadingTasks = [];
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_items = TaskList(
|
_list = TaskList(
|
||||||
id: widget.taskList.id, title: widget.taskList.title, tasks: []);
|
id: widget.taskList.id, title: widget.taskList.title, tasks: []);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
@ -37,12 +39,16 @@ class _ListPageState extends State<ListPage> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: new Text(_items.title),
|
title: new Text(_list.title),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.edit),
|
icon: Icon(Icons.edit),
|
||||||
onPressed: () => {/* TODO add edit list functionality */},
|
onPressed: () => Navigator.push(
|
||||||
)
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ListEditPage(
|
||||||
|
list: _list,
|
||||||
|
))))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: !this._loading
|
body: !this._loading
|
||||||
|
@ -62,7 +68,7 @@ class _ListPageState extends State<ListPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _listTasks() {
|
List<Widget> _listTasks() {
|
||||||
var tasks = (_items?.tasks?.map(_buildTile) ?? []).toList();
|
var tasks = (_list?.tasks?.map(_buildTile) ?? []).toList();
|
||||||
tasks.addAll(_loadingTasks.map(_buildLoadingTile));
|
tasks.addAll(_loadingTasks.map(_buildLoadingTile));
|
||||||
return tasks;
|
return tasks;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +91,7 @@ class _ListPageState extends State<ListPage> {
|
||||||
.then((tasks) {
|
.then((tasks) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_loading = false;
|
_loading = false;
|
||||||
_items = tasks;
|
_list = tasks;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -104,9 +110,9 @@ class _ListPageState extends State<ListPage> {
|
||||||
var newTask =
|
var newTask =
|
||||||
Task(id: null, text: name, owner: globalState.currentUser, done: false);
|
Task(id: null, text: name, owner: globalState.currentUser, done: false);
|
||||||
setState(() => _loadingTasks.add(newTask));
|
setState(() => _loadingTasks.add(newTask));
|
||||||
globalState.taskService.add(_items.id, newTask).then((task) {
|
globalState.taskService.add(_list.id, newTask).then((task) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_items.tasks.add(task);
|
_list.tasks.add(task);
|
||||||
});
|
});
|
||||||
}).then((_) => _updateList()
|
}).then((_) => _updateList()
|
||||||
.then((_) => setState(() => _loadingTasks.remove(newTask))));
|
.then((_) => setState(() => _loadingTasks.remove(newTask))));
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:vikunja_app/models/list.dart';
|
||||||
import 'package:vikunja_app/models/namespace.dart';
|
import 'package:vikunja_app/models/namespace.dart';
|
||||||
import 'package:vikunja_app/models/task.dart';
|
import 'package:vikunja_app/models/task.dart';
|
||||||
import 'package:vikunja_app/models/user.dart';
|
import 'package:vikunja_app/models/user.dart';
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:vikunja_app/models/list.dart';
|
||||||
import 'package:vikunja_app/models/namespace.dart';
|
import 'package:vikunja_app/models/namespace.dart';
|
||||||
import 'package:vikunja_app/models/task.dart';
|
import 'package:vikunja_app/models/task.dart';
|
||||||
import 'package:vikunja_app/models/user.dart';
|
import 'package:vikunja_app/models/user.dart';
|
||||||
|
|
Reference in New Issue