1
0
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:
Benimautner 2022-10-14 18:07:35 +02:00
parent dd0d8e624d
commit e3e23e8c27
19 changed files with 249 additions and 144 deletions

View File

@ -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);
});
}
}

View File

@ -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);

View File

@ -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));
});
}
}

View File

@ -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));
});
}
}

View File

@ -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);
});
}
}

View File

@ -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

View File

@ -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);
});
}
}

View File

@ -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);
});
}
}

View File

@ -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));
});
}

View File

@ -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));
}
}

View File

@ -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(

View File

@ -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(

View File

@ -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;
});
});
}

View File

@ -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;
});

View File

@ -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();

View File

@ -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) {

View File

@ -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(

View File

@ -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 {

View File

@ -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();