Compare commits
34 Commits
master
...
feature/ta
Author | SHA1 | Date | |
---|---|---|---|
873bcef161 | |||
11e5711aab | |||
d4dbc5b4ae | |||
a89068bbdf | |||
ac2d9722e9 | |||
7bd340f45c | |||
bc8188f44c | |||
972b194f56 | |||
9f37496497 | |||
db9be17857 | |||
38c7348aa8 | |||
876ac88a1d | |||
9348e6acc5 | |||
bb6139cef7 | |||
9fd47cde57 | |||
a34daacf23 | |||
25d64f14a5 | |||
413c2703e6 | |||
e110a4b9eb | |||
c146add9bf | |||
d08c7c3e70 | |||
5f7d59c7ea | |||
78ddbecd19 | |||
6fc336223b | |||
b5363cb6ac | |||
96dbddb10c | |||
53dfe7327b | |||
ea8ba3cfd9 | |||
ac67ccbd4c | |||
b36f0215d8 | |||
2345a131b7 | |||
187337c580 | |||
cf9c9b4cb4 | |||
9833ef4885 |
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:vikunja_app/api/response.dart';
|
||||
|
||||
class Client {
|
||||
final JsonDecoder _decoder = new JsonDecoder();
|
||||
|
@ -25,33 +26,45 @@ class Client {
|
|||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
Future<dynamic> get(String url) {
|
||||
return http
|
||||
.get('${this.base}$url', headers: _headers)
|
||||
.then(_handleResponse);
|
||||
Future<Response> get(String url,
|
||||
[Map<String, List<String>> queryParameters]) {
|
||||
// TODO: This could be moved to a seperate function
|
||||
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
|
||||
.delete('${this.base}$url', headers: _headers)
|
||||
.then(_handleResponse);
|
||||
}
|
||||
|
||||
Future<dynamic> post(String url, {dynamic body}) {
|
||||
Future<Response> post(String url, {dynamic body}) {
|
||||
return http
|
||||
.post('${this.base}$url',
|
||||
headers: _headers, body: _encoder.convert(body))
|
||||
.then(_handleResponse);
|
||||
}
|
||||
|
||||
Future<dynamic> put(String url, {dynamic body}) {
|
||||
Future<Response> put(String url, {dynamic body}) {
|
||||
return http
|
||||
.put('${this.base}$url',
|
||||
headers: _headers, body: _encoder.convert(body))
|
||||
.then(_handleResponse);
|
||||
}
|
||||
|
||||
dynamic _handleResponse(http.Response response) {
|
||||
Response _handleResponse(http.Response response) {
|
||||
if (response.statusCode < 200 ||
|
||||
response.statusCode >= 400 ||
|
||||
json == null) {
|
||||
|
@ -65,12 +78,14 @@ class Client {
|
|||
throw new ApiException(
|
||||
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 {
|
||||
final String message;
|
||||
|
||||
InvalidRequestApiException(int errorCode, String path, this.message)
|
||||
: super(errorCode, path);
|
||||
|
||||
|
@ -83,6 +98,7 @@ class InvalidRequestApiException extends ApiException {
|
|||
class ApiException implements Exception {
|
||||
final int errorCode;
|
||||
final String path;
|
||||
|
||||
ApiException(this.errorCode, this.path);
|
||||
|
||||
@override
|
||||
|
|
|
@ -12,7 +12,7 @@ class ListAPIService extends APIService implements ListService {
|
|||
Future<TaskList> create(namespaceId, TaskList tl) {
|
||||
return client
|
||||
.put('/namespaces/$namespaceId/lists', body: tl.toJSON())
|
||||
.then((map) => TaskList.fromJson(map));
|
||||
.then((response) => TaskList.fromJson(response.body));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -22,25 +22,27 @@ class ListAPIService extends APIService implements ListService {
|
|||
|
||||
@override
|
||||
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
|
||||
Future<List<TaskList>> getAll() {
|
||||
return client.get('/lists').then(
|
||||
(list) => convertList(list, (result) => TaskList.fromJson(result)));
|
||||
return client.get('/lists').then((response) =>
|
||||
convertList(response.body, (result) => TaskList.fromJson(result)));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TaskList>> getByNamespace(int namespaceId) {
|
||||
return client.get('/namespaces/$namespaceId/lists').then(
|
||||
(list) => convertList(list, (result) => TaskList.fromJson(result)));
|
||||
return client.get('/namespaces/$namespaceId/lists').then((response) =>
|
||||
convertList(response.body, (result) => TaskList.fromJson(result)));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<TaskList> update(TaskList tl) {
|
||||
return client
|
||||
.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) {
|
||||
return client
|
||||
.put('/namespaces', body: ns.toJSON())
|
||||
.then((map) => Namespace.fromJson(map));
|
||||
.then((response) => Namespace.fromJson(response.body));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -24,19 +24,19 @@ class NamespaceAPIService extends APIService implements NamespaceService {
|
|||
Future<Namespace> get(int namespaceId) {
|
||||
return client
|
||||
.get('/namespaces/$namespaceId')
|
||||
.then((map) => Namespace.fromJson(map));
|
||||
.then((response) => Namespace.fromJson(response.body));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Namespace>> getAll() {
|
||||
return client.get('/namespaces').then(
|
||||
(list) => convertList(list, (result) => Namespace.fromJson(result)));
|
||||
return client.get('/namespaces').then((response) =>
|
||||
convertList(response.body, (result) => Namespace.fromJson(result)));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Namespace> update(Namespace ns) {
|
||||
return client
|
||||
.post('/namespaces/${ns.id}', body: ns.toJSON())
|
||||
.then((map) => Namespace.fromJson(map));
|
||||
.then((response) => Namespace.fromJson(response.body));
|
||||
}
|
||||
}
|
||||
|
|
9
lib/api/response.dart
Normal file
9
lib/api/response.dart
Normal file
|
@ -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 'package:vikunja_app/api/client.dart';
|
||||
import 'package:vikunja_app/api/response.dart';
|
||||
import 'package:vikunja_app/api/service.dart';
|
||||
import 'package:vikunja_app/models/task.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) {
|
||||
return client
|
||||
.put('/lists/$listId', body: task.toJSON())
|
||||
.then((map) => Task.fromJson(map));
|
||||
.then((response) => Task.fromJson(response.body));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -24,6 +25,16 @@ class TaskAPIService extends APIService implements TaskService {
|
|||
Future<Task> update(Task task) {
|
||||
return client
|
||||
.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: {
|
||||
'username': username,
|
||||
'password': password
|
||||
}).then((map) => map['token']);
|
||||
}).then((response) => response.body['token']);
|
||||
return UserAPIService(Client(token, client.base))
|
||||
.getCurrentUser()
|
||||
.then((user) => UserTokenPair(user, token));
|
||||
|
@ -25,12 +25,12 @@ class UserAPIService extends APIService implements UserService {
|
|||
'username': username,
|
||||
'email': email,
|
||||
'password': password
|
||||
}).then((resp) => resp['username']);
|
||||
}).then((response) => response.body['username']);
|
||||
return login(newUser, password);
|
||||
}
|
||||
|
||||
@override
|
||||
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:vikunja_app/models/task.dart';
|
||||
import 'package:vikunja_app/models/user.dart';
|
||||
|
||||
class TaskList {
|
||||
|
@ -7,7 +6,6 @@ class TaskList {
|
|||
final String title, description;
|
||||
final User owner;
|
||||
final DateTime created, updated;
|
||||
final List<Task> tasks;
|
||||
|
||||
TaskList(
|
||||
{@required this.id,
|
||||
|
@ -15,8 +13,7 @@ class TaskList {
|
|||
this.description,
|
||||
this.owner,
|
||||
this.created,
|
||||
this.updated,
|
||||
this.tasks});
|
||||
this.updated});
|
||||
|
||||
TaskList.fromJson(Map<String, dynamic> json)
|
||||
: id = json['id'],
|
||||
|
@ -24,10 +21,7 @@ class TaskList {
|
|||
description = json['description'],
|
||||
title = json['title'],
|
||||
updated = DateTime.parse(json['updated']),
|
||||
created = DateTime.parse(json['created']),
|
||||
tasks = (json['tasks'] == null ? [] : json['tasks'] as List<dynamic>)
|
||||
?.map((taskJson) => Task.fromJson(taskJson))
|
||||
?.toList();
|
||||
created = DateTime.parse(json['created']);
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
|
|
|
@ -27,12 +27,16 @@ class Task {
|
|||
reminders = (json['reminder_dates'] as List<dynamic>)
|
||||
?.map((r) => DateTime.parse(r))
|
||||
?.toList(),
|
||||
due =
|
||||
json['due_date'] != null ? DateTime.parse(json['due_date']) : null,
|
||||
due = json['due_date'].toString() == 'null'
|
||||
? null
|
||||
: DateTime.parse(json['due_date']),
|
||||
description = json['description'],
|
||||
title = json['title'],
|
||||
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() => {
|
||||
'id': id,
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.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/list_edit.dart';
|
||||
import 'package:vikunja_app/stores/list_store.dart';
|
||||
|
||||
class ListPage extends StatefulWidget {
|
||||
final TaskList taskList;
|
||||
|
@ -19,24 +19,18 @@ class ListPage extends StatefulWidget {
|
|||
|
||||
class _ListPageState extends State<ListPage> {
|
||||
TaskList _list;
|
||||
List<Task> _loadingTasks = [];
|
||||
bool _loading = true;
|
||||
int _currentPage = 1;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_list = TaskList(
|
||||
id: widget.taskList.id, title: widget.taskList.title, tasks: []);
|
||||
_list = TaskList(id: widget.taskList.id, title: widget.taskList.title);
|
||||
Future.microtask(() => _loadList());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
_loadList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final taskState = Provider.of<ListProvider>(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: new Text(_list.title),
|
||||
|
@ -51,15 +45,31 @@ class _ListPageState extends State<ListPage> {
|
|||
))))
|
||||
],
|
||||
),
|
||||
body: !this._loading
|
||||
body: !taskState.isLoading
|
||||
? RefreshIndicator(
|
||||
child: _list.tasks.length > 0
|
||||
? ListView(
|
||||
child: taskState.tasks.length > 0
|
||||
? ListView.builder(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
children: ListTile.divideTiles(
|
||||
context: context, tiles: _listTasks())
|
||||
.toList(),
|
||||
)
|
||||
itemBuilder: (context, i) {
|
||||
if (i.isOdd) return Divider();
|
||||
|
||||
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.')),
|
||||
onRefresh: _loadList,
|
||||
)
|
||||
|
@ -70,56 +80,31 @@ class _ListPageState extends State<ListPage> {
|
|||
));
|
||||
}
|
||||
|
||||
List<Widget> _listTasks() {
|
||||
var tasks = (_list?.tasks?.map(_buildTile) ?? []).toList();
|
||||
tasks.addAll(_loadingTasks.map(_buildLoadingTile));
|
||||
return tasks;
|
||||
Future<void> _loadList() async {
|
||||
_loadTasksForPage(1);
|
||||
}
|
||||
|
||||
TaskTile _buildTile(Task task) {
|
||||
return TaskTile(task: task, loading: false);
|
||||
}
|
||||
|
||||
TaskTile _buildLoadingTile(Task task) {
|
||||
return TaskTile(
|
||||
task: task,
|
||||
loading: true,
|
||||
void _loadTasksForPage(int page) {
|
||||
Provider.of<ListProvider>(context, listen: false).loadTasks(
|
||||
context: context,
|
||||
listId: _list.id,
|
||||
page: page,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadList() {
|
||||
return VikunjaGlobal.of(context)
|
||||
.listService
|
||||
.get(widget.taskList.id)
|
||||
.then((list) {
|
||||
setState(() {
|
||||
_loading = false;
|
||||
_list = list;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_addItemDialog(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => AddDialog(
|
||||
onAdd: (name) => _addItem(name, context),
|
||||
onAdd: (title) => _addItem(title, context),
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'Task Name', hintText: 'eg. Milk')));
|
||||
}
|
||||
|
||||
_addItem(String name, BuildContext context) {
|
||||
var globalState = VikunjaGlobal.of(context);
|
||||
var newTask = Task(
|
||||
id: null, title: name, owner: globalState.currentUser, done: false);
|
||||
setState(() => _loadingTasks.add(newTask));
|
||||
globalState.taskService.add(_list.id, newTask).then((task) {
|
||||
setState(() {
|
||||
_list.tasks.add(task);
|
||||
});
|
||||
}).then((_) {
|
||||
_loadList();
|
||||
setState(() => _loadingTasks.remove(newTask));
|
||||
_addItem(String title, BuildContext context) {
|
||||
Provider.of<ListProvider>(context, listen: false)
|
||||
.addTask(context: context, title: title, listId: _list.id)
|
||||
.then((_) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text('The task was added successfully!'),
|
||||
));
|
||||
|
|
|
@ -3,12 +3,14 @@ import 'dart:async';
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:after_layout/after_layout.dart';
|
||||
import 'package:provider/provider.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/pages/list/list.dart';
|
||||
import 'package:vikunja_app/stores/list_store.dart';
|
||||
|
||||
class NamespacePage extends StatefulWidget {
|
||||
final Namespace namespace;
|
||||
|
@ -89,6 +91,7 @@ class _NamespacePageState extends State<NamespacePage>
|
|||
}
|
||||
|
||||
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)
|
||||
.listService
|
||||
.getByNamespace(widget.namespace.id)
|
||||
|
@ -99,8 +102,14 @@ class _NamespacePageState extends State<NamespacePage>
|
|||
}
|
||||
|
||||
_openList(BuildContext context, TaskList list) {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) => ListPage(taskList: list)));
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => ChangeNotifierProvider<ListProvider>(
|
||||
create: (_) => new ListProvider(),
|
||||
child: ListPage(
|
||||
taskList: list,
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
_addListDialog(BuildContext context) {
|
||||
|
@ -116,7 +125,7 @@ class _NamespacePageState extends State<NamespacePage>
|
|||
_addList(String name, BuildContext context) {
|
||||
VikunjaGlobal.of(context)
|
||||
.listService
|
||||
.create(widget.namespace.id, TaskList(id: null, title: name, tasks: []))
|
||||
.create(widget.namespace.id, TaskList(id: null, title: name))
|
||||
.then((_) {
|
||||
setState(() {});
|
||||
_loadLists();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:vikunja_app/api/response.dart';
|
||||
import 'package:vikunja_app/models/list.dart';
|
||||
import 'package:vikunja_app/models/namespace.dart';
|
||||
import 'package:vikunja_app/models/task.dart';
|
||||
|
@ -28,7 +29,6 @@ var _lists = {
|
|||
1: TaskList(
|
||||
id: 1,
|
||||
title: 'List 1',
|
||||
tasks: _tasks.values.toList(),
|
||||
owner: _users[1],
|
||||
description: 'A nice list',
|
||||
created: DateTime.now(),
|
||||
|
@ -120,20 +120,13 @@ class MockedListService implements ListService {
|
|||
class MockedTaskService implements TaskService {
|
||||
@override
|
||||
Future delete(int taskId) {
|
||||
_lists.forEach(
|
||||
(_, list) => list.tasks.removeWhere((task) => task.id == taskId));
|
||||
_tasks.remove(taskId);
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Task> update(Task task) {
|
||||
_lists.forEach((_, list) {
|
||||
if (list.tasks.where((t) => t.id == task.id).length > 0) {
|
||||
list.tasks.removeWhere((t) => t.id == task.id);
|
||||
list.tasks.add(task);
|
||||
}
|
||||
});
|
||||
_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) {
|
||||
var id = _tasks.keys.last + 1;
|
||||
_tasks[id] = task;
|
||||
_lists[listId].tasks.add(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 {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:vikunja_app/api/response.dart';
|
||||
import 'package:vikunja_app/models/list.dart';
|
||||
import 'package:vikunja_app/models/namespace.dart';
|
||||
import 'package:vikunja_app/models/task.dart';
|
||||
|
@ -26,6 +27,8 @@ abstract class TaskService {
|
|||
Future<Task> update(Task task);
|
||||
Future delete(int taskId);
|
||||
Future<Task> add(int listId, Task task);
|
||||
Future<Response> getAll(int listId,
|
||||
[Map<String, List<String>> queryParameters]);
|
||||
}
|
||||
|
||||
abstract class UserService {
|
||||
|
|
54
lib/stores/list_store.dart
Normal file
54
lib/stores/list_store.dart
Normal file
|
@ -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"
|
||||
source: hosted
|
||||
version: "1.1.8"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nested
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.4"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -160,6 +167,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.0.5+1"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -258,3 +272,4 @@ packages:
|
|||
version: "2.2.0"
|
||||
sdks:
|
||||
dart: ">=2.6.0 <3.0.0"
|
||||
flutter: ">=1.12.1"
|
||||
|
|
|
@ -14,6 +14,7 @@ dependencies:
|
|||
http: ^0.12.1
|
||||
after_layout: ^1.0.7
|
||||
sentry: ^3.0.1
|
||||
provider: ^4.0.5
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Reference in New Issue
Block a user