Move all maxPages related stuff to provider
continuous-integration/drone/pr Build is failing
Details
continuous-integration/drone/pr Build is failing
Details
This commit is contained in:
parent
d4dbc5b4ae
commit
11e5711aab
|
@ -1,15 +1,13 @@
|
||||||
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();
|
||||||
final JsonEncoder _encoder = new JsonEncoder();
|
final JsonEncoder _encoder = new JsonEncoder();
|
||||||
final String _token;
|
final String _token;
|
||||||
final String _base;
|
final String _base;
|
||||||
int _maxPages;
|
|
||||||
|
|
||||||
int get maxPages => _maxPages;
|
|
||||||
|
|
||||||
String get base => _base;
|
String get base => _base;
|
||||||
|
|
||||||
|
@ -28,7 +26,8 @@ class Client {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
};
|
};
|
||||||
|
|
||||||
Future<dynamic> get(String url, [Map<String, List<String>> queryParameters]) {
|
Future<Response> get(String url,
|
||||||
|
[Map<String, List<String>> queryParameters]) {
|
||||||
// TODO: This could be moved to a seperate function
|
// TODO: This could be moved to a seperate function
|
||||||
var uri = Uri.parse('${this.base}$url');
|
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.
|
// 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.
|
||||||
|
@ -45,27 +44,27 @@ class Client {
|
||||||
return http.get(newUri, headers: _headers).then(_handleResponse);
|
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) {
|
||||||
|
@ -79,22 +78,14 @@ class Client {
|
||||||
throw new ApiException(
|
throw new ApiException(
|
||||||
response.statusCode, response.request.url.toString());
|
response.statusCode, response.request.url.toString());
|
||||||
}
|
}
|
||||||
// FIXME: This is a workaround for when the client makes another
|
return new Response(
|
||||||
// unrelated api request in between requesting multiple pages of the same thing.
|
_decoder.convert(response.body), response.statusCode, response.headers);
|
||||||
// To properly fix this, we need a way to pass the max number of pages somewhere else
|
|
||||||
// and not save them in the global client which everyone uses.
|
|
||||||
// This workaround only works when the other api requests in between two pages
|
|
||||||
// are not using pagination.
|
|
||||||
if (response.headers["x-pagination-total-pages"] != null) {
|
|
||||||
_maxPages = int.parse(response.headers["x-pagination-total-pages"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _decoder.convert(response.body);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
|
|
@ -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,25 @@ 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(
|
||||||
(list) => convertList(list, (result) => TaskList.fromJson(result)));
|
(response) => 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(
|
||||||
(list) => convertList(list, (result) => TaskList.fromJson(result)));
|
(response) => 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(
|
||||||
(list) => convertList(list, (result) => Namespace.fromJson(result)));
|
(response) => 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,11 @@
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
|
@ -4,8 +4,6 @@ import 'package:meta/meta.dart';
|
||||||
class APIService {
|
class APIService {
|
||||||
final Client _client;
|
final Client _client;
|
||||||
|
|
||||||
int get maxPages => _client.maxPages;
|
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
Client get client => _client;
|
Client get client => _client;
|
||||||
|
|
||||||
|
|
|
@ -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,14 +25,14 @@ 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
|
@override
|
||||||
Future<List<Task>> getAll(int listId,
|
Future<Response> getAll(int listId,
|
||||||
[Map<String, List<String>> queryParameters]) {
|
[Map<String, List<String>> queryParameters]) {
|
||||||
return client
|
return client.get('/lists/$listId/tasks', queryParameters).then(
|
||||||
.get('/lists/$listId/tasks', queryParameters)
|
(response) =>
|
||||||
.then((list) => convertList(list, (result) => Task.fromJson(result)));
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.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/pages/list/list_edit.dart';
|
import 'package:vikunja_app/pages/list/list_edit.dart';
|
||||||
import 'package:vikunja_app/stores/list_store.dart';
|
import 'package:vikunja_app/stores/list_store.dart';
|
||||||
|
@ -57,15 +56,11 @@ class _ListPageState extends State<ListPage> {
|
||||||
final index = i ~/ 2;
|
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
|
// This handles the case if there are no more elements in the list left which can be provided by the api
|
||||||
if (VikunjaGlobal.of(context).taskService.maxPages ==
|
if (taskState.maxPages == _currentPage &&
|
||||||
_currentPage &&
|
|
||||||
index == taskState.tasks.length - 1) return null;
|
index == taskState.tasks.length - 1) return null;
|
||||||
|
|
||||||
if (index >= taskState.tasks.length &&
|
if (index >= taskState.tasks.length &&
|
||||||
_currentPage <
|
_currentPage < taskState.maxPages) {
|
||||||
VikunjaGlobal.of(context)
|
|
||||||
.taskService
|
|
||||||
.maxPages) {
|
|
||||||
_currentPage++;
|
_currentPage++;
|
||||||
_loadTasksForPage(_currentPage);
|
_loadTasksForPage(_currentPage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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';
|
||||||
|
@ -137,9 +138,9 @@ class MockedTaskService implements TaskService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Task>> getAll(int listId,
|
Future<Response> getAll(int listId,
|
||||||
[Map<String, List<String>> queryParameters]) {
|
[Map<String, List<String>> queryParameters]) {
|
||||||
return Future.value(_tasks.values.toList());
|
return Future.value(new Response(_tasks.values.toList(), 200, {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -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,10 +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<List<Task>> getAll(int listId,
|
Future<Response> getAll(int listId,
|
||||||
[Map<String, List<String>> queryParameters]);
|
[Map<String, List<String>> queryParameters]);
|
||||||
// TODO: Avoid having to add this to each abstract class
|
|
||||||
int get maxPages;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class UserService {
|
abstract class UserService {
|
||||||
|
|
|
@ -4,12 +4,15 @@ import 'package:vikunja_app/global.dart';
|
||||||
|
|
||||||
class ListProvider with ChangeNotifier {
|
class ListProvider with ChangeNotifier {
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
|
int _maxPages = 0;
|
||||||
|
|
||||||
// TODO: Streams
|
// TODO: Streams
|
||||||
List<Task> _tasks = [];
|
List<Task> _tasks = [];
|
||||||
|
|
||||||
bool get isLoading => _isLoading;
|
bool get isLoading => _isLoading;
|
||||||
|
|
||||||
|
int get maxPages => _maxPages;
|
||||||
|
|
||||||
set tasks(List<Task> tasks) {
|
set tasks(List<Task> tasks) {
|
||||||
_tasks = tasks;
|
_tasks = tasks;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
@ -25,8 +28,11 @@ class ListProvider with ChangeNotifier {
|
||||||
"sort_by": ["done", "id"],
|
"sort_by": ["done", "id"],
|
||||||
"order_by": ["asc", "desc"],
|
"order_by": ["asc", "desc"],
|
||||||
"page": [page.toString()]
|
"page": [page.toString()]
|
||||||
}).then((tasks) {
|
}).then((response) {
|
||||||
_tasks.addAll(tasks);
|
if (response.headers["x-pagination-total-pages"] != null) {
|
||||||
|
_maxPages = int.parse(response.headers["x-pagination-total-pages"]);
|
||||||
|
}
|
||||||
|
_tasks.addAll(response.body);
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
});
|
});
|
||||||
|
|
Reference in New Issue