mirror of
https://github.com/go-vikunja/app
synced 2024-06-05 03:59:48 +00:00
- made client return null and not an empty response when loading from the server.
- added scrolling ability to every window even when getting tasks fails. - fixed error handler in client to always show snackbar
This commit is contained in:
parent
dd0d8e624d
commit
e3e23e8c27
|
@ -8,10 +8,13 @@ class BucketAPIService extends APIService implements BucketService {
|
|||
BucketAPIService(Client client) : super(client);
|
||||
|
||||
@override
|
||||
Future<Bucket> add(int listId, Bucket bucket) {
|
||||
Future<Bucket?> add(int listId, Bucket bucket) {
|
||||
return client
|
||||
.put('/lists/$listId/buckets', body: bucket.toJSON())
|
||||
.then((response) => Bucket.fromJSON(response.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return Bucket.fromJSON(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -30,15 +33,15 @@ class BucketAPIService extends APIService implements BucketService {
|
|||
*/
|
||||
|
||||
@override
|
||||
Future<Response> getAllByList(int listId,
|
||||
Future<Response?> getAllByList(int listId,
|
||||
[Map<String, List<String>>? queryParameters]) {
|
||||
return client
|
||||
.get('/lists/$listId/buckets', queryParameters)
|
||||
.then((response) => new Response(
|
||||
.then((response) => response != null ? new Response(
|
||||
convertList(response.body, (result) => Bucket.fromJSON(result)),
|
||||
response.statusCode,
|
||||
response.headers
|
||||
));
|
||||
) : null);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -46,9 +49,12 @@ class BucketAPIService extends APIService implements BucketService {
|
|||
int get maxPages => maxPages;
|
||||
|
||||
@override
|
||||
Future<Bucket> update(Bucket bucket) {
|
||||
Future<Bucket?> update(Bucket bucket) {
|
||||
return client
|
||||
.post('/lists/${bucket.listId}/buckets/${bucket.id}', body: bucket.toJSON())
|
||||
.then((response) => Bucket.fromJSON(response.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return Bucket.fromJSON(response.body);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -70,16 +70,16 @@ class Client {
|
|||
authenticated = false;
|
||||
}
|
||||
|
||||
Future<Response> get(String url,
|
||||
Future<Response?> get(String url,
|
||||
[Map<String, List<String>>? queryParameters]) {
|
||||
final uri = Uri.parse('${this.base}$url').replace(
|
||||
queryParameters: queryParameters);
|
||||
return http.get(uri, headers: _headers)
|
||||
.then(_handleResponse).onError((error, stackTrace) =>
|
||||
_handleError(error, stackTrace));
|
||||
.then(_handleResponse).catchError((error) =>
|
||||
_handleError(error, null));
|
||||
}
|
||||
|
||||
Future<Response> delete(String url) {
|
||||
Future<Response?> delete(String url) {
|
||||
return http
|
||||
.delete(
|
||||
'${this.base}$url'.toUri()!,
|
||||
|
@ -89,7 +89,7 @@ class Client {
|
|||
_handleError(error, stackTrace));
|
||||
}
|
||||
|
||||
Future<Response> post(String url, {dynamic body}) {
|
||||
Future<Response?> post(String url, {dynamic body}) {
|
||||
return http
|
||||
.post(
|
||||
'${this.base}$url'.toUri()!,
|
||||
|
@ -100,7 +100,7 @@ class Client {
|
|||
_handleError(error, stackTrace));
|
||||
}
|
||||
|
||||
Future<Response> put(String url, {dynamic body}) {
|
||||
Future<Response?> put(String url, {dynamic body}) {
|
||||
return http
|
||||
.put(
|
||||
'${this.base}$url'.toUri()!,
|
||||
|
@ -111,12 +111,12 @@ class Client {
|
|||
_handleError(error, stackTrace));
|
||||
}
|
||||
|
||||
FutureOr<Response> _handleError(Object? e, StackTrace? st) {
|
||||
Response? _handleError(Object? e, StackTrace? st) {
|
||||
SnackBar snackBar = SnackBar(
|
||||
content: Text("Error on request: " + e.toString()),
|
||||
action: SnackBarAction(label: "Clear", onPressed: () => global.currentState?.clearSnackBars()),);
|
||||
global.currentState?.showSnackBar(snackBar);
|
||||
return Response("", 0, {}, error: true);
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, String> headersToMap(HttpHeaders headers) {
|
||||
|
@ -153,8 +153,7 @@ class Client {
|
|||
|
||||
void _handleResponseErrors(http.Response response) {
|
||||
if (response.statusCode < 200 ||
|
||||
response.statusCode >= 400 ||
|
||||
json == null) {
|
||||
response.statusCode >= 400) {
|
||||
Map<String, dynamic> error;
|
||||
error = _decoder.convert(response.body);
|
||||
if (response.statusCode ~/ 100 == 4) {
|
||||
|
@ -184,7 +183,7 @@ class Client {
|
|||
}
|
||||
}
|
||||
|
||||
Response _handleResponse(http.Response response) {
|
||||
Response? _handleResponse(http.Response response) {
|
||||
_handleResponseErrors(response);
|
||||
return Response(
|
||||
_decoder.convert(response.body), response.statusCode, response.headers);
|
||||
|
|
|
@ -8,25 +8,34 @@ class LabelTaskAPIService extends APIService implements LabelTaskService {
|
|||
LabelTaskAPIService(Client client) : super(client);
|
||||
|
||||
@override
|
||||
Future<Label> create(LabelTask lt) async {
|
||||
Future<Label?> create(LabelTask lt) async {
|
||||
return client
|
||||
.put('/tasks/${lt.task!.id}/labels', body: lt.toJSON())
|
||||
.then((result) => Label.fromJson(result.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return Label.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Label> delete(LabelTask lt) async {
|
||||
Future<Label?> delete(LabelTask lt) async {
|
||||
return client
|
||||
.delete('/tasks/${lt.task!.id}/labels/${lt.label.id}')
|
||||
.then((result) => Label.fromJson(result.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return Label.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Label>> getAll(LabelTask lt, {String? query}) async {
|
||||
Future<List<Label>?> getAll(LabelTask lt, {String? query}) async {
|
||||
String? params =
|
||||
query == null ? null : '?s=' + Uri.encodeQueryComponent(query);
|
||||
|
||||
return client.get('/tasks/${lt.task!.id}/labels$params').then(
|
||||
(label) => convertList(label, (result) => Label.fromJson(result)));
|
||||
(label) {
|
||||
if (label == null) return null;
|
||||
return convertList(label, (result) => Label.fromJson(result));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,13 +10,16 @@ class LabelTaskBulkAPIService extends APIService
|
|||
LabelTaskBulkAPIService(Client client) : super(client);
|
||||
|
||||
@override
|
||||
Future<List<Label>> update(Task task, List<Label>? labels) {
|
||||
Future<List<Label>?> update(Task task, List<Label>? labels) {
|
||||
if(labels == null)
|
||||
labels = [];
|
||||
return client
|
||||
.post('/tasks/${task.id}/labels/bulk',
|
||||
body: LabelTaskBulk(labels: labels).toJSON())
|
||||
.then((response) => convertList(
|
||||
response.body['labels'], (result) => Label.fromJson(result)));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return convertList(
|
||||
response.body['labels'], (result) => Label.fromJson(result));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,38 +7,53 @@ class LabelAPIService extends APIService implements LabelService {
|
|||
LabelAPIService(Client client) : super(client);
|
||||
|
||||
@override
|
||||
Future<Label> create(Label label) {
|
||||
Future<Label?> create(Label label) {
|
||||
return client
|
||||
.put('/labels', body: label.toJSON())
|
||||
.then((response) => Label.fromJson(response.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return Label.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Label> delete(Label label) {
|
||||
Future<Label?> delete(Label label) {
|
||||
return client
|
||||
.delete('/labels/${label.id}')
|
||||
.then((response) => Label.fromJson(response.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return Label.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Label> get(int labelID) {
|
||||
Future<Label?> get(int labelID) {
|
||||
return client
|
||||
.get('/labels/$labelID')
|
||||
.then((response) => Label.fromJson(response.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return Label.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Label>> getAll({String? query}) {
|
||||
Future<List<Label>?> getAll({String? query}) {
|
||||
String params =
|
||||
query == null ? '' : '?s=' + Uri.encodeQueryComponent(query);
|
||||
return client.get('/labels$params').then(
|
||||
(response) => convertList(response.body, (result) => Label.fromJson(result)));
|
||||
(response) {
|
||||
if (response == null) return null;
|
||||
return convertList(response.body, (result) => Label.fromJson(result));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Label> update(Label label) {
|
||||
Future<Label?> update(Label label) {
|
||||
return client
|
||||
.post('/labels/${label.id}', body: label)
|
||||
.then((response) => Label.fromJson(response.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return Label.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,11 +12,14 @@ class ListAPIService extends APIService implements ListService {
|
|||
ListAPIService(Client client, FlutterSecureStorage storage) : _storage = storage, super(client);
|
||||
|
||||
@override
|
||||
Future<TaskList> create(namespaceId, TaskList tl) {
|
||||
Future<TaskList?> create(namespaceId, TaskList tl) {
|
||||
tl.namespaceId = namespaceId;
|
||||
return client
|
||||
.put('/namespaces/$namespaceId/lists', body: tl.toJSON())
|
||||
.then((response) => TaskList.fromJson(response.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return TaskList.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -25,14 +28,15 @@ class ListAPIService extends APIService implements ListService {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<TaskList> get(int listId) {
|
||||
Future<TaskList?> get(int listId) {
|
||||
return client.get('/lists/$listId').then((response) {
|
||||
if (response == null) return null;
|
||||
final map = response.body;
|
||||
if (map.containsKey('id')) {
|
||||
return client
|
||||
.get("/lists/$listId/tasks")
|
||||
.then((tasks) {
|
||||
map['tasks'] = tasks.body;
|
||||
map['tasks'] = tasks?.body;
|
||||
return TaskList.fromJson(map);
|
||||
});
|
||||
}
|
||||
|
@ -41,31 +45,40 @@ class ListAPIService extends APIService implements ListService {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<List<TaskList>> getAll() {
|
||||
Future<List<TaskList>?> getAll() {
|
||||
return client.get('/lists').then(
|
||||
(list) {
|
||||
if (list == null) return null;
|
||||
if (list.body.toString().isEmpty)
|
||||
return Future.value([]);
|
||||
return convertList(list.body, (result) => TaskList.fromJson(result));});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TaskList>> getByNamespace(int namespaceId) {
|
||||
Future<List<TaskList>?> getByNamespace(int namespaceId) {
|
||||
// TODO there needs to be a better way for this. /namespaces/-2/lists should
|
||||
// return favorite lists
|
||||
if(namespaceId == -2) {
|
||||
// Favourites.
|
||||
return getAll().then((value) {value.removeWhere((element) => !element.isFavorite); return value;});
|
||||
return getAll().then((value) {
|
||||
if (value == null) return null;
|
||||
value.removeWhere((element) => !element.isFavorite); return value;});
|
||||
}
|
||||
return client.get('/namespaces/$namespaceId/lists').then(
|
||||
(list) => convertList(list.body, (result) => TaskList.fromJson(result)));
|
||||
(list) {
|
||||
if (list == null) return null;
|
||||
return convertList(list.body, (result) => TaskList.fromJson(result));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<TaskList> update(TaskList tl) {
|
||||
Future<TaskList?> update(TaskList tl) {
|
||||
return client
|
||||
.post('/lists/${tl.id}', body: tl.toJSON())
|
||||
.then((response) => TaskList.fromJson(response.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return TaskList.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -9,10 +9,11 @@ class NamespaceAPIService extends APIService implements NamespaceService {
|
|||
NamespaceAPIService(Client client) : super(client);
|
||||
|
||||
@override
|
||||
Future<Namespace> create(Namespace ns) {
|
||||
return client
|
||||
.put('/namespaces', body: ns.toJSON())
|
||||
.then((response) => Namespace.fromJson(response.body));
|
||||
Future<Namespace?> create(Namespace ns) {
|
||||
return client.put('/namespaces', body: ns.toJSON()).then((response) {
|
||||
if (response == null) return null;
|
||||
return Namespace.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -21,24 +22,28 @@ class NamespaceAPIService extends APIService implements NamespaceService {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Namespace> get(int namespaceId) {
|
||||
return client
|
||||
.get('/namespaces/$namespaceId')
|
||||
.then((response) => Namespace.fromJson(response.body));
|
||||
Future<Namespace?> get(int namespaceId) {
|
||||
return client.get('/namespaces/$namespaceId').then((response) {
|
||||
if (response == null) return null;
|
||||
return Namespace.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Namespace>> getAll() {
|
||||
Future<List<Namespace>?> getAll() {
|
||||
return client.get('/namespaces').then((response) {
|
||||
if(response.error)
|
||||
return Future.value([]);
|
||||
return convertList(response.body, (result) => Namespace.fromJson(result));});
|
||||
if (response == null) return null;
|
||||
return convertList(response.body, (result) => Namespace.fromJson(result));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Namespace> update(Namespace ns) {
|
||||
Future<Namespace?> update(Namespace ns) {
|
||||
return client
|
||||
.post('/namespaces/${ns.id}', body: ns.toJSON())
|
||||
.then((response) => Namespace.fromJson(response.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return Namespace.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,11 @@ class ServerAPIService extends APIService implements ServerService {
|
|||
ServerAPIService(Client client) : super(client);
|
||||
|
||||
@override
|
||||
Future<Server> getInfo() {
|
||||
return client.get('/info').then((value) => Server.fromJson(value.body));
|
||||
Future<Server?> getInfo() {
|
||||
return client.get('/info').then((value) {
|
||||
if(value == null)
|
||||
return null;
|
||||
return Server.fromJson(value.body);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,17 +11,23 @@ class TaskAPIService extends APIService implements TaskService {
|
|||
TaskAPIService(Client client) : super(client);
|
||||
|
||||
@override
|
||||
Future<Task> add(int listId, Task task) {
|
||||
Future<Task?> add(int listId, Task task) {
|
||||
return client
|
||||
.put('/lists/$listId', body: task.toJSON())
|
||||
.then((response) => Task.fromJson(response.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return Task.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Task> get(int listId) {
|
||||
Future<Task?> get(int listId) {
|
||||
return client
|
||||
.get('/list/$listId/tasks')
|
||||
.then((response) => Task.fromJson(response.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return Task.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -31,20 +37,22 @@ class TaskAPIService extends APIService implements TaskService {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Task> update(Task task) {
|
||||
Future<Task?> update(Task task) {
|
||||
return client
|
||||
.post('/tasks/${task.id}', body: task.toJSON())
|
||||
.then((response) => Task.fromJson(response.body));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return Task.fromJson(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Task>> getAll() {
|
||||
Future<List<Task>?> getAll() {
|
||||
return client
|
||||
.get('/tasks/all')
|
||||
.then((response) {
|
||||
int page_n = 0;
|
||||
if(response.error)
|
||||
return Future.value([]);
|
||||
if (response == null) return null;
|
||||
if (response.headers["x-pagination-total-pages"] != null) {
|
||||
page_n = int.parse(response.headers["x-pagination-total-pages"]!);
|
||||
} else {
|
||||
|
@ -58,7 +66,7 @@ class TaskAPIService extends APIService implements TaskService {
|
|||
Map<String, List<String>> paramMap = {
|
||||
"page": [i.toString()]
|
||||
};
|
||||
futureList.add(client.get('/tasks/all', paramMap).then((pageResponse) { convertList(pageResponse.body, (result) {taskList.add(Task.fromJson(result));});}));
|
||||
futureList.add(client.get('/tasks/all', paramMap).then((pageResponse) {convertList(pageResponse?.body, (result) {taskList.add(Task.fromJson(result));});}));
|
||||
}
|
||||
return Future.wait(futureList).then((value) {
|
||||
return taskList;
|
||||
|
@ -67,29 +75,27 @@ class TaskAPIService extends APIService implements TaskService {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<Response> getAllByList(int listId,
|
||||
Future<Response?> getAllByList(int listId,
|
||||
[Map<String, List<String>>? queryParameters]) {
|
||||
return client
|
||||
.get('/lists/$listId/tasks', queryParameters).then(
|
||||
(response) {
|
||||
if(response.error)
|
||||
return Response("", 0, {}, error: true);
|
||||
return new Response(
|
||||
return response != null ?
|
||||
new Response(
|
||||
convertList(response.body, (result) => Task.fromJson(result)),
|
||||
response.statusCode,
|
||||
response.headers);
|
||||
response.headers) : null;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Task>> getByOptions(TaskServiceOptions options) {
|
||||
Future<List<Task>?> getByOptions(TaskServiceOptions options) {
|
||||
String optionString = options.getOptions();
|
||||
return client
|
||||
.get('/tasks/all?$optionString')
|
||||
.then((value) {
|
||||
if(value.error)
|
||||
return Future.value([]);
|
||||
return convertList(value.body, (result) => Task.fromJson(result));
|
||||
.then((response) {
|
||||
if (response == null) return null;
|
||||
return convertList(response.body, (result) => Task.fromJson(result));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ class UserAPIService extends APIService implements UserService {
|
|||
'password': password,
|
||||
'totp_passcode': totp,
|
||||
'username': username,
|
||||
}).then((response) => response.body['token']);
|
||||
}).then((response) => response?.body['token']);
|
||||
client.configure(token: token);
|
||||
return UserAPIService(client)
|
||||
.getCurrentUser()
|
||||
|
@ -30,12 +30,12 @@ class UserAPIService extends APIService implements UserService {
|
|||
'username': username,
|
||||
'email': email,
|
||||
'password': password
|
||||
}).then((resp) => resp.body['username']);
|
||||
}).then((resp) => resp?.body['username']);
|
||||
return login(newUser, password);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<User> getCurrentUser() {
|
||||
return client.get('/user').then((map) => User.fromJson(map.body));
|
||||
return client.get('/user').then((map) => User.fromJson(map?.body));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,15 +109,16 @@ class TaskTileState extends State<TaskTile> with AutomaticKeepAliveClientMixin {
|
|||
setState(() {
|
||||
this._currentTask.loading = true;
|
||||
});
|
||||
Task newTask = await _updateTask(_currentTask, value);
|
||||
Task? newTask = await _updateTask(_currentTask, value);
|
||||
setState(() {
|
||||
this._currentTask = newTask;
|
||||
if(newTask != null)
|
||||
this._currentTask = newTask;
|
||||
this._currentTask.loading = false;
|
||||
});
|
||||
widget.onEdit();
|
||||
}
|
||||
|
||||
Future<Task> _updateTask(Task task, bool checked) {
|
||||
Future<Task?> _updateTask(Task task, bool checked) {
|
||||
return Provider.of<ListProvider>(context, listen: false).updateTask(
|
||||
context: context,
|
||||
task: task.copyWith(
|
||||
|
|
|
@ -159,8 +159,12 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
|
|||
}
|
||||
|
||||
Future<void> scheduleDueNotifications() async {
|
||||
await notificationsPlugin.cancelAll();
|
||||
final tasks = await taskService.getAll();
|
||||
if(tasks == null) {
|
||||
dev.log("did not receive tasks on notification update");
|
||||
return;
|
||||
}
|
||||
await notificationsPlugin.cancelAll();
|
||||
for (final task in tasks) {
|
||||
for (final reminder in task.reminderDates) {
|
||||
scheduleNotification(
|
||||
|
|
|
@ -218,7 +218,8 @@ class HomePageState extends State<HomePage> with AfterLayoutMixin<HomePage> {
|
|||
return VikunjaGlobal.of(context).namespaceService.getAll().then((result) {
|
||||
setState(() {
|
||||
_loading = false;
|
||||
_namespaces = result;
|
||||
if(result != null)
|
||||
_namespaces = result;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -69,13 +69,26 @@ class LandingPageState extends State<LandingPage>
|
|||
switch (landingPageStatus) {
|
||||
case LandingPageStatus.built:
|
||||
_loadList(context);
|
||||
body = new Stack(children: [ListView(), Center(child: CircularProgressIndicator(),)]);
|
||||
body = new Stack(children: [
|
||||
ListView(),
|
||||
Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
]);
|
||||
break;
|
||||
case LandingPageStatus.loading:
|
||||
body = new Stack(children: [ListView(), Center(child: CircularProgressIndicator(),)]);
|
||||
body = new Stack(children: [
|
||||
ListView(),
|
||||
Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
]);
|
||||
break;
|
||||
case LandingPageStatus.error:
|
||||
body = new Stack(children: [ListView(), Center(child: Text("There was an error loading this view"))]);
|
||||
body = new Stack(children: [
|
||||
ListView(),
|
||||
Center(child: Text("There was an error loading this view"))
|
||||
]);
|
||||
break;
|
||||
case LandingPageStatus.success:
|
||||
body = ListView(
|
||||
|
@ -165,15 +178,19 @@ class LandingPageState extends State<LandingPage>
|
|||
.taskService
|
||||
.getByOptions(VikunjaGlobal.of(context).taskServiceOptions)
|
||||
.then<Future<void>?>((taskList) {
|
||||
if (taskList.isEmpty) {
|
||||
if (taskList != null && taskList.isEmpty) {
|
||||
landingPageStatus = LandingPageStatus.error;
|
||||
return null;
|
||||
}
|
||||
return VikunjaGlobal.of(context).listService.getAll().then((lists) {
|
||||
//taskList.forEach((task) {task.list = lists.firstWhere((element) => element.id == task.list_id);});
|
||||
setState(() {
|
||||
_list = taskList;
|
||||
landingPageStatus = LandingPageStatus.success;
|
||||
if (taskList != null) {
|
||||
_list = taskList;
|
||||
landingPageStatus = LandingPageStatus.success;
|
||||
} else {
|
||||
landingPageStatus = LandingPageStatus.error;
|
||||
}
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
|
|
@ -423,6 +423,7 @@ class _TaskEditPageState extends State<TaskEditPage> {
|
|||
return VikunjaGlobal.of(context)
|
||||
.labelService.getAll(query: query).then((labels) {
|
||||
// Only show those labels which aren't already added to the task
|
||||
if(labels == null) return [];
|
||||
labels.removeWhere((labelToRemove) => _labels.contains(labelToRemove));
|
||||
_suggestedLabels = labels;
|
||||
List<String?> labelText = labels.map((label) => label.title).toList();
|
||||
|
@ -465,6 +466,7 @@ class _TaskEditPageState extends State<TaskEditPage> {
|
|||
.labelService
|
||||
.create(newLabel)
|
||||
.then((createdLabel) {
|
||||
if(createdLabel == null) return null;
|
||||
setState(() {
|
||||
_labels.add(createdLabel);
|
||||
_labelTypeAheadController.clear();
|
||||
|
|
|
@ -96,9 +96,10 @@ class _NamespacePageState extends State<NamespacePage>
|
|||
.listService
|
||||
.getByNamespace(widget.namespace.id)
|
||||
.then((lists) => setState(() {
|
||||
this._lists = lists;
|
||||
this._loading = false;
|
||||
}));
|
||||
this._loading = false;
|
||||
if(lists != null)
|
||||
this._lists = lists;
|
||||
}));
|
||||
}
|
||||
|
||||
_openList(BuildContext context, TaskList list) {
|
||||
|
|
|
@ -85,6 +85,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
enabled: !_loading,
|
||||
controller: _serverController,
|
||||
autocorrect: false,
|
||||
autofillHints: [AutofillHints.url],
|
||||
validator: (address) {
|
||||
return (isUrl(address) || address != null || address!.isEmpty) ? null : 'Invalid URL';
|
||||
},
|
||||
|
@ -98,6 +99,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
child: TextFormField(
|
||||
enabled: !_loading,
|
||||
controller: _usernameController,
|
||||
autofillHints: [AutofillHints.username],
|
||||
decoration: new InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'Username'),
|
||||
|
@ -108,6 +110,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
child: TextFormField(
|
||||
enabled: !_loading,
|
||||
controller: _passwordController,
|
||||
autofillHints: [AutofillHints.password],
|
||||
decoration: new InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'Password'),
|
||||
|
@ -184,8 +187,9 @@ class _LoginPageState extends State<LoginPage> {
|
|||
if(_server.endsWith("/"))
|
||||
_server = _server.substring(0,_server.length-1);
|
||||
vGlobal.client.configure(base: _server);
|
||||
Server info = await vGlobal.serverService.getInfo();
|
||||
|
||||
Server? info = await vGlobal.serverService.getInfo();
|
||||
if(info == null)
|
||||
throw Exception("Getting server info failed");
|
||||
|
||||
UserTokenPair? newUser;
|
||||
|
||||
|
@ -219,7 +223,6 @@ class _LoginPageState extends State<LoginPage> {
|
|||
vGlobal.changeUser(newUser.user, token: newUser.token, base: _server);
|
||||
} catch (ex, stacktrace) {
|
||||
log(stacktrace.toString());
|
||||
throw ex;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => new AlertDialog(
|
||||
|
|
|
@ -83,35 +83,35 @@ class TaskServiceOptions {
|
|||
}
|
||||
|
||||
abstract class NamespaceService {
|
||||
Future<List<Namespace>> getAll();
|
||||
Future<Namespace> get(int namespaceId);
|
||||
Future<Namespace> create(Namespace ns);
|
||||
Future<Namespace> update(Namespace ns);
|
||||
Future<List<Namespace>?> getAll();
|
||||
Future<Namespace?> get(int namespaceId);
|
||||
Future<Namespace?> create(Namespace ns);
|
||||
Future<Namespace?> update(Namespace ns);
|
||||
Future delete(int namespaceId);
|
||||
}
|
||||
|
||||
abstract class ListService {
|
||||
Future<List<TaskList>> getAll();
|
||||
Future<TaskList> get(int listId);
|
||||
Future<List<TaskList>> getByNamespace(int namespaceId);
|
||||
Future<TaskList> create(int namespaceId, TaskList tl);
|
||||
Future<TaskList> update(TaskList tl);
|
||||
Future<List<TaskList>?> getAll();
|
||||
Future<TaskList?> get(int listId);
|
||||
Future<List<TaskList>?> getByNamespace(int namespaceId);
|
||||
Future<TaskList?> create(int namespaceId, TaskList tl);
|
||||
Future<TaskList?> update(TaskList tl);
|
||||
Future delete(int listId);
|
||||
Future<String> getDisplayDoneTasks(int listId);
|
||||
Future<String?> getDisplayDoneTasks(int listId);
|
||||
void setDisplayDoneTasks(int listId, String value);
|
||||
Future<String?> getDefaultList();
|
||||
void setDefaultList(int? listId);
|
||||
}
|
||||
|
||||
abstract class TaskService {
|
||||
Future<Task> get(int taskId);
|
||||
Future<Task> update(Task task);
|
||||
Future<Task?> get(int taskId);
|
||||
Future<Task?> update(Task task);
|
||||
Future delete(int taskId);
|
||||
Future<Task> add(int listId, Task task);
|
||||
Future<List<Task>> getAll();
|
||||
Future<Response> getAllByList(int listId,
|
||||
Future<Task?> add(int listId, Task task);
|
||||
Future<List<Task>?> getAll();
|
||||
Future<Response?> getAllByList(int listId,
|
||||
[Map<String, List<String>> queryParameters]);
|
||||
Future<List<Task>> getByOptions(TaskServiceOptions options);
|
||||
Future<List<Task>?> getByOptions(TaskServiceOptions options);
|
||||
|
||||
int get maxPages;
|
||||
}
|
||||
|
@ -119,41 +119,41 @@ abstract class TaskService {
|
|||
abstract class BucketService {
|
||||
// Not implemented in the Vikunja API
|
||||
// Future<Bucket> get(int listId, int bucketId);
|
||||
Future<Bucket> update(Bucket bucket);
|
||||
Future<Bucket?> update(Bucket bucket);
|
||||
Future delete(int listId, int bucketId);
|
||||
Future<Bucket> add(int listId, Bucket bucket);
|
||||
Future<Response> getAllByList(int listId,
|
||||
Future<Bucket?> add(int listId, Bucket bucket);
|
||||
Future<Response?> getAllByList(int listId,
|
||||
[Map<String, List<String>> queryParameters]);
|
||||
|
||||
int get maxPages;
|
||||
}
|
||||
|
||||
abstract class UserService {
|
||||
Future<UserTokenPair> login(String username, String password, {bool rememberMe = false, String totp});
|
||||
Future<UserTokenPair> register(String username, email, password);
|
||||
Future<User> getCurrentUser();
|
||||
Future<UserTokenPair?> login(String username, String password, {bool rememberMe = false, String totp});
|
||||
Future<UserTokenPair?> register(String username, email, password);
|
||||
Future<User?> getCurrentUser();
|
||||
}
|
||||
|
||||
abstract class LabelService {
|
||||
Future<List<Label>> getAll({String query});
|
||||
Future<Label> get(int labelID);
|
||||
Future<Label> create(Label label);
|
||||
Future<Label> delete(Label label);
|
||||
Future<Label> update(Label label);
|
||||
Future<List<Label>?> getAll({String query});
|
||||
Future<Label?> get(int labelID);
|
||||
Future<Label?> create(Label label);
|
||||
Future<Label?> delete(Label label);
|
||||
Future<Label?> update(Label label);
|
||||
}
|
||||
|
||||
abstract class LabelTaskService {
|
||||
Future<List<Label>> getAll(LabelTask lt, {String query});
|
||||
Future<Label> create(LabelTask lt);
|
||||
Future<Label> delete(LabelTask lt);
|
||||
Future<List<Label>?> getAll(LabelTask lt, {String query});
|
||||
Future<Label?> create(LabelTask lt);
|
||||
Future<Label?> delete(LabelTask lt);
|
||||
}
|
||||
|
||||
abstract class LabelTaskBulkService {
|
||||
Future<List<Label>> update(Task task, List<Label> labels);
|
||||
Future<List<Label>?> update(Task task, List<Label> labels);
|
||||
}
|
||||
|
||||
abstract class ServerService {
|
||||
Future<Server> getInfo();
|
||||
Future<Server?> getInfo();
|
||||
}
|
||||
|
||||
class SettingsManager {
|
||||
|
|
|
@ -57,7 +57,7 @@ class ListProvider with ChangeNotifier {
|
|||
}
|
||||
return VikunjaGlobal.of(context).taskService.getAllByList(listId, queryParams).then((response) {
|
||||
_isLoading = false;
|
||||
if(response.error)
|
||||
if(response == null)
|
||||
return;
|
||||
if (response.headers["x-pagination-total-pages"] != null) {
|
||||
_maxPages = int.parse(response.headers["x-pagination-total-pages"]!);
|
||||
|
@ -77,6 +77,8 @@ class ListProvider with ChangeNotifier {
|
|||
};
|
||||
|
||||
return VikunjaGlobal.of(context).bucketService.getAllByList(listId, queryParams).then((response) {
|
||||
if(response == null)
|
||||
return;
|
||||
if (response.headers["x-pagination-total-pages"] != null) {
|
||||
_maxPages = int.parse(response.headers["x-pagination-total-pages"]!);
|
||||
}
|
||||
|
@ -104,8 +106,9 @@ class ListProvider with ChangeNotifier {
|
|||
notifyListeners();
|
||||
|
||||
return globalState.taskService.add(listId, newTask).then((task) {
|
||||
_tasks.insert(0, task);
|
||||
_isLoading = false;
|
||||
if(task != null)
|
||||
_tasks.insert(0, task);
|
||||
notifyListeners();
|
||||
});
|
||||
}
|
||||
|
@ -116,6 +119,8 @@ class ListProvider with ChangeNotifier {
|
|||
notifyListeners();
|
||||
|
||||
return globalState.taskService.add(listId, newTask).then((task) {
|
||||
if (task == null)
|
||||
return;
|
||||
if (_tasks.isNotEmpty)
|
||||
_tasks.insert(0, task);
|
||||
if (_buckets.isNotEmpty) {
|
||||
|
@ -127,10 +132,12 @@ class ListProvider with ChangeNotifier {
|
|||
});
|
||||
}
|
||||
|
||||
Future<Task> updateTask({required BuildContext context, required Task task}) {
|
||||
Future<Task?> updateTask({required BuildContext context, required Task task}) {
|
||||
return VikunjaGlobal.of(context).taskService.update(task).then((task) {
|
||||
// FIXME: This is ugly. We should use a redux to not have to do these kind of things.
|
||||
// This is enough for now (it works™) but we should definitly fix it later.
|
||||
if(task == null)
|
||||
return null;
|
||||
_tasks.asMap().forEach((i, t) {
|
||||
if (task.id == t.id) {
|
||||
_tasks[i] = task;
|
||||
|
@ -150,6 +157,8 @@ class ListProvider with ChangeNotifier {
|
|||
notifyListeners();
|
||||
return VikunjaGlobal.of(context).bucketService.add(listId, newBucket)
|
||||
.then((bucket) {
|
||||
if(bucket == null)
|
||||
return null;
|
||||
_buckets.add(bucket);
|
||||
notifyListeners();
|
||||
});
|
||||
|
@ -158,6 +167,8 @@ class ListProvider with ChangeNotifier {
|
|||
Future<void> updateBucket({required BuildContext context, required Bucket bucket}) {
|
||||
return VikunjaGlobal.of(context).bucketService.update(bucket)
|
||||
.then((rBucket) {
|
||||
if(rBucket == null)
|
||||
return null;
|
||||
_buckets[_buckets.indexWhere((b) => rBucket.id == b.id)] = rBucket;
|
||||
_buckets.sort((a, b) => a.position!.compareTo(b.position!));
|
||||
notifyListeners();
|
||||
|
@ -172,12 +183,14 @@ class ListProvider with ChangeNotifier {
|
|||
});
|
||||
}
|
||||
|
||||
Future<void> moveTaskToBucket({required BuildContext context, required Task task, int? newBucketId, required int index}) async {
|
||||
Future<void> moveTaskToBucket({required BuildContext context, required Task? task, int? newBucketId, required int index}) async {
|
||||
if(task == null)
|
||||
throw Exception("Task to be moved may not be null");
|
||||
final sameBucket = task.bucketId == newBucketId;
|
||||
final newBucketIndex = _buckets.indexWhere((b) => b.id == newBucketId);
|
||||
if (sameBucket && index > _buckets[newBucketIndex].tasks.indexWhere((t) => t.id == task.id)) index--;
|
||||
if (sameBucket && index > _buckets[newBucketIndex].tasks.indexWhere((t) => t.id == task?.id)) index--;
|
||||
|
||||
_buckets[_buckets.indexWhere((b) => b.id == task.bucketId)].tasks.remove(task);
|
||||
_buckets[_buckets.indexWhere((b) => b.id == task?.bucketId)].tasks.remove(task);
|
||||
if (index >= _buckets[newBucketIndex].tasks.length)
|
||||
_buckets[newBucketIndex].tasks.add(task);
|
||||
else
|
||||
|
@ -192,6 +205,8 @@ class ListProvider with ChangeNotifier {
|
|||
? _buckets[newBucketIndex].tasks[index + 1].kanbanPosition : null,
|
||||
),
|
||||
));
|
||||
if(task == null)
|
||||
return;
|
||||
_buckets[newBucketIndex].tasks[index] = task;
|
||||
|
||||
// make sure the first 2 tasks don't have 0 kanbanPosition
|
||||
|
@ -206,16 +221,17 @@ class ListProvider with ChangeNotifier {
|
|||
? _buckets[newBucketIndex].tasks[2].kanbanPosition : null,
|
||||
),
|
||||
));
|
||||
_buckets[newBucketIndex].tasks[1] = secondTask;
|
||||
if(secondTask != null)
|
||||
_buckets[newBucketIndex].tasks[1] = secondTask;
|
||||
}
|
||||
|
||||
if (_tasks.isNotEmpty) {
|
||||
_tasks[_tasks.indexWhere((t) => t.id == task.id)] = task;
|
||||
_tasks[_tasks.indexWhere((t) => t.id == task?.id)] = task;
|
||||
if (secondTask != null)
|
||||
_tasks[_tasks.indexWhere((t) => t.id == secondTask!.id)] = secondTask;
|
||||
}
|
||||
|
||||
_buckets[newBucketIndex].tasks[_buckets[newBucketIndex].tasks.indexWhere((t) => t.id == task.id)] = task;
|
||||
_buckets[newBucketIndex].tasks[_buckets[newBucketIndex].tasks.indexWhere((t) => t.id == task?.id)] = task;
|
||||
_buckets[newBucketIndex].tasks.sort((a, b) => a.kanbanPosition!.compareTo(b.kanbanPosition!));
|
||||
|
||||
notifyListeners();
|
||||
|
|
Loading…
Reference in New Issue
Block a user