From 00943b75367af2d36e72d822eb71277f77807153 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 17 Feb 2021 08:00:41 +1100 Subject: [PATCH] API error messages now use snackIcon - Press "details" for further error information - Is nice --- assets/release_notes.md | 8 +++++ lib/api.dart | 43 ++++++++++++----------- lib/barcode.dart | 28 ++++----------- lib/inventree/model.dart | 16 ++++----- lib/inventree/stock.dart | 3 +- lib/l10n | 2 +- lib/settings/login.dart | 2 +- lib/widget/dialogs.dart | 74 +++++++++++++++++++++------------------- lib/widget/snacks.dart | 41 +++++++++++----------- 9 files changed, 108 insertions(+), 109 deletions(-) diff --git a/assets/release_notes.md b/assets/release_notes.md index e02d1010..d1f374f6 100644 --- a/assets/release_notes.md +++ b/assets/release_notes.md @@ -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. diff --git a/lib/api.dart b/lib/api.dart index 1730a444..143d61c4 100644 --- a/lib/api.dart +++ b/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; } diff --git a/lib/barcode.dart b/lib/barcode.dart index 924c0e6f..ff2518db 100644 --- a/lib/barcode.dart +++ b/lib/barcode.dart @@ -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 onBarcodeUnhandled(Map 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 onBarcodeMatched(Map 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: () { diff --git a/lib/inventree/model.dart b/lib/inventree/model.dart index 16633f59..95cfd47a 100644 --- a/lib/inventree/model.dart +++ b/lib/inventree/model.dart @@ -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 results = new List(); if (response.statusCode != 200) { - showStatusCodeError(context, response.statusCode); + showStatusCodeError(response.statusCode); // Return empty list return results; diff --git a/lib/inventree/stock.dart b/lib/inventree/stock.dart index fea031f5..b39e6a04 100644 --- a/lib/inventree/stock.dart +++ b/lib/inventree/stock.dart @@ -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; } diff --git a/lib/l10n b/lib/l10n index dd7073d7..f1e99119 160000 --- a/lib/l10n +++ b/lib/l10n @@ -1 +1 @@ -Subproject commit dd7073d7434d5359ebbf9febff4e319ca0ec2ce0 +Subproject commit f1e991199f3656bfc774f3793ad7ea1609857027 diff --git a/lib/settings/login.dart b/lib/settings/login.dart index 56a058e9..13fb8585 100644 --- a/lib/settings/login.dart +++ b/lib/settings/login.dart @@ -283,7 +283,7 @@ class _InvenTreeLoginSettingsState extends State { // Navigator.of(context, rootNavigator: true).pop(); confirmationDialog( I18N.of(context).delete, - "Delete this profile?", + I18N.of(context).profileDelete + "?", onAccept: () { _deleteProfile(profile); } diff --git a/lib/widget/dialogs.dart b/lib/widget/dialogs.dart index 1e467ec5..939533ee 100644 --- a/lib/widget/dialogs.dart +++ b/lib/widget/dialogs.dart @@ -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 showInfoDialog(BuildContext context, String title, String descripti }); } -Future showErrorDialog(BuildContext context, String title, String description, {IconData icon = FontAwesomeIcons.exclamationCircle, String error, Function onDismissed}) async { +Future 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: [ - 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 showServerError(BuildContext context, String title, String description) async { +Future 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 showStatusCodeError(BuildContext context, int status, {int expected = 200}) async { +Future 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 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) { diff --git a/lib/widget/snacks.dart b/lib/widget/snacks.dart index 753ac12e..f02045d4 100644 --- a/lib/widget/snacks.dart +++ b/lib/widget/snacks.dart @@ -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 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 + ) + ); } \ No newline at end of file