mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-28 05:26:47 +00:00
API error messages now use snackIcon
- Press "details" for further error information - Is nice
This commit is contained in:
parent
8ae4d2b584
commit
00943b7536
@ -1,3 +1,11 @@
|
||||
## 0.1.1 - February 2021
|
||||
---
|
||||
|
||||
- Fixes crash bug on top-level part category
|
||||
- Fixed crash bug on top-level stock location
|
||||
- Adds context overlay to barcode scanner view
|
||||
- Notifcations are less obtrusive (uses snack bar)
|
||||
|
||||
## 0.1.0 - February 2021
|
||||
---
|
||||
This is the initial release of the InvenTree app.
|
||||
|
43
lib/api.dart
43
lib/api.dart
@ -3,6 +3,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:InvenTree/user_profile.dart';
|
||||
import 'package:InvenTree/widget/snacks.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
@ -12,6 +13,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:InvenTree/widget/dialogs.dart';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:one_context/one_context.dart';
|
||||
|
||||
|
||||
/**
|
||||
@ -172,13 +174,11 @@ class InvenTreeAPI {
|
||||
String password = profile.password.trim();
|
||||
|
||||
if (address.isEmpty || username.isEmpty || password.isEmpty) {
|
||||
await showErrorDialog(
|
||||
context,
|
||||
I18N.of(context).error,
|
||||
showSnackIcon(
|
||||
"Incomplete server details",
|
||||
icon: FontAwesomeIcons.server
|
||||
icon: FontAwesomeIcons.exclamationCircle,
|
||||
success: false
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -203,7 +203,6 @@ class InvenTreeAPI {
|
||||
|
||||
if (error is SocketException) {
|
||||
showServerError(
|
||||
context,
|
||||
I18N.of(context).connectionRefused,
|
||||
error.toString());
|
||||
return null;
|
||||
@ -224,7 +223,7 @@ class InvenTreeAPI {
|
||||
if (response.statusCode != 200) {
|
||||
// Any status code other than 200!
|
||||
|
||||
showStatusCodeError(context, response.statusCode);
|
||||
showStatusCodeError(response.statusCode);
|
||||
|
||||
// TODO: Interpret the error codes and show custom message?
|
||||
return false;
|
||||
@ -238,7 +237,6 @@ class InvenTreeAPI {
|
||||
if (!data.containsKey("server") || !data.containsKey("version") || !data.containsKey("instance")) {
|
||||
|
||||
showServerError(
|
||||
context,
|
||||
"Missing Data",
|
||||
"Server response missing required fields"
|
||||
);
|
||||
@ -253,9 +251,8 @@ class InvenTreeAPI {
|
||||
// Check that the remote server version is *new* enough
|
||||
if (!_checkServerVersion(_version)) {
|
||||
showServerError(
|
||||
context,
|
||||
"Old Server Version",
|
||||
"\n\nServer Version: ${_version}\n\nRequired version: ${_requiredVersionString}"
|
||||
I18N.of(OneContext().context).serverOld,
|
||||
"\n\nServer Version: ${_version}\n\nRequired version: ${_requiredVersionString}"
|
||||
);
|
||||
|
||||
return false;
|
||||
@ -275,22 +272,22 @@ class InvenTreeAPI {
|
||||
|
||||
if (response == null) {
|
||||
showServerError(
|
||||
context, "Token Error", "Error requesting access token from server"
|
||||
I18N.of(OneContext().context).tokenError,
|
||||
"Error requesting access token from server"
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
showStatusCodeError(context, response.statusCode);
|
||||
showStatusCodeError(response.statusCode);
|
||||
return false;
|
||||
} else {
|
||||
var data = json.decode(response.body);
|
||||
|
||||
if (!data.containsKey("token")) {
|
||||
showServerError(
|
||||
context,
|
||||
"Missing Token",
|
||||
I18N.of(OneContext().context).tokenMissing,
|
||||
"Access token missing from response"
|
||||
);
|
||||
|
||||
@ -328,10 +325,10 @@ class InvenTreeAPI {
|
||||
print("API Profile: ${profile.toString()}");
|
||||
|
||||
if (profile == null) {
|
||||
await showErrorDialog(
|
||||
context,
|
||||
"Select Profile",
|
||||
"User profile not selected"
|
||||
showSnackIcon(
|
||||
I18N.of(OneContext().context).profileSelect,
|
||||
success: false,
|
||||
icon: FontAwesomeIcons.exclamationCircle
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@ -344,6 +341,14 @@ class InvenTreeAPI {
|
||||
|
||||
_connecting = false;
|
||||
|
||||
if (result) {
|
||||
showSnackIcon(
|
||||
I18N.of(OneContext().context).serverConnected,
|
||||
icon: FontAwesomeIcons.server,
|
||||
success: true,
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,6 @@ class BarcodeHandler {
|
||||
// Called when the server does not know about a barcode
|
||||
// Override this function
|
||||
showErrorDialog(
|
||||
_context,
|
||||
"Invalid Barcode",
|
||||
"Barcode does not match any known item",
|
||||
error: "Barcode Error",
|
||||
@ -60,7 +59,6 @@ class BarcodeHandler {
|
||||
Future<void> onBarcodeUnhandled(Map<String, dynamic> data) {
|
||||
// Called when the server returns an unhandled response
|
||||
showErrorDialog(
|
||||
_context,
|
||||
"Response Data",
|
||||
data.toString(),
|
||||
error: "Unknown Response",
|
||||
@ -85,19 +83,8 @@ class BarcodeHandler {
|
||||
).then((var response) {
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
showErrorDialog(
|
||||
context,
|
||||
"Status Code: ${response.statusCode}",
|
||||
"${response.body
|
||||
.toString()
|
||||
.split('\n')
|
||||
.first}",
|
||||
onDismissed: () {
|
||||
_controller.resumeCamera();
|
||||
},
|
||||
error: "Server Error",
|
||||
icon: FontAwesomeIcons.server,
|
||||
);
|
||||
showStatusCodeError(response.statusCode);
|
||||
_controller.resumeCamera();
|
||||
|
||||
return;
|
||||
}
|
||||
@ -117,8 +104,7 @@ class BarcodeHandler {
|
||||
).catchError((error) {
|
||||
|
||||
showErrorDialog(
|
||||
context,
|
||||
"Error",
|
||||
I18N.of(OneContext().context).error,
|
||||
error.toString(),
|
||||
onDismissed: () {
|
||||
_controller.resumeCamera();
|
||||
@ -145,8 +131,9 @@ class BarcodeScanHandler extends BarcodeHandler {
|
||||
showSnackIcon(
|
||||
"No barcode",
|
||||
icon: FontAwesomeIcons.exclamationCircle,
|
||||
onTap: () {
|
||||
print("Tappity");
|
||||
actionText: "Details",
|
||||
onAction: () {
|
||||
print("Action!");
|
||||
},
|
||||
success: true,
|
||||
);
|
||||
@ -249,7 +236,6 @@ class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
|
||||
Future<void> onBarcodeMatched(Map<String, dynamic> data) {
|
||||
// If the barcode is known, we can't asisgn it to the stock item!
|
||||
showErrorDialog(
|
||||
_context,
|
||||
"Barcode in Use",
|
||||
"Barcode is already known",
|
||||
onDismissed: () {
|
||||
@ -264,7 +250,6 @@ class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
|
||||
|
||||
if (!data.containsKey("hash")) {
|
||||
showErrorDialog(
|
||||
_context,
|
||||
"Missing Data",
|
||||
"Missing hash data from server",
|
||||
onDismissed: () {
|
||||
@ -291,7 +276,6 @@ class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
|
||||
);
|
||||
} else {
|
||||
showErrorDialog(
|
||||
_context,
|
||||
"Server Error",
|
||||
"Could not assign barcode",
|
||||
onDismissed: () {
|
||||
|
@ -148,7 +148,6 @@ class InvenTreeModel {
|
||||
|
||||
if (e is SocketException) {
|
||||
showServerError(
|
||||
context,
|
||||
I18N.of(context).connectionRefused,
|
||||
e.toString()
|
||||
);
|
||||
@ -168,7 +167,7 @@ class InvenTreeModel {
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
showStatusCodeError(context, response.statusCode);
|
||||
showStatusCodeError(response.statusCode);
|
||||
print("Error retrieving data");
|
||||
return false;
|
||||
}
|
||||
@ -195,7 +194,6 @@ class InvenTreeModel {
|
||||
|
||||
if (e is SocketException) {
|
||||
showServerError(
|
||||
context,
|
||||
I18N.of(context).connectionRefused,
|
||||
e.toString()
|
||||
);
|
||||
@ -212,7 +210,7 @@ class InvenTreeModel {
|
||||
if (response == null) return false;
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
showStatusCodeError(context, response.statusCode);
|
||||
showStatusCodeError(response.statusCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -248,7 +246,7 @@ class InvenTreeModel {
|
||||
.catchError((e) {
|
||||
|
||||
if (e is SocketException) {
|
||||
showServerError(context, I18N.of(context).connectionRefused, e.toString());
|
||||
showServerError(I18N.of(context).connectionRefused, e.toString());
|
||||
}
|
||||
else if (e is TimeoutException) {
|
||||
showTimeoutError(context);
|
||||
@ -264,7 +262,7 @@ class InvenTreeModel {
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
showStatusCodeError(context, response.statusCode);
|
||||
showStatusCodeError(response.statusCode);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -293,7 +291,6 @@ class InvenTreeModel {
|
||||
|
||||
if (e is SocketException) {
|
||||
showServerError(
|
||||
context,
|
||||
I18N.of(context).connectionRefused,
|
||||
e.toString()
|
||||
);
|
||||
@ -313,7 +310,7 @@ class InvenTreeModel {
|
||||
var decoded = json.decode(response.body);
|
||||
_model = createFromJson(decoded);
|
||||
} else {
|
||||
showStatusCodeError(context, response.statusCode);
|
||||
showStatusCodeError(response.statusCode);
|
||||
}
|
||||
});
|
||||
|
||||
@ -346,7 +343,6 @@ class InvenTreeModel {
|
||||
|
||||
if (e is SocketException) {
|
||||
showServerError(
|
||||
context,
|
||||
I18N.of(context).connectionRefused,
|
||||
e.toString()
|
||||
);
|
||||
@ -369,7 +365,7 @@ class InvenTreeModel {
|
||||
List<InvenTreeModel> results = new List<InvenTreeModel>();
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
showStatusCodeError(context, response.statusCode);
|
||||
showStatusCodeError(response.statusCode);
|
||||
|
||||
// Return empty list
|
||||
return results;
|
||||
|
@ -442,7 +442,6 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
showTimeoutError(context);
|
||||
} else if (error is SocketException) {
|
||||
showServerError(
|
||||
context,
|
||||
I18N.of(context).connectionRefused,
|
||||
error.toString()
|
||||
);
|
||||
@ -458,7 +457,7 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
if (response == null) return false;
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
showStatusCodeError(context, response.statusCode);
|
||||
showStatusCodeError(response.statusCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
2
lib/l10n
2
lib/l10n
@ -1 +1 @@
|
||||
Subproject commit dd7073d7434d5359ebbf9febff4e319ca0ec2ce0
|
||||
Subproject commit f1e991199f3656bfc774f3793ad7ea1609857027
|
@ -283,7 +283,7 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
||||
// Navigator.of(context, rootNavigator: true).pop();
|
||||
confirmationDialog(
|
||||
I18N.of(context).delete,
|
||||
"Delete this profile?",
|
||||
I18N.of(context).profileDelete + "?",
|
||||
onAccept: () {
|
||||
_deleteProfile(profile);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
|
||||
import 'package:InvenTree/widget/snacks.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
@ -86,61 +87,64 @@ Future<void> showInfoDialog(BuildContext context, String title, String descripti
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> showErrorDialog(BuildContext context, String title, String description, {IconData icon = FontAwesomeIcons.exclamationCircle, String error, Function onDismissed}) async {
|
||||
Future<void> showErrorDialog(String title, String description, {IconData icon = FontAwesomeIcons.exclamationCircle, String error, Function onDismissed}) async {
|
||||
|
||||
if (error == null || error.isEmpty) {
|
||||
error = I18N.of(context).error;
|
||||
error = I18N.of(OneContext().context).error;
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return SimpleDialog(
|
||||
title: ListTile(
|
||||
title: Text(error),
|
||||
leading: FaIcon(icon),
|
||||
),
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
title: Text(title),
|
||||
subtitle: Text(description)
|
||||
)
|
||||
]
|
||||
);
|
||||
}).then((value) {
|
||||
if (onDismissed != null) {
|
||||
onDismissed();
|
||||
}
|
||||
});
|
||||
OneContext().showDialog(
|
||||
builder: (context) => SimpleDialog(
|
||||
title: ListTile(
|
||||
title: Text(error),
|
||||
leading: FaIcon(icon),
|
||||
),
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(title),
|
||||
subtitle: Text(description),
|
||||
)
|
||||
],
|
||||
)
|
||||
).then((value) {
|
||||
if (onDismissed != null) {
|
||||
onDismissed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> showServerError(BuildContext context, String title, String description) async {
|
||||
Future<void> showServerError(String title, String description) async {
|
||||
|
||||
if (title == null || title.isEmpty) {
|
||||
title = I18N.of(context).serverError;
|
||||
title = I18N.of(OneContext().context).serverError;
|
||||
}
|
||||
|
||||
await showErrorDialog(
|
||||
context,
|
||||
title,
|
||||
description,
|
||||
error: I18N.of(context).serverError,
|
||||
icon: FontAwesomeIcons.server
|
||||
showSnackIcon(
|
||||
title,
|
||||
success: false,
|
||||
actionText: I18N.of(OneContext().context).details,
|
||||
onAction: () {
|
||||
showErrorDialog(
|
||||
title,
|
||||
description,
|
||||
error: I18N.of(OneContext().context).serverError,
|
||||
icon: FontAwesomeIcons.server
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showStatusCodeError(BuildContext context, int status, {int expected = 200}) async {
|
||||
Future<void> showStatusCodeError(int status, {int expected = 200}) async {
|
||||
|
||||
await showServerError(
|
||||
context,
|
||||
"Invalid Response Code",
|
||||
showServerError(
|
||||
I18N.of(OneContext().context).responseInvalid,
|
||||
"Server responded with status code ${status}"
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showTimeoutError(BuildContext context) async {
|
||||
|
||||
await showServerError(context, I18N.of(context).timeout, I18N.of(context).noResponse);
|
||||
await showServerError(I18N.of(context).timeout, I18N.of(context).noResponse);
|
||||
}
|
||||
|
||||
void showProgressDialog(BuildContext context, String title, String description) {
|
||||
|
@ -13,7 +13,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:one_context/one_context.dart';
|
||||
|
||||
void showSnackIcon(String text, {IconData icon, Function onTap, bool success}) {
|
||||
void showSnackIcon(String text, {IconData icon, Function onAction, bool success, String actionText}) {
|
||||
|
||||
OneContext().hideCurrentSnackBar();
|
||||
|
||||
@ -23,32 +23,35 @@ void showSnackIcon(String text, {IconData icon, Function onTap, bool success}) {
|
||||
if (success == true) {
|
||||
backgroundColor = Colors.lightGreen;
|
||||
|
||||
// Unspecified icon?
|
||||
if (icon == null) {
|
||||
icon = FontAwesomeIcons.checkCircle;
|
||||
}
|
||||
|
||||
} else if (success == false) {
|
||||
backgroundColor = Colors.deepOrange;
|
||||
}
|
||||
|
||||
if (icon == null) {
|
||||
icon = FontAwesomeIcons.timesCircle;
|
||||
}
|
||||
SnackBarAction action;
|
||||
|
||||
if (onAction != null && actionText != null) {
|
||||
action = SnackBarAction(
|
||||
label: actionText,
|
||||
onPressed: onAction,
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> childs = [
|
||||
Text(text),
|
||||
Spacer(),
|
||||
];
|
||||
|
||||
if (icon != null) {
|
||||
childs.add(FaIcon(icon));
|
||||
}
|
||||
|
||||
OneContext().showSnackBar(builder: (context) => SnackBar(
|
||||
content: GestureDetector(
|
||||
child: Row(
|
||||
children: [
|
||||
Text(text),
|
||||
Spacer(),
|
||||
FaIcon(icon)
|
||||
],
|
||||
),
|
||||
onTap: onTap,
|
||||
content: Row(
|
||||
children: childs
|
||||
),
|
||||
backgroundColor: backgroundColor,
|
||||
));
|
||||
action: action
|
||||
)
|
||||
);
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user