mirror of
https://github.com/go-vikunja/app
synced 2024-06-02 18:49:47 +00:00
added error handler to client for network requests
This commit is contained in:
parent
57b219836f
commit
397a06e9a0
|
@ -32,15 +32,18 @@ class Client {
|
|||
return otherClient._token == _token;
|
||||
}
|
||||
|
||||
Client(this.global, {String? token, String? base, bool authenticated = false}) {
|
||||
Client(this.global,
|
||||
{String? token, String? base, bool authenticated = false}) {
|
||||
configure(token: token, base: base, authenticated: authenticated);
|
||||
}
|
||||
|
||||
void reload_ignore_certs(bool? val) {
|
||||
ignoreCertificates = val ?? false;
|
||||
HttpOverrides.global = new IgnoreCertHttpOverrides(ignoreCertificates);
|
||||
VikunjaGlobal.of(global.currentContext!).settingsManager.setIgnoreCertificates(ignoreCertificates);
|
||||
|
||||
VikunjaGlobal
|
||||
.of(global.currentContext!)
|
||||
.settingsManager
|
||||
.setIgnoreCertificates(ignoreCertificates);
|
||||
}
|
||||
|
||||
get _headers =>
|
||||
|
@ -53,11 +56,11 @@ class Client {
|
|||
int get hashCode => _token.hashCode;
|
||||
|
||||
void configure({String? token, String? base, bool? authenticated}) {
|
||||
if(token != null)
|
||||
if (token != null)
|
||||
_token = token;
|
||||
if(base != null)
|
||||
if (base != null)
|
||||
_base = base.endsWith('/api/v1') ? base : '$base/api/v1';
|
||||
if(authenticated != null)
|
||||
if (authenticated != null)
|
||||
this.authenticated = authenticated;
|
||||
}
|
||||
|
||||
|
@ -69,9 +72,11 @@ class Client {
|
|||
|
||||
Future<Response> get(String url,
|
||||
[Map<String, List<String>>? queryParameters]) {
|
||||
final uri = Uri.parse('${this.base}$url').replace(queryParameters: queryParameters);
|
||||
final uri = Uri.parse('${this.base}$url').replace(
|
||||
queryParameters: queryParameters);
|
||||
return http.get(uri, headers: _headers)
|
||||
.then(_handleResponse, onError: _handleError);
|
||||
.then(_handleResponse).onError((error, stackTrace) =>
|
||||
_handleError(error, stackTrace));
|
||||
}
|
||||
|
||||
Future<Response> delete(String url) {
|
||||
|
@ -80,7 +85,8 @@ class Client {
|
|||
'${this.base}$url'.toUri()!,
|
||||
headers: _headers,
|
||||
)
|
||||
.then(_handleResponse, onError: _handleError);
|
||||
.then(_handleResponse).onError((error, stackTrace) =>
|
||||
_handleError(error, stackTrace));
|
||||
}
|
||||
|
||||
Future<Response> post(String url, {dynamic body}) {
|
||||
|
@ -90,7 +96,8 @@ class Client {
|
|||
headers: _headers,
|
||||
body: _encoder.convert(body),
|
||||
)
|
||||
.then(_handleResponse, onError: _handleError);
|
||||
.then(_handleResponse).onError((error, stackTrace) =>
|
||||
_handleError(error, stackTrace));
|
||||
}
|
||||
|
||||
Future<Response> put(String url, {dynamic body}) {
|
||||
|
@ -100,14 +107,16 @@ class Client {
|
|||
headers: _headers,
|
||||
body: _encoder.convert(body),
|
||||
)
|
||||
.then(_handleResponse, onError: _handleError);
|
||||
.then(_handleResponse).onError((error, stackTrace) =>
|
||||
_handleError(error, stackTrace));
|
||||
}
|
||||
|
||||
void _handleError(dynamic e) {
|
||||
log(e.toString());
|
||||
FutureOr<Response> _handleError(Object? e, StackTrace? st) {
|
||||
SnackBar snackBar = SnackBar(
|
||||
content: Text("Error on request: " + e.toString()));
|
||||
content: Text("Error on request: " + e.toString()),
|
||||
action: SnackBarAction(label: "Clear", onPressed: () => global.currentState?.clearSnackBars()),);
|
||||
global.currentState?.showSnackBar(snackBar);
|
||||
return Response("", 0, {}, error: true);
|
||||
}
|
||||
|
||||
Map<String, String> headersToMap(HttpHeaders headers) {
|
||||
|
|
|
@ -43,7 +43,10 @@ class ListAPIService extends APIService implements ListService {
|
|||
@override
|
||||
Future<List<TaskList>> getAll() {
|
||||
return client.get('/lists').then(
|
||||
(list) => convertList(list.body, (result) => TaskList.fromJson(result)));
|
||||
(list) {
|
||||
if (list.body.toString().isEmpty)
|
||||
return Future.value([]);
|
||||
return convertList(list.body, (result) => TaskList.fromJson(result));});
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'dart:developer';
|
||||
import 'package:vikunja_app/api/client.dart';
|
||||
import 'package:vikunja_app/api/service.dart';
|
||||
import 'package:vikunja_app/models/namespace.dart';
|
||||
|
@ -29,8 +29,10 @@ class NamespaceAPIService extends APIService implements NamespaceService {
|
|||
|
||||
@override
|
||||
Future<List<Namespace>> getAll() {
|
||||
return client.get('/namespaces').then((response) =>
|
||||
convertList(response.body, (result) => Namespace.fromJson(result)));
|
||||
return client.get('/namespaces').then((response) {
|
||||
if(response.error)
|
||||
return Future.value([]);
|
||||
return convertList(response.body, (result) => Namespace.fromJson(result));});
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// 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);
|
||||
Response(this.body, this.statusCode, this.headers, {this.error = false});
|
||||
|
||||
final dynamic body;
|
||||
final int statusCode;
|
||||
final Map<String, String> headers;
|
||||
final bool error;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ class TaskAPIService extends APIService implements TaskService {
|
|||
.get('/tasks/all')
|
||||
.then((response) {
|
||||
int page_n = 0;
|
||||
if(response.error)
|
||||
return Future.value([]);
|
||||
if (response.headers["x-pagination-total-pages"] != null) {
|
||||
page_n = int.parse(response.headers["x-pagination-total-pages"]!);
|
||||
} else {
|
||||
|
@ -69,10 +71,14 @@ class TaskAPIService extends APIService implements TaskService {
|
|||
[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));
|
||||
(response) {
|
||||
if(response.error)
|
||||
return Response("", 0, {}, error: true);
|
||||
return new Response(
|
||||
convertList(response.body, (result) => Task.fromJson(result)),
|
||||
response.statusCode,
|
||||
response.headers);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -81,7 +87,9 @@ class TaskAPIService extends APIService implements TaskService {
|
|||
return client
|
||||
.get('/tasks/all?$optionString')
|
||||
.then((value) {
|
||||
return convertList(value.body, (result) => Task.fromJson(result));
|
||||
if(value.error)
|
||||
return Future.value([]);
|
||||
return convertList(value.body, (result) => Task.fromJson(result));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -10,45 +10,42 @@ import '../components/TaskTile.dart';
|
|||
import '../models/task.dart';
|
||||
|
||||
class LandingPage extends StatefulWidget {
|
||||
|
||||
const LandingPage(
|
||||
{Key? key})
|
||||
: super(key: key);
|
||||
const LandingPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => LandingPageState();
|
||||
|
||||
}
|
||||
|
||||
class LandingPageState extends State<LandingPage> with AfterLayoutMixin<LandingPage> {
|
||||
enum LandingPageStatus { built, loading, success, error }
|
||||
|
||||
class LandingPageState extends State<LandingPage>
|
||||
with AfterLayoutMixin<LandingPage> {
|
||||
int? defaultList;
|
||||
List<Task>? _list;
|
||||
List<Task> _list = [];
|
||||
LandingPageStatus landingPageStatus = LandingPageStatus.built;
|
||||
static const platform = const MethodChannel('vikunja');
|
||||
|
||||
|
||||
Future<void> _updateDefaultList() async {
|
||||
return VikunjaGlobal.of(context)
|
||||
.listService
|
||||
.getDefaultList()
|
||||
.then((value) => setState(() => defaultList = value == null ? null : int.tryParse(value)));
|
||||
return VikunjaGlobal.of(context).listService.getDefaultList().then(
|
||||
(value) => setState(
|
||||
() => defaultList = value == null ? null : int.tryParse(value)));
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
Future.delayed(Duration.zero, () =>
|
||||
_updateDefaultList().then((value) {
|
||||
try {
|
||||
platform.invokeMethod("isQuickTile","").then((value) => {
|
||||
if(value is bool && value)
|
||||
_addItemDialog(context)
|
||||
});
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}}));
|
||||
Future.delayed(
|
||||
Duration.zero,
|
||||
() => _updateDefaultList().then((value) {
|
||||
try {
|
||||
platform.invokeMethod("isQuickTile", "").then((value) =>
|
||||
{if (value is bool && value) _addItemDialog(context)});
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
}));
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void afterFirstLayout(BuildContext context) {
|
||||
try {
|
||||
|
@ -66,46 +63,55 @@ class LandingPageState extends State<LandingPage> with AfterLayoutMixin<LandingP
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if(_list == null || _list!.isEmpty)
|
||||
_loadList(context);
|
||||
Widget body;
|
||||
switch (landingPageStatus) {
|
||||
case LandingPageStatus.built:
|
||||
_loadList(context);
|
||||
body = new Stack(children: [ListView(), Center(child: CircularProgressIndicator(),)]);
|
||||
break;
|
||||
case LandingPageStatus.loading:
|
||||
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"))]);
|
||||
break;
|
||||
case LandingPageStatus.success:
|
||||
body = ListView(
|
||||
scrollDirection: Axis.vertical,
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
children:
|
||||
ListTile.divideTiles(context: context, tiles: _listTasks(context))
|
||||
.toList(),
|
||||
);
|
||||
break;
|
||||
}
|
||||
return new Scaffold(
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () => _loadList(context),
|
||||
child: _list != null ? ListView(
|
||||
scrollDirection: Axis.vertical,
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
children: ListTile.divideTiles(
|
||||
context: context, tiles: _listTasks(context)).toList(),
|
||||
) : new Center(child: CircularProgressIndicator(),),
|
||||
),
|
||||
body:
|
||||
RefreshIndicator(onRefresh: () => _loadList(context), child: body),
|
||||
floatingActionButton: Builder(
|
||||
builder: (context) =>
|
||||
FloatingActionButton(
|
||||
builder: (context) => FloatingActionButton(
|
||||
onPressed: () {
|
||||
_addItemDialog(context);
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
)
|
||||
)
|
||||
);
|
||||
)));
|
||||
}
|
||||
|
||||
_addItemDialog(BuildContext context) {
|
||||
if(defaultList == null) {
|
||||
if (defaultList == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text('Please select a default list in the settings'),
|
||||
));
|
||||
} else {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) =>
|
||||
AddDialog(
|
||||
onAddTask: (title, dueDate) => _addTask(title, dueDate, context),
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'Task Name', hintText: 'eg. Milk')));
|
||||
builder: (_) => AddDialog(
|
||||
onAddTask: (title, dueDate) => _addTask(title, dueDate, context),
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'Task Name', hintText: 'eg. Milk')));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,9 +138,8 @@ class LandingPageState extends State<LandingPage> with AfterLayoutMixin<LandingP
|
|||
_loadList(context).then((value) => setState(() {}));
|
||||
}
|
||||
|
||||
|
||||
List<Widget> _listTasks(BuildContext context) {
|
||||
var tasks = (_list?.map((task) => _buildTile(task, context)) ?? []).toList();
|
||||
var tasks = (_list.map((task) => _buildTile(task, context))).toList();
|
||||
//tasks.addAll(_loadingTasks.map(_buildLoadingTile));
|
||||
return tasks;
|
||||
}
|
||||
|
@ -142,28 +147,36 @@ class LandingPageState extends State<LandingPage> with AfterLayoutMixin<LandingP
|
|||
TaskTile _buildTile(Task task, BuildContext context) {
|
||||
// key: UniqueKey() seems like a weird workaround to fix the loading issue
|
||||
// is there a better way?
|
||||
return TaskTile(key: UniqueKey(), task: task,onEdit: () => _loadList(context), showInfo: true,);
|
||||
return TaskTile(
|
||||
key: UniqueKey(),
|
||||
task: task,
|
||||
onEdit: () => _loadList(context),
|
||||
showInfo: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadList(BuildContext context) {
|
||||
log("reloading list");
|
||||
_list = [];
|
||||
landingPageStatus = LandingPageStatus.loading;
|
||||
// FIXME: loads and reschedules tasks each time list is updated
|
||||
VikunjaGlobal.of(context).scheduleDueNotifications();
|
||||
return VikunjaGlobal.of(context)
|
||||
.taskService
|
||||
.getByOptions(VikunjaGlobal.of(context).taskServiceOptions)
|
||||
.then((taskList) {
|
||||
VikunjaGlobal.of(context)
|
||||
.listService
|
||||
.getAll()
|
||||
.then((lists) {
|
||||
//taskList.forEach((task) {task.list = lists.firstWhere((element) => element.id == task.list_id);});
|
||||
setState(() {
|
||||
_list = taskList;
|
||||
});
|
||||
});
|
||||
.then<Future<void>?>((taskList) {
|
||||
if (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;
|
||||
});
|
||||
return null;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ class _ListPageState extends State<ListPage> {
|
|||
}(),
|
||||
),
|
||||
)
|
||||
: Center(child: Text('This list is empty.')),
|
||||
: Stack(children: [ListView(), Center(child: Text('This list is empty.'))]),
|
||||
onRefresh: _loadList,
|
||||
)
|
||||
: Center(child: CircularProgressIndicator()),
|
||||
|
|
|
@ -66,7 +66,7 @@ class _NamespacePageState extends State<NamespacePage>
|
|||
},
|
||||
))).toList(),
|
||||
)
|
||||
: Center(child: Text('This namespace is empty.')),
|
||||
: Stack(children: [ListView(),Center(child: Text('This namespace is empty.'))]),
|
||||
onRefresh: _loadLists,
|
||||
)
|
||||
: Center(child: CircularProgressIndicator()),
|
||||
|
|
|
@ -56,14 +56,15 @@ class ListProvider with ChangeNotifier {
|
|||
});
|
||||
}
|
||||
return VikunjaGlobal.of(context).taskService.getAllByList(listId, queryParams).then((response) {
|
||||
_isLoading = false;
|
||||
if(response.error)
|
||||
return;
|
||||
if (response.headers["x-pagination-total-pages"] != null) {
|
||||
_maxPages = int.parse(response.headers["x-pagination-total-pages"]!);
|
||||
}
|
||||
_tasks.addAll(response.body);
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
});
|
||||
}).onError((error, stackTrace) {_isLoading = false;});
|
||||
}
|
||||
|
||||
Future<void> loadBuckets({required BuildContext context, required int listId, int page = 1}) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user