mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-28 13:36:50 +00:00
Cleanup for API error handling
Ref: https://stackoverflow.com/questions/54617432/looking-up-a-deactivated-widgets-ancestor-is-unsafe
This commit is contained in:
parent
90072904a0
commit
33bb6148de
75
lib/api.dart
75
lib/api.dart
@ -68,15 +68,6 @@ class InvenTreeAPI {
|
|||||||
static const _URL_GET_TOKEN = "user/token/";
|
static const _URL_GET_TOKEN = "user/token/";
|
||||||
static const _URL_GET_VERSION = "";
|
static const _URL_GET_VERSION = "";
|
||||||
|
|
||||||
Future<void> showServerError(BuildContext context, String description) async {
|
|
||||||
showErrorDialog(
|
|
||||||
context,
|
|
||||||
I18N.of(context).serverError,
|
|
||||||
description,
|
|
||||||
icon: FontAwesomeIcons.server
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base URL for InvenTree API e.g. http://192.168.120.10:8000
|
// Base URL for InvenTree API e.g. http://192.168.120.10:8000
|
||||||
String _BASE_URL = "";
|
String _BASE_URL = "";
|
||||||
|
|
||||||
@ -209,18 +200,18 @@ class InvenTreeAPI {
|
|||||||
|
|
||||||
print("Connecting to ${apiUrl} -> ${username}:${password}");
|
print("Connecting to ${apiUrl} -> ${username}:${password}");
|
||||||
|
|
||||||
var response = await get("").timeout(Duration(seconds: 5)).catchError((error) {
|
var response = await get("").timeout(Duration(seconds: 10)).catchError((error) {
|
||||||
|
|
||||||
print("Error connecting to server: ${error.toString()}");
|
print("Error connecting to server: ${error.toString()}");
|
||||||
|
|
||||||
if (error is SocketException) {
|
if (error is SocketException) {
|
||||||
print("Error: socket exception: ${error.toString()}");
|
showServerError(
|
||||||
// TODO - Display error dialog!!
|
context,
|
||||||
//showServerError(context, "Connection Refused");
|
I18N.of(context).connectionRefused,
|
||||||
|
error.toString());
|
||||||
return null;
|
return null;
|
||||||
} else if (error is TimeoutException) {
|
} else if (error is TimeoutException) {
|
||||||
// TODO - Display timeout dialog here
|
showTimeoutError(context);
|
||||||
//showTimeoutDialog(context);
|
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
// Unknown error type - re-throw the error and Sentry will catch it
|
// Unknown error type - re-throw the error and Sentry will catch it
|
||||||
@ -236,8 +227,9 @@ class InvenTreeAPI {
|
|||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
// Any status code other than 200!
|
// Any status code other than 200!
|
||||||
|
|
||||||
|
showStatusCodeError(context, response.statusCode);
|
||||||
|
|
||||||
// TODO: Interpret the error codes and show custom message?
|
// TODO: Interpret the error codes and show custom message?
|
||||||
await showServerError(context, "Invalid response code: ${response.statusCode.toString()}");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,29 +238,30 @@ class InvenTreeAPI {
|
|||||||
print("Response from server: $data");
|
print("Response from server: $data");
|
||||||
|
|
||||||
// We expect certain response from the server
|
// We expect certain response from the server
|
||||||
if (!data.containsKey("server") || !data.containsKey("version")) {
|
if (!data.containsKey("server") || !data.containsKey("version") || !data.containsKey("instance")) {
|
||||||
|
|
||||||
|
showServerError(
|
||||||
|
context,
|
||||||
|
"Missing Data",
|
||||||
|
"Server response missing required fields"
|
||||||
|
);
|
||||||
|
|
||||||
await showServerError(context, "Server response missing required fields");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
print("Server: " + data["server"]);
|
// Record server information
|
||||||
print("Version: " + data["version"]);
|
|
||||||
|
|
||||||
_version = data["version"];
|
_version = data["version"];
|
||||||
|
|
||||||
if (!_checkServerVersion(_version)) {
|
|
||||||
await showServerError(context, "Server version is too old.\n\nServer Version: ${_version}\n\nRequired version: ${_requiredVersionString}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record the instance name of the server
|
|
||||||
instance = data['instance'] ?? '';
|
instance = data['instance'] ?? '';
|
||||||
|
|
||||||
// Request token from the server if we do not already have one
|
// Check that the remote server version is *new* enough
|
||||||
if (false && _token.isNotEmpty) {
|
if (!_checkServerVersion(_version)) {
|
||||||
print("Already have token - $_token");
|
showServerError(
|
||||||
return true;
|
context,
|
||||||
|
"Old Server Version",
|
||||||
|
"\n\nServer Version: ${_version}\n\nRequired version: ${_requiredVersionString}"
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the existing token value
|
// Clear the existing token value
|
||||||
@ -277,24 +270,33 @@ class InvenTreeAPI {
|
|||||||
print("Requesting token from server");
|
print("Requesting token from server");
|
||||||
|
|
||||||
response = await get(_URL_GET_TOKEN).timeout(Duration(seconds: 10)).catchError((error) {
|
response = await get(_URL_GET_TOKEN).timeout(Duration(seconds: 10)).catchError((error) {
|
||||||
|
|
||||||
print("Error requesting token:");
|
print("Error requesting token:");
|
||||||
print(error);
|
print(error);
|
||||||
return null;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
await showServerError(context, "Error requesting access token");
|
showServerError(
|
||||||
|
context, "Token Error", "Error requesting access token from server"
|
||||||
|
);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
await showServerError(context, "Invalid status code: ${response.statusCode.toString()}");
|
showStatusCodeError(context, response.statusCode);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
var data = json.decode(response.body);
|
var data = json.decode(response.body);
|
||||||
|
|
||||||
if (!data.containsKey("token")) {
|
if (!data.containsKey("token")) {
|
||||||
await showServerError(context, "No token provided in response");
|
showServerError(
|
||||||
|
context,
|
||||||
|
"Missing Token",
|
||||||
|
"Access token missing from response"
|
||||||
|
);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,6 +306,7 @@ class InvenTreeAPI {
|
|||||||
|
|
||||||
_connected = true;
|
_connected = true;
|
||||||
|
|
||||||
|
// Ok, probably pretty good...
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -66,14 +66,11 @@ class BarcodeHandler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> processBarcode(BuildContext context, QRViewController _controller, String barcode, {String url = "barcode/", bool show_dialog = false}) {
|
Future<void> processBarcode(BuildContext context, QRViewController _controller, String barcode, {String url = "barcode/"}) {
|
||||||
this._context = context;
|
this._context = context;
|
||||||
this._controller = _controller;
|
this._controller = _controller;
|
||||||
|
|
||||||
print("Scanned barcode data: ${barcode}");
|
print("Scanned barcode data: ${barcode}");
|
||||||
if (show_dialog) {
|
|
||||||
showProgressDialog(context, "Scanning", "Sending barcode data to server");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send barcode request to server
|
// Send barcode request to server
|
||||||
InvenTreeAPI().post(
|
InvenTreeAPI().post(
|
||||||
@ -83,10 +80,6 @@ class BarcodeHandler {
|
|||||||
}
|
}
|
||||||
).then((var response) {
|
).then((var response) {
|
||||||
|
|
||||||
if (show_dialog) {
|
|
||||||
hideProgressDialog(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
showErrorDialog(
|
showErrorDialog(
|
||||||
context,
|
context,
|
||||||
@ -119,10 +112,6 @@ class BarcodeHandler {
|
|||||||
Duration(seconds: 5)
|
Duration(seconds: 5)
|
||||||
).catchError((error) {
|
).catchError((error) {
|
||||||
|
|
||||||
if (show_dialog) {
|
|
||||||
hideProgressDialog(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
showErrorDialog(
|
showErrorDialog(
|
||||||
context,
|
context,
|
||||||
"Error",
|
"Error",
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:InvenTree/api.dart';
|
import 'package:InvenTree/api.dart';
|
||||||
import 'package:InvenTree/widget/dialogs.dart';
|
import 'package:InvenTree/widget/dialogs.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
@ -108,22 +108,21 @@ class InvenTreeModel {
|
|||||||
/*
|
/*
|
||||||
* Reload this object, by requesting data from the server
|
* Reload this object, by requesting data from the server
|
||||||
*/
|
*/
|
||||||
Future<bool> reload(BuildContext context, {bool dialog = false}) async {
|
Future<bool> reload(BuildContext context) async {
|
||||||
|
|
||||||
if (dialog) {
|
|
||||||
showProgressDialog(context, I18N.of(context).refreshing, "Refreshing data for ${NAME}");
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await api.get(url, params: defaultGetFilters())
|
var response = await api.get(url, params: defaultGetFilters())
|
||||||
.timeout(Duration(seconds: 10))
|
.timeout(Duration(seconds: 10))
|
||||||
.catchError((e) {
|
.catchError((e) {
|
||||||
|
|
||||||
if (dialog) {
|
if (e is SocketException) {
|
||||||
hideProgressDialog(context);
|
showServerError(
|
||||||
|
context,
|
||||||
|
I18N.of(context).connectionRefused,
|
||||||
|
e.toString()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
else if (e is TimeoutException) {
|
||||||
if (e is TimeoutException) {
|
showTimeoutError(context);
|
||||||
showTimeoutDialog(context);
|
|
||||||
} else {
|
} else {
|
||||||
// Re-throw the error (Sentry will catch)
|
// Re-throw the error (Sentry will catch)
|
||||||
throw e;
|
throw e;
|
||||||
@ -136,17 +135,8 @@ class InvenTreeModel {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dialog) {
|
|
||||||
hideProgressDialog(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
showErrorDialog(
|
showStatusCodeError(context, response.statusCode);
|
||||||
context,
|
|
||||||
I18N.of(context).serverError,
|
|
||||||
"${I18N.of(context).statusCode}: ${response.statusCode}"
|
|
||||||
);
|
|
||||||
|
|
||||||
print("Error retrieving data");
|
print("Error retrieving data");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -159,7 +149,7 @@ class InvenTreeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// POST data to update the model
|
// POST data to update the model
|
||||||
Future<bool> update(BuildContext context, {Map<String, String> values, bool show_dialog = false}) async {
|
Future<bool> update(BuildContext context, {Map<String, String> values}) async {
|
||||||
|
|
||||||
var addr = path.join(URL, pk.toString());
|
var addr = path.join(URL, pk.toString());
|
||||||
|
|
||||||
@ -167,20 +157,18 @@ class InvenTreeModel {
|
|||||||
addr += "/";
|
addr += "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (show_dialog) {
|
|
||||||
showProgressDialog(context, "Updating ${NAME}", "Sending data to server");
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await api.patch(addr, body: values)
|
var response = await api.patch(addr, body: values)
|
||||||
.timeout(Duration(seconds: 10))
|
.timeout(Duration(seconds: 10))
|
||||||
.catchError((e) {
|
.catchError((e) {
|
||||||
|
|
||||||
if (show_dialog) {
|
if (e is SocketException) {
|
||||||
hideProgressDialog(context);
|
showServerError(
|
||||||
}
|
context,
|
||||||
|
I18N.of(context).connectionRefused,
|
||||||
if (e is TimeoutException) {
|
e.toString()
|
||||||
showTimeoutDialog(context);
|
);
|
||||||
|
} else if (e is TimeoutException) {
|
||||||
|
showTimeoutError(context);
|
||||||
} else {
|
} else {
|
||||||
// Re-throw the error, let Sentry report it
|
// Re-throw the error, let Sentry report it
|
||||||
throw e;
|
throw e;
|
||||||
@ -191,12 +179,8 @@ class InvenTreeModel {
|
|||||||
|
|
||||||
if (response == null) return false;
|
if (response == null) return false;
|
||||||
|
|
||||||
if (show_dialog) {
|
|
||||||
hideProgressDialog(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
showErrorDialog(context, I18N.of(context).serverError, "${I18N.of(context).statusCode}: ${response.statusCode}");
|
showStatusCodeError(context, response.statusCode);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,7 +189,7 @@ class InvenTreeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return the detail view for the associated pk
|
// Return the detail view for the associated pk
|
||||||
Future<InvenTreeModel> get(BuildContext context, int pk, {Map<String, String> filters, bool dialog = false}) async {
|
Future<InvenTreeModel> get(BuildContext context, int pk, {Map<String, String> filters}) async {
|
||||||
|
|
||||||
// TODO - Add "timeout"
|
// TODO - Add "timeout"
|
||||||
// TODO - Add error catching
|
// TODO - Add error catching
|
||||||
@ -227,20 +211,15 @@ class InvenTreeModel {
|
|||||||
|
|
||||||
print("GET: $addr ${params.toString()}");
|
print("GET: $addr ${params.toString()}");
|
||||||
|
|
||||||
if (dialog) {
|
|
||||||
showProgressDialog(context, I18N.of(context).requestingData, "Requesting ${NAME} data from server");
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await api.get(addr, params: params)
|
var response = await api.get(addr, params: params)
|
||||||
.timeout(Duration(seconds: 10))
|
.timeout(Duration(seconds: 10))
|
||||||
.catchError((e) {
|
.catchError((e) {
|
||||||
|
|
||||||
if (dialog) {
|
if (e is SocketException) {
|
||||||
hideProgressDialog(context);
|
showServerError(context, I18N.of(context).connectionRefused, e.toString());
|
||||||
}
|
}
|
||||||
|
else if (e is TimeoutException) {
|
||||||
if (e is TimeoutException) {
|
showTimeoutError(context);
|
||||||
showTimeoutDialog(context);
|
|
||||||
} else {
|
} else {
|
||||||
// Re-throw the error (handled by Sentry)
|
// Re-throw the error (handled by Sentry)
|
||||||
throw e;
|
throw e;
|
||||||
@ -252,12 +231,8 @@ class InvenTreeModel {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dialog) {
|
|
||||||
hideProgressDialog(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
showErrorDialog(context, I18N.of(context).serverError, "${I18N.of(context).statusCode}: ${response.statusCode}");
|
showStatusCodeError(context, response.statusCode);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,15 +255,24 @@ class InvenTreeModel {
|
|||||||
|
|
||||||
InvenTreeModel _model;
|
InvenTreeModel _model;
|
||||||
|
|
||||||
await api.post(URL, body: data)
|
await api.post(URL, body: data).timeout(Duration(seconds: 10)).catchError((e) {
|
||||||
.timeout(Duration(seconds: 5))
|
print("Error during CREATE");
|
||||||
.catchError((e) {
|
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
showErrorDialog(
|
|
||||||
context,
|
if (e is SocketException) {
|
||||||
I18N.of(context).serverError,
|
showServerError(
|
||||||
e.toString()
|
context,
|
||||||
);
|
I18N.of(context).connectionRefused,
|
||||||
|
e.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (e is TimeoutException) {
|
||||||
|
showTimeoutError(context);
|
||||||
|
} else {
|
||||||
|
// Re-throw the error (Sentry will catch)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
.then((http.Response response) {
|
.then((http.Response response) {
|
||||||
@ -297,11 +281,7 @@ class InvenTreeModel {
|
|||||||
var decoded = json.decode(response.body);
|
var decoded = json.decode(response.body);
|
||||||
_model = createFromJson(decoded);
|
_model = createFromJson(decoded);
|
||||||
} else {
|
} else {
|
||||||
showErrorDialog(
|
showStatusCodeError(context, response.statusCode);
|
||||||
context,
|
|
||||||
I18N.of(context).serverError,
|
|
||||||
"${I18N.of(context).statusCode}: ${response.statusCode}"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -309,7 +289,7 @@ class InvenTreeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return list of objects from the database, with optional filters
|
// Return list of objects from the database, with optional filters
|
||||||
Future<List<InvenTreeModel>> list(BuildContext context, {Map<String, String> filters, bool dialog=false}) async {
|
Future<List<InvenTreeModel>> list(BuildContext context, {Map<String, String> filters}) async {
|
||||||
|
|
||||||
if (filters == null) {
|
if (filters == null) {
|
||||||
filters = {};
|
filters = {};
|
||||||
@ -328,20 +308,19 @@ class InvenTreeModel {
|
|||||||
// TODO - Add "timeout"
|
// TODO - Add "timeout"
|
||||||
// TODO - Add error catching
|
// TODO - Add error catching
|
||||||
|
|
||||||
if (dialog) {
|
|
||||||
showProgressDialog(context, I18N.of(context).requestingData, "Requesting ${NAME} data from server");
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await api.get(URL, params:params)
|
var response = await api.get(URL, params:params)
|
||||||
.timeout(Duration(seconds: 10))
|
.timeout(Duration(seconds: 10))
|
||||||
.catchError((e) {
|
.catchError((e) {
|
||||||
|
|
||||||
if (dialog) {
|
if (e is SocketException) {
|
||||||
hideProgressDialog(context);
|
showServerError(
|
||||||
|
context,
|
||||||
|
I18N.of(context).connectionRefused,
|
||||||
|
e.toString()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
else if (e is TimeoutException) {
|
||||||
if (e is TimeoutException) {
|
showTimeoutError(context);
|
||||||
showTimeoutDialog(context);
|
|
||||||
} else {
|
} else {
|
||||||
// Re-throw the error
|
// Re-throw the error
|
||||||
throw e;
|
throw e;
|
||||||
@ -354,15 +333,13 @@ class InvenTreeModel {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dialog) {
|
|
||||||
hideProgressDialog(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A list of "InvenTreeModel" items
|
// A list of "InvenTreeModel" items
|
||||||
List<InvenTreeModel> results = new List<InvenTreeModel>();
|
List<InvenTreeModel> results = new List<InvenTreeModel>();
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
print("Error retreiving data");
|
showStatusCodeError(context, response.statusCode);
|
||||||
|
|
||||||
|
// Return empty list
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,8 +397,6 @@ class InvenTreeModel {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -161,7 +161,6 @@ class InvenTreePart extends InvenTreeModel {
|
|||||||
"part": "${pk}",
|
"part": "${pk}",
|
||||||
"in_stock": "true",
|
"in_stock": "true",
|
||||||
},
|
},
|
||||||
dialog: showDialog,
|
|
||||||
).then((var items) {
|
).then((var items) {
|
||||||
stockItems.clear();
|
stockItems.clear();
|
||||||
|
|
||||||
@ -186,7 +185,6 @@ class InvenTreePart extends InvenTreeModel {
|
|||||||
filters: {
|
filters: {
|
||||||
"part": "${pk}",
|
"part": "${pk}",
|
||||||
},
|
},
|
||||||
dialog: showDialog,
|
|
||||||
).then((var templates) {
|
).then((var templates) {
|
||||||
|
|
||||||
testingTemplates.clear();
|
testingTemplates.clear();
|
||||||
@ -205,6 +203,15 @@ class InvenTreePart extends InvenTreeModel {
|
|||||||
// Get the stock count for this Part
|
// Get the stock count for this Part
|
||||||
double get inStock => double.tryParse(jsondata['in_stock'].toString() ?? '0');
|
double get inStock => double.tryParse(jsondata['in_stock'].toString() ?? '0');
|
||||||
|
|
||||||
|
String get inStockString {
|
||||||
|
|
||||||
|
if (inStock == inStock.toInt()) {
|
||||||
|
return inStock.toInt().toString();
|
||||||
|
} else {
|
||||||
|
return inStock.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get the number of units being build for this Part
|
// Get the number of units being build for this Part
|
||||||
double get building => double.tryParse(jsondata['building'].toString() ?? '0');
|
double get building => double.tryParse(jsondata['building'].toString() ?? '0');
|
||||||
|
|
||||||
|
@ -97,7 +97,6 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
filters: {
|
filters: {
|
||||||
"part": "${partId}",
|
"part": "${partId}",
|
||||||
},
|
},
|
||||||
dialog: showDialog,
|
|
||||||
).then((var templates) {
|
).then((var templates) {
|
||||||
testTemplates.clear();
|
testTemplates.clear();
|
||||||
|
|
||||||
@ -113,7 +112,7 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
|
|
||||||
int get testResultCount => testResults.length;
|
int get testResultCount => testResults.length;
|
||||||
|
|
||||||
Future<void> getTestResults(BuildContext context, {bool showDialog=false}) async {
|
Future<void> getTestResults(BuildContext context) async {
|
||||||
|
|
||||||
await InvenTreeStockItemTestResult().list(
|
await InvenTreeStockItemTestResult().list(
|
||||||
context,
|
context,
|
||||||
@ -121,7 +120,6 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
"stock_item": "${pk}",
|
"stock_item": "${pk}",
|
||||||
"user_detail": "true",
|
"user_detail": "true",
|
||||||
},
|
},
|
||||||
dialog: showDialog,
|
|
||||||
).then((var results) {
|
).then((var results) {
|
||||||
testResults.clear();
|
testResults.clear();
|
||||||
|
|
||||||
|
2
lib/l10n
2
lib/l10n
@ -1 +1 @@
|
|||||||
Subproject commit 79b2c87e9611abbae7a7251ac68cbfed475f7699
|
Subproject commit 90f3bbf1fae86efd0bb0686bef12452a09507669
|
@ -19,6 +19,8 @@ class InvenTreeLoginSettingsWidget extends StatefulWidget {
|
|||||||
|
|
||||||
class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
||||||
|
|
||||||
|
final GlobalKey<_InvenTreeLoginSettingsState> _loginKey = GlobalKey<_InvenTreeLoginSettingsState>();
|
||||||
|
|
||||||
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
|
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
|
||||||
|
|
||||||
final GlobalKey<FormState> _addProfileKey = new GlobalKey<FormState>();
|
final GlobalKey<FormState> _addProfileKey = new GlobalKey<FormState>();
|
||||||
@ -176,7 +178,7 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
|||||||
_reload();
|
_reload();
|
||||||
|
|
||||||
// Attempt server login (this will load the newly selected profile
|
// Attempt server login (this will load the newly selected profile
|
||||||
InvenTreeAPI().connectToServer(context).then((result) {
|
InvenTreeAPI().connectToServer(_loginKey.currentContext).then((result) {
|
||||||
_reload();
|
_reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -209,7 +211,7 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
|||||||
if (InvenTreeAPI().isConnected() && profile.key == InvenTreeAPI().profile.key) {
|
if (InvenTreeAPI().isConnected() && profile.key == InvenTreeAPI().profile.key) {
|
||||||
// Attempt server login (this will load the newly selected profile
|
// Attempt server login (this will load the newly selected profile
|
||||||
|
|
||||||
InvenTreeAPI().connectToServer(context).then((result) {
|
InvenTreeAPI().connectToServer(_loginKey.currentContext).then((result) {
|
||||||
_reload();
|
_reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -262,8 +264,6 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
|||||||
|
|
||||||
final Size screenSize = MediaQuery.of(context).size;
|
final Size screenSize = MediaQuery.of(context).size;
|
||||||
|
|
||||||
print("Building!");
|
|
||||||
|
|
||||||
List<Widget> children = [];
|
List<Widget> children = [];
|
||||||
|
|
||||||
if (profiles != null && profiles.length > 0) {
|
if (profiles != null && profiles.length > 0) {
|
||||||
@ -333,6 +333,7 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
key: _loginKey,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(I18N.of(context).profileSelect),
|
title: Text(I18N.of(context).profileSelect),
|
||||||
),
|
),
|
||||||
|
@ -268,7 +268,7 @@ class PartList extends StatelessWidget {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text("${part.name}"),
|
title: Text("${part.name}"),
|
||||||
subtitle: Text("${part.description}"),
|
subtitle: Text("${part.description}"),
|
||||||
trailing: Text("${part.inStock}"),
|
trailing: Text("${part.inStockString}"),
|
||||||
leading: InvenTreeAPI().getImage(
|
leading: InvenTreeAPI().getImage(
|
||||||
part.thumbnail,
|
part.thumbnail,
|
||||||
width: 40,
|
width: 40,
|
||||||
|
@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
Future<void> confirmationDialog(BuildContext context, String title, String text, {String acceptText, String rejectText, Function onAccept, Function onReject}) async {
|
Future<void> confirmationDialog(BuildContext context, String title, String text, {String acceptText, String rejectText, Function onAccept, Function onReject}) async {
|
||||||
@ -92,37 +93,55 @@ Future<void> showErrorDialog(BuildContext context, String title, String descript
|
|||||||
|
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
child: SimpleDialog(
|
builder: (dialogContext) {
|
||||||
title: ListTile(
|
return SimpleDialog(
|
||||||
title: Text(error),
|
title: ListTile(
|
||||||
leading: FaIcon(icon),
|
title: Text(error),
|
||||||
),
|
leading: FaIcon(icon),
|
||||||
children: <Widget>[
|
),
|
||||||
ListTile(
|
children: <Widget>[
|
||||||
title: Text(title),
|
ListTile(
|
||||||
subtitle: Text(description)
|
title: Text(title),
|
||||||
)
|
subtitle: Text(description)
|
||||||
]
|
)
|
||||||
)
|
]
|
||||||
).then((value) {
|
);
|
||||||
if (onDismissed != null) {
|
}).then((value) {
|
||||||
onDismissed();
|
if (onDismissed != null) {
|
||||||
}
|
onDismissed();
|
||||||
});
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void showTimeoutDialog(BuildContext context) {
|
Future<void> showServerError(BuildContext context, String title, String description) async {
|
||||||
/*
|
|
||||||
Show a server timeout dialog
|
|
||||||
*/
|
|
||||||
|
|
||||||
showErrorDialog(
|
if (title == null || title.isEmpty) {
|
||||||
|
title = I18N.of(context).serverError;
|
||||||
|
}
|
||||||
|
|
||||||
|
await showErrorDialog(
|
||||||
context,
|
context,
|
||||||
I18N.of(context).timeout,
|
title,
|
||||||
I18N.of(context).noResponse
|
description,
|
||||||
|
error: I18N.of(context).serverError,
|
||||||
|
icon: FontAwesomeIcons.server
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> showStatusCodeError(BuildContext context, int status, {int expected = 200}) async {
|
||||||
|
|
||||||
|
await showServerError(
|
||||||
|
context,
|
||||||
|
"Invalid Response Code",
|
||||||
|
"Server responded with status code ${status}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> showTimeoutError(BuildContext context) async {
|
||||||
|
|
||||||
|
await showServerError(context, I18N.of(context).timeout, I18N.of(context).noResponse);
|
||||||
|
}
|
||||||
|
|
||||||
void showProgressDialog(BuildContext context, String title, String description) {
|
void showProgressDialog(BuildContext context, String title, String description) {
|
||||||
|
|
||||||
showDialog(
|
showDialog(
|
||||||
|
@ -27,6 +27,8 @@ class InvenTreeHomePage extends StatefulWidget {
|
|||||||
|
|
||||||
class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
||||||
|
|
||||||
|
final GlobalKey<_InvenTreeHomePageState> _homeKey = GlobalKey<_InvenTreeHomePageState>();
|
||||||
|
|
||||||
_InvenTreeHomePageState() : super() {
|
_InvenTreeHomePageState() : super() {
|
||||||
|
|
||||||
// Initially load the profile and attempt server connection
|
// Initially load the profile and attempt server connection
|
||||||
@ -115,7 +117,7 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
|||||||
if (!InvenTreeAPI().isConnected() && !InvenTreeAPI().isConnecting()) {
|
if (!InvenTreeAPI().isConnected() && !InvenTreeAPI().isConnecting()) {
|
||||||
|
|
||||||
// Attempt server connection
|
// Attempt server connection
|
||||||
InvenTreeAPI().connectToServer(_context).then((result) {
|
InvenTreeAPI().connectToServer(_homeKey.currentContext).then((result) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -198,6 +200,7 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
|||||||
// fast, so that you can just rebuild anything that needs updating rather
|
// fast, so that you can just rebuild anything that needs updating rather
|
||||||
// than having to individually change instances of widgets.
|
// than having to individually change instances of widgets.
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
key: _homeKey,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(I18N.of(context).appTitle),
|
title: Text(I18N.of(context).appTitle),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
|
@ -217,7 +217,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
title: Text(I18N.of(context).stock),
|
title: Text(I18N.of(context).stock),
|
||||||
leading: FaIcon(FontAwesomeIcons.boxes),
|
leading: FaIcon(FontAwesomeIcons.boxes),
|
||||||
trailing: Text("${part.inStock}"),
|
trailing: Text("${part.inStockString}"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_showStock(context);
|
_showStock(context);
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user