mirror of
https://github.com/go-vikunja/app
synced 2025-02-11 09:12:49 +00:00
List edit (#21)
This commit is contained in:
parent
ae6d9f0222
commit
0b870f8f62
@ -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,
|
||||
"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?)
|
||||
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/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';
|
||||
|
Loading…
x
Reference in New Issue
Block a user