Re-implemented getting tasks with the new seperate endpoint #39
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:vikunja_app/api/response.dart';
|
||||||
|
|
||||||
class Client {
|
class Client {
|
||||||
final JsonDecoder _decoder = new JsonDecoder();
|
final JsonDecoder _decoder = new JsonDecoder();
|
||||||
|
@ -25,33 +26,45 @@ class Client {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
};
|
};
|
||||||
|
|
||||||
Future<dynamic> get(String url) {
|
Future<Response> get(String url,
|
||||||
return http
|
[Map<String, List<String>> queryParameters]) {
|
||||||
.get('${this.base}$url', headers: _headers)
|
// TODO: This could be moved to a seperate function
|
||||||
.then(_handleResponse);
|
var uri = Uri.parse('${this.base}$url');
|
||||||
|
// Because these are all final values, we can't just add the queryParameters and must instead build a new Uri Object every time this method is called.
|
||||||
|
var newUri = Uri(
|
||||||
|
scheme: uri.scheme,
|
||||||
|
userInfo: uri.userInfo,
|
||||||
|
host: uri.host,
|
||||||
|
port: uri.port,
|
||||||
|
path: uri.path,
|
||||||
|
query: uri.query,
|
||||||
|
queryParameters: queryParameters,
|
||||||
|
// Because dart takes a Map<String, String> here, it is only possible to sort by one parameter while the api supports n parameters.
|
||||||
|
fragment: uri.fragment);
|
||||||
|
return http.get(newUri, headers: _headers).then(_handleResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> delete(String url) {
|
Future<Response> delete(String url) {
|
||||||
return http
|
return http
|
||||||
.delete('${this.base}$url', headers: _headers)
|
.delete('${this.base}$url', headers: _headers)
|
||||||
.then(_handleResponse);
|
.then(_handleResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> post(String url, {dynamic body}) {
|
Future<Response> post(String url, {dynamic body}) {
|
||||||
return http
|
return http
|
||||||
.post('${this.base}$url',
|
.post('${this.base}$url',
|
||||||
headers: _headers, body: _encoder.convert(body))
|
headers: _headers, body: _encoder.convert(body))
|
||||||
.then(_handleResponse);
|
.then(_handleResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> put(String url, {dynamic body}) {
|
Future<Response> put(String url, {dynamic body}) {
|
||||||
return http
|
return http
|
||||||
.put('${this.base}$url',
|
.put('${this.base}$url',
|
||||||
headers: _headers, body: _encoder.convert(body))
|
headers: _headers, body: _encoder.convert(body))
|
||||||
.then(_handleResponse);
|
.then(_handleResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic _handleResponse(http.Response response) {
|
Response _handleResponse(http.Response response) {
|
||||||
if (response.statusCode < 200 ||
|
if (response.statusCode < 200 ||
|
||||||
response.statusCode >= 400 ||
|
response.statusCode >= 400 ||
|
||||||
json == null) {
|
json == null) {
|
||||||
|
@ -65,12 +78,14 @@ class Client {
|
||||||
throw new ApiException(
|
throw new ApiException(
|
||||||
response.statusCode, response.request.url.toString());
|
response.statusCode, response.request.url.toString());
|
||||||
}
|
}
|
||||||
return _decoder.convert(response.body);
|
return new Response(
|
||||||
|
_decoder.convert(response.body), response.statusCode, response.headers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InvalidRequestApiException extends ApiException {
|
class InvalidRequestApiException extends ApiException {
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
InvalidRequestApiException(int errorCode, String path, this.message)
|
InvalidRequestApiException(int errorCode, String path, this.message)
|
||||||
: super(errorCode, path);
|
: super(errorCode, path);
|
||||||
|
|||||||
|
|
||||||
|
@ -83,6 +98,7 @@ class InvalidRequestApiException extends ApiException {
|
||||||
class ApiException implements Exception {
|
class ApiException implements Exception {
|
||||||
final int errorCode;
|
final int errorCode;
|
||||||
final String path;
|
final String path;
|
||||||
|
|
||||||
ApiException(this.errorCode, this.path);
|
ApiException(this.errorCode, this.path);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -12,7 +12,7 @@ class ListAPIService extends APIService implements ListService {
|
||||||
Future<TaskList> create(namespaceId, TaskList tl) {
|
Future<TaskList> create(namespaceId, TaskList tl) {
|
||||||
return client
|
return client
|
||||||
.put('/namespaces/$namespaceId/lists', body: tl.toJSON())
|
.put('/namespaces/$namespaceId/lists', body: tl.toJSON())
|
||||||
.then((map) => TaskList.fromJson(map));
|
.then((response) => TaskList.fromJson(response.body));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -22,25 +22,27 @@ class ListAPIService extends APIService implements ListService {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<TaskList> get(int listId) {
|
Future<TaskList> get(int listId) {
|
||||||
return client.get('/lists/$listId').then((map) => TaskList.fromJson(map));
|
return client
|
||||||
|
.get('/lists/$listId')
|
||||||
|
.then((response) => TaskList.fromJson(response.body));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<TaskList>> getAll() {
|
Future<List<TaskList>> getAll() {
|
||||||
return client.get('/lists').then(
|
return client.get('/lists').then((response) =>
|
||||||
(list) => convertList(list, (result) => TaskList.fromJson(result)));
|
convertList(response.body, (result) => TaskList.fromJson(result)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<TaskList>> getByNamespace(int namespaceId) {
|
Future<List<TaskList>> getByNamespace(int namespaceId) {
|
||||||
return client.get('/namespaces/$namespaceId/lists').then(
|
return client.get('/namespaces/$namespaceId/lists').then((response) =>
|
||||||
(list) => convertList(list, (result) => TaskList.fromJson(result)));
|
convertList(response.body, (result) => TaskList.fromJson(result)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<TaskList> update(TaskList tl) {
|
Future<TaskList> update(TaskList tl) {
|
||||||
return client
|
return client
|
||||||
.post('/lists/${tl.id}', body: tl.toJSON())
|
.post('/lists/${tl.id}', body: tl.toJSON())
|
||||||
.then((map) => TaskList.fromJson(map));
|
.then((response) => TaskList.fromJson(response.body));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ class NamespaceAPIService extends APIService implements NamespaceService {
|
||||||
Future<Namespace> create(Namespace ns) {
|
Future<Namespace> create(Namespace ns) {
|
||||||
return client
|
return client
|
||||||
.put('/namespaces', body: ns.toJSON())
|
.put('/namespaces', body: ns.toJSON())
|
||||||
.then((map) => Namespace.fromJson(map));
|
.then((response) => Namespace.fromJson(response.body));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -24,19 +24,19 @@ class NamespaceAPIService extends APIService implements NamespaceService {
|
||||||
Future<Namespace> get(int namespaceId) {
|
Future<Namespace> get(int namespaceId) {
|
||||||
return client
|
return client
|
||||||
.get('/namespaces/$namespaceId')
|
.get('/namespaces/$namespaceId')
|
||||||
.then((map) => Namespace.fromJson(map));
|
.then((response) => Namespace.fromJson(response.body));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Namespace>> getAll() {
|
Future<List<Namespace>> getAll() {
|
||||||
return client.get('/namespaces').then(
|
return client.get('/namespaces').then((response) =>
|
||||||
(list) => convertList(list, (result) => Namespace.fromJson(result)));
|
convertList(response.body, (result) => Namespace.fromJson(result)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Namespace> update(Namespace ns) {
|
Future<Namespace> update(Namespace ns) {
|
||||||
return client
|
return client
|
||||||
.post('/namespaces/${ns.id}', body: ns.toJSON())
|
.post('/namespaces/${ns.id}', body: ns.toJSON())
|
||||||
.then((map) => Namespace.fromJson(map));
|
.then((response) => Namespace.fromJson(response.body));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
// This is a wrapper class to be able to return the headers up to the provider
|
||||||
|
// to properly handle things like pagination with it.
|
||||||
|
class Response {
|
||||||
|
Response(this.body, this.statusCode, this.headers);
|
||||||
|
|
||||||
|
final dynamic body;
|
||||||
|
final int statusCode;
|
||||||
|
final Map<String, String> headers;
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:vikunja_app/api/client.dart';
|
import 'package:vikunja_app/api/client.dart';
|
||||||
|
import 'package:vikunja_app/api/response.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/task.dart';
|
||||||
import 'package:vikunja_app/service/services.dart';
|
import 'package:vikunja_app/service/services.dart';
|
||||||
|
@ -12,7 +13,7 @@ class TaskAPIService extends APIService implements TaskService {
|
||||||
Future<Task> add(int listId, Task task) {
|
Future<Task> add(int listId, Task task) {
|
||||||
return client
|
return client
|
||||||
.put('/lists/$listId', body: task.toJSON())
|
.put('/lists/$listId', body: task.toJSON())
|
||||||
.then((map) => Task.fromJson(map));
|
.then((response) => Task.fromJson(response.body));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -24,6 +25,16 @@ class TaskAPIService extends APIService implements TaskService {
|
||||||
Future<Task> update(Task task) {
|
Future<Task> update(Task task) {
|
||||||
return client
|
return client
|
||||||
.post('/tasks/${task.id}', body: task.toJSON())
|
.post('/tasks/${task.id}', body: task.toJSON())
|
||||||
.then((map) => Task.fromJson(map));
|
.then((response) => Task.fromJson(response.body));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Response> getAll(int listId,
|
||||||
|
[Map<String, List<String>> queryParameters]) {
|
||||||
|
return client.get('/lists/$listId/tasks', queryParameters).then(
|
||||||
|
(response) => new Response(
|
||||||
|
convertList(response.body, (result) => Task.fromJson(result)),
|
||||||
|
response.statusCode,
|
||||||
|
response.headers));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ class UserAPIService extends APIService implements UserService {
|
||||||
var token = await client.post('/login', body: {
|
var token = await client.post('/login', body: {
|
||||||
'username': username,
|
'username': username,
|
||||||
'password': password
|
'password': password
|
||||||
}).then((map) => map['token']);
|
}).then((response) => response.body['token']);
|
||||||
return UserAPIService(Client(token, client.base))
|
return UserAPIService(Client(token, client.base))
|
||||||
.getCurrentUser()
|
.getCurrentUser()
|
||||||
.then((user) => UserTokenPair(user, token));
|
.then((user) => UserTokenPair(user, token));
|
||||||
|
@ -25,12 +25,12 @@ class UserAPIService extends APIService implements UserService {
|
||||||
'username': username,
|
'username': username,
|
||||||
'email': email,
|
'email': email,
|
||||||
'password': password
|
'password': password
|
||||||
}).then((resp) => resp['username']);
|
}).then((response) => response.body['username']);
|
||||||
return login(newUser, password);
|
return login(newUser, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<User> getCurrentUser() {
|
Future<User> getCurrentUser() {
|
||||||
return client.get('/user').then((map) => User.fromJson(map));
|
return client.get('/user').then((response) => User.fromJson(response.body));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:vikunja_app/models/task.dart';
|
|
||||||
import 'package:vikunja_app/models/user.dart';
|
import 'package:vikunja_app/models/user.dart';
|
||||||
|
|
||||||
class TaskList {
|
class TaskList {
|
||||||
|
@ -7,7 +6,6 @@ class TaskList {
|
||||||
final String title, description;
|
final String title, description;
|
||||||
final User owner;
|
final User owner;
|
||||||
final DateTime created, updated;
|
final DateTime created, updated;
|
||||||
final List<Task> tasks;
|
|
||||||
|
|
||||||
TaskList(
|
TaskList(
|
||||||
{@required this.id,
|
{@required this.id,
|
||||||
|
@ -15,8 +13,7 @@ class TaskList {
|
||||||
this.description,
|
this.description,
|
||||||
this.owner,
|
this.owner,
|
||||||
this.created,
|
this.created,
|
||||||
this.updated,
|
this.updated});
|
||||||
this.tasks});
|
|
||||||
|
|
||||||
TaskList.fromJson(Map<String, dynamic> json)
|
TaskList.fromJson(Map<String, dynamic> json)
|
||||||
: id = json['id'],
|
: id = json['id'],
|
||||||
|
@ -24,10 +21,7 @@ class TaskList {
|
||||||
description = json['description'],
|
description = json['description'],
|
||||||
title = json['title'],
|
title = json['title'],
|
||||||
updated = DateTime.parse(json['updated']),
|
updated = DateTime.parse(json['updated']),
|
||||||
created = DateTime.parse(json['created']),
|
created = DateTime.parse(json['created']);
|
||||||
tasks = (json['tasks'] == null ? [] : json['tasks'] as List<dynamic>)
|
|
||||||
?.map((taskJson) => Task.fromJson(taskJson))
|
|
||||||
?.toList();
|
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -27,12 +27,16 @@ class Task {
|
||||||
reminders = (json['reminder_dates'] as List<dynamic>)
|
reminders = (json['reminder_dates'] as List<dynamic>)
|
||||||
?.map((r) => DateTime.parse(r))
|
?.map((r) => DateTime.parse(r))
|
||||||
?.toList(),
|
?.toList(),
|
||||||
due =
|
due = json['due_date'].toString() == 'null'
|
||||||
json['due_date'] != null ? DateTime.parse(json['due_date']) : null,
|
? null
|
||||||
|
: DateTime.parse(json['due_date']),
|
||||||
description = json['description'],
|
description = json['description'],
|
||||||
title = json['title'],
|
title = json['title'],
|
||||||
done = json['done'],
|
done = json['done'],
|
||||||
owner = User.fromJson(json['created_by']);
|
owner = json['created_by'].toString() == "null"
|
||||||
|
? null
|
||||||
|
: User.fromJson(json[
|
||||||
|
'created_by']); // There has to be a better way of doing this...
|
||||||
|
|
||||||
toJSON() => {
|
toJSON() => {
|
||||||
'id': id,
|
'id': id,
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.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/models/list.dart';
|
import 'package:vikunja_app/models/list.dart';
|
||||||
import 'package:vikunja_app/models/task.dart';
|
|
||||||
import 'package:vikunja_app/pages/list/list_edit.dart';
|
import 'package:vikunja_app/pages/list/list_edit.dart';
|
||||||
|
import 'package:vikunja_app/stores/list_store.dart';
|
||||||
|
|
||||||
class ListPage extends StatefulWidget {
|
class ListPage extends StatefulWidget {
|
||||||
final TaskList taskList;
|
final TaskList taskList;
|
||||||
|
@ -19,24 +19,18 @@ class ListPage extends StatefulWidget {
|
||||||
|
|
||||||
class _ListPageState extends State<ListPage> {
|
class _ListPageState extends State<ListPage> {
|
||||||
TaskList _list;
|
TaskList _list;
|
||||||
List<Task> _loadingTasks = [];
|
int _currentPage = 1;
|
||||||
bool _loading = true;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_list = TaskList(
|
_list = TaskList(id: widget.taskList.id, title: widget.taskList.title);
|
||||||
id: widget.taskList.id, title: widget.taskList.title, tasks: []);
|
Future.microtask(() => _loadList());
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeDependencies() {
|
|
||||||
super.didChangeDependencies();
|
|
||||||
_loadList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final taskState = Provider.of<ListProvider>(context);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: new Text(_list.title),
|
title: new Text(_list.title),
|
||||||
|
@ -51,15 +45,31 @@ class _ListPageState extends State<ListPage> {
|
||||||
))))
|
))))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: !this._loading
|
body: !taskState.isLoading
|
||||||
? RefreshIndicator(
|
? RefreshIndicator(
|
||||||
child: _list.tasks.length > 0
|
child: taskState.tasks.length > 0
|
||||||
? ListView(
|
? ListView.builder(
|
||||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||||
children: ListTile.divideTiles(
|
itemBuilder: (context, i) {
|
||||||
context: context, tiles: _listTasks())
|
if (i.isOdd) return Divider();
|
||||||
.toList(),
|
|
||||||
)
|
final index = i ~/ 2;
|
||||||
|
|
||||||
|
// This handles the case if there are no more elements in the list left which can be provided by the api
|
||||||
|
if (taskState.maxPages == _currentPage &&
|
||||||
|
index == taskState.tasks.length - 1) return null;
|
||||||
|
|
||||||
|
if (index >= taskState.tasks.length &&
|
||||||
|
_currentPage < taskState.maxPages) {
|
||||||
|
_currentPage++;
|
||||||
|
_loadTasksForPage(_currentPage);
|
||||||
|
}
|
||||||
|
return index < taskState.tasks.length
|
||||||
|
? TaskTile(
|
||||||
|
task: taskState.tasks[index],
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
})
|
||||||
: Center(child: Text('This list is empty.')),
|
: Center(child: Text('This list is empty.')),
|
||||||
onRefresh: _loadList,
|
onRefresh: _loadList,
|
||||||
)
|
)
|
||||||
|
@ -70,56 +80,31 @@ class _ListPageState extends State<ListPage> {
|
||||||
));
|
));
|
||||||
JonasFranz
commented
Please create a custom widget which handles auto loading new tasks and inserting dividers. Please create a custom widget which handles auto loading new tasks and inserting dividers.
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _listTasks() {
|
Future<void> _loadList() async {
|
||||||
var tasks = (_list?.tasks?.map(_buildTile) ?? []).toList();
|
_loadTasksForPage(1);
|
||||||
tasks.addAll(_loadingTasks.map(_buildLoadingTile));
|
|
||||||
return tasks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskTile _buildTile(Task task) {
|
void _loadTasksForPage(int page) {
|
||||||
return TaskTile(task: task, loading: false);
|
Provider.of<ListProvider>(context, listen: false).loadTasks(
|
||||||
}
|
context: context,
|
||||||
|
listId: _list.id,
|
||||||
TaskTile _buildLoadingTile(Task task) {
|
page: page,
|
||||||
return TaskTile(
|
|
||||||
task: task,
|
|
||||||
loading: true,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadList() {
|
|
||||||
return VikunjaGlobal.of(context)
|
|
||||||
.listService
|
|
||||||
.get(widget.taskList.id)
|
|
||||||
.then((list) {
|
|
||||||
setState(() {
|
|
||||||
_loading = false;
|
|
||||||
_list = list;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_addItemDialog(BuildContext context) {
|
_addItemDialog(BuildContext context) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => AddDialog(
|
builder: (_) => AddDialog(
|
||||||
onAdd: (name) => _addItem(name, context),
|
onAdd: (title) => _addItem(title, context),
|
||||||
decoration: new InputDecoration(
|
decoration: new InputDecoration(
|
||||||
labelText: 'Task Name', hintText: 'eg. Milk')));
|
labelText: 'Task Name', hintText: 'eg. Milk')));
|
||||||
}
|
}
|
||||||
|
|
||||||
_addItem(String name, BuildContext context) {
|
_addItem(String title, BuildContext context) {
|
||||||
var globalState = VikunjaGlobal.of(context);
|
Provider.of<ListProvider>(context, listen: false)
|
||||||
var newTask = Task(
|
.addTask(context: context, title: title, listId: _list.id)
|
||||||
id: null, title: name, owner: globalState.currentUser, done: false);
|
.then((_) {
|
||||||
setState(() => _loadingTasks.add(newTask));
|
|
||||||
globalState.taskService.add(_list.id, newTask).then((task) {
|
|
||||||
setState(() {
|
|
||||||
_list.tasks.add(task);
|
|
||||||
});
|
|
||||||
}).then((_) {
|
|
||||||
_loadList();
|
|
||||||
setState(() => _loadingTasks.remove(newTask));
|
|
||||||
Scaffold.of(context).showSnackBar(SnackBar(
|
Scaffold.of(context).showSnackBar(SnackBar(
|
||||||
content: Text('The task was added successfully!'),
|
content: Text('The task was added successfully!'),
|
||||||
));
|
));
|
||||||
|
|
|
@ -3,12 +3,14 @@ import 'dart:async';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:after_layout/after_layout.dart';
|
import 'package:after_layout/after_layout.dart';
|
||||||
|
import 'package:provider/provider.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/list.dart';
|
||||||
import 'package:vikunja_app/models/namespace.dart';
|
import 'package:vikunja_app/models/namespace.dart';
|
||||||
import 'package:vikunja_app/pages/list/list.dart';
|
import 'package:vikunja_app/pages/list/list.dart';
|
||||||
|
import 'package:vikunja_app/stores/list_store.dart';
|
||||||
|
|
||||||
class NamespacePage extends StatefulWidget {
|
class NamespacePage extends StatefulWidget {
|
||||||
final Namespace namespace;
|
final Namespace namespace;
|
||||||
|
@ -89,6 +91,7 @@ class _NamespacePageState extends State<NamespacePage>
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadLists() {
|
Future<void> _loadLists() {
|
||||||
|
// FIXME: This is called even when the tasks on a list are loaded - which is not needed at all
|
||||||
return VikunjaGlobal.of(context)
|
return VikunjaGlobal.of(context)
|
||||||
.listService
|
.listService
|
||||||
.getByNamespace(widget.namespace.id)
|
.getByNamespace(widget.namespace.id)
|
||||||
|
@ -99,8 +102,14 @@ class _NamespacePageState extends State<NamespacePage>
|
||||||
}
|
}
|
||||||
|
|
||||||
_openList(BuildContext context, TaskList list) {
|
_openList(BuildContext context, TaskList list) {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(MaterialPageRoute(
|
||||||
MaterialPageRoute(builder: (context) => ListPage(taskList: list)));
|
builder: (context) => ChangeNotifierProvider<ListProvider>(
|
||||||
|
create: (_) => new ListProvider(),
|
||||||
|
child: ListPage(
|
||||||
|
taskList: list,
|
||||||
|
),
|
||||||
|
),
|
||||||
JonasFranz
commented
Maybe init the provider in the ListPage? Make sure to use Builder as child when doing it. Maybe init the provider in the ListPage? Make sure to use Builder as child when doing it.
konrad
commented
You mean in the You mean in the `build` method of it?
|
|||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
_addListDialog(BuildContext context) {
|
_addListDialog(BuildContext context) {
|
||||||
|
@ -116,7 +125,7 @@ class _NamespacePageState extends State<NamespacePage>
|
||||||
_addList(String name, BuildContext context) {
|
_addList(String name, BuildContext context) {
|
||||||
VikunjaGlobal.of(context)
|
VikunjaGlobal.of(context)
|
||||||
.listService
|
.listService
|
||||||
.create(widget.namespace.id, TaskList(id: null, title: name, tasks: []))
|
.create(widget.namespace.id, TaskList(id: null, title: name))
|
||||||
.then((_) {
|
.then((_) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
_loadLists();
|
_loadLists();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:vikunja_app/api/response.dart';
|
||||||
import 'package:vikunja_app/models/list.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';
|
||||||
|
@ -28,7 +29,6 @@ var _lists = {
|
||||||
1: TaskList(
|
1: TaskList(
|
||||||
id: 1,
|
id: 1,
|
||||||
title: 'List 1',
|
title: 'List 1',
|
||||||
tasks: _tasks.values.toList(),
|
|
||||||
owner: _users[1],
|
owner: _users[1],
|
||||||
description: 'A nice list',
|
description: 'A nice list',
|
||||||
created: DateTime.now(),
|
created: DateTime.now(),
|
||||||
|
@ -120,20 +120,13 @@ class MockedListService implements ListService {
|
||||||
class MockedTaskService implements TaskService {
|
class MockedTaskService implements TaskService {
|
||||||
@override
|
@override
|
||||||
Future delete(int taskId) {
|
Future delete(int taskId) {
|
||||||
_lists.forEach(
|
|
||||||
(_, list) => list.tasks.removeWhere((task) => task.id == taskId));
|
|
||||||
_tasks.remove(taskId);
|
_tasks.remove(taskId);
|
||||||
return Future.value();
|
return Future.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Task> update(Task task) {
|
Future<Task> update(Task task) {
|
||||||
_lists.forEach((_, list) {
|
_tasks[task.id] = task;
|
||||||
if (list.tasks.where((t) => t.id == task.id).length > 0) {
|
|
||||||
list.tasks.removeWhere((t) => t.id == task.id);
|
|
||||||
list.tasks.add(task);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return Future.value(_tasks[task.id] = task);
|
return Future.value(_tasks[task.id] = task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,9 +134,17 @@ class MockedTaskService implements TaskService {
|
||||||
Future<Task> add(int listId, Task task) {
|
Future<Task> add(int listId, Task task) {
|
||||||
var id = _tasks.keys.last + 1;
|
var id = _tasks.keys.last + 1;
|
||||||
_tasks[id] = task;
|
_tasks[id] = task;
|
||||||
_lists[listId].tasks.add(task);
|
|
||||||
return Future.value(task);
|
return Future.value(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Response> getAll(int listId,
|
||||||
|
[Map<String, List<String>> queryParameters]) {
|
||||||
|
return Future.value(new Response(_tasks.values.toList(), 200, {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get maxPages => 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockedUserService implements UserService {
|
class MockedUserService implements UserService {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:vikunja_app/api/response.dart';
|
||||||
import 'package:vikunja_app/models/list.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';
|
||||||
|
@ -26,6 +27,8 @@ abstract class TaskService {
|
||||||
Future<Task> update(Task task);
|
Future<Task> update(Task task);
|
||||||
Future delete(int taskId);
|
Future delete(int taskId);
|
||||||
Future<Task> add(int listId, Task task);
|
Future<Task> add(int listId, Task task);
|
||||||
|
Future<Response> getAll(int listId,
|
||||||
|
[Map<String, List<String>> queryParameters]);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class UserService {
|
abstract class UserService {
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:vikunja_app/models/task.dart';
|
||||||
|
import 'package:vikunja_app/global.dart';
|
||||||
|
|
||||||
|
class ListProvider with ChangeNotifier {
|
||||||
|
bool _isLoading = false;
|
||||||
|
int _maxPages = 0;
|
||||||
|
|
||||||
|
// TODO: Streams
|
||||||
|
List<Task> _tasks = [];
|
||||||
|
|
||||||
|
bool get isLoading => _isLoading;
|
||||||
|
|
||||||
|
int get maxPages => _maxPages;
|
||||||
|
|
||||||
|
set tasks(List<Task> tasks) {
|
||||||
|
_tasks = tasks;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Task> get tasks => _tasks;
|
||||||
|
|
||||||
|
void loadTasks({BuildContext context, int listId, int page = 1}) {
|
||||||
|
_isLoading = true;
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
VikunjaGlobal.of(context).taskService.getAll(listId, {
|
||||||
|
"sort_by": ["done", "id"],
|
||||||
|
"order_by": ["asc", "desc"],
|
||||||
|
"page": [page.toString()]
|
||||||
|
}).then((response) {
|
||||||
|
if (response.headers["x-pagination-total-pages"] != null) {
|
||||||
|
_maxPages = int.parse(response.headers["x-pagination-total-pages"]);
|
||||||
|
}
|
||||||
|
_tasks.addAll(response.body);
|
||||||
|
_isLoading = false;
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addTask({BuildContext context, String title, int listId}) {
|
||||||
|
var globalState = VikunjaGlobal.of(context);
|
||||||
|
var newTask = Task(
|
||||||
|
id: null, title: title, owner: globalState.currentUser, done: false);
|
||||||
|
_isLoading = true;
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
return globalState.taskService.add(listId, newTask).then((task) {
|
||||||
|
_tasks.insert(0, task);
|
||||||
|
_isLoading = false;
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
15
pubspec.lock
15
pubspec.lock
|
@ -139,6 +139,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.8"
|
version: "1.1.8"
|
||||||
|
nested:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: nested
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.4"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -160,6 +167,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "2.4.0"
|
||||||
|
provider:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: provider
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.5+1"
|
||||||
quiver:
|
quiver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -258,3 +272,4 @@ packages:
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.6.0 <3.0.0"
|
dart: ">=2.6.0 <3.0.0"
|
||||||
|
flutter: ">=1.12.1"
|
||||||
|
|
|
@ -14,6 +14,7 @@ dependencies:
|
||||||
http: ^0.12.1
|
http: ^0.12.1
|
||||||
after_layout: ^1.0.7
|
after_layout: ^1.0.7
|
||||||
sentry: ^3.0.1
|
sentry: ^3.0.1
|
||||||
|
provider: ^4.0.5
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Reference in New Issue
Please save this in stores. This shouldn't be handled by the client.
Done!