mirror of
				https://github.com/inventree/inventree-app.git
				synced 2025-10-31 13:25:40 +00:00 
			
		
		
		
	Update Requirements (#541)
* Update package requiremenst * github workflow updates * ios build updates * Theme adjustments * Further updates * Fix typo * Deprecated imperative apply of Flutter's Gradle plugins Ref: https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply * Refactor wedge scanner * Add context checks * Adjust behaviour if testing * Further refactoring * Moar checks * Logic fix * Fix for wedge scanner test * Fix for barcode processing * Fix * Yet another fix
This commit is contained in:
		| @@ -1393,7 +1393,7 @@ class InvenTreeAPI { | ||||
|   // Find the current locale code for the running app | ||||
|   String get currentLocale { | ||||
|  | ||||
|     if (OneContext.hasContext) { | ||||
|     if (hasContext()) { | ||||
|       // Try to get app context | ||||
|       BuildContext? context = OneContext().context; | ||||
|  | ||||
|   | ||||
| @@ -1,22 +1,29 @@ | ||||
| import "package:adaptive_theme/adaptive_theme.dart"; | ||||
| import "package:flutter/material.dart"; | ||||
| import "package:inventree/helpers.dart"; | ||||
| import "package:one_context/one_context.dart"; | ||||
|  | ||||
| const Color COLOR_GRAY_LIGHT = Color.fromRGBO(150, 150, 150, 1); | ||||
| bool isDarkMode() { | ||||
|  | ||||
| // Return an "action" color based on the current theme | ||||
| Color get COLOR_ACTION { | ||||
|  | ||||
|   // OneContext might not have context, e.g. in testing | ||||
|   if (!OneContext.hasContext) { | ||||
|     return Colors.lightBlue; | ||||
|   if (!hasContext()) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   BuildContext? context = OneContext().context; | ||||
|  | ||||
|   if (context != null) { | ||||
|     return Theme.of(context).indicatorColor; | ||||
|   if (context == null) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   return AdaptiveTheme.of(context).brightness == Brightness.dark; | ||||
| } | ||||
|  | ||||
| // Return an "action" color based on the current theme | ||||
| Color get COLOR_ACTION { | ||||
|   if (isDarkMode()) { | ||||
|     return Colors.lightBlueAccent; | ||||
|   } else { | ||||
|     return Colors.lightBlue; | ||||
|     return Colors.blue; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -24,3 +31,4 @@ const Color COLOR_WARNING = Color.fromRGBO(250, 150, 50, 1); | ||||
| const Color COLOR_DANGER = Color.fromRGBO(200, 50, 75, 1); | ||||
| const Color COLOR_SUCCESS = Color.fromRGBO(100, 200, 75, 1); | ||||
| const Color COLOR_PROGRESS = Color.fromRGBO(50, 100, 200, 1); | ||||
| const Color COLOR_GRAY_LIGHT = Color.fromRGBO(150, 150, 150, 1); | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import "package:flutter/material.dart"; | ||||
|  | ||||
| import "package:flutter_speed_dial/flutter_speed_dial.dart"; | ||||
| import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; | ||||
| import "package:inventree/helpers.dart"; | ||||
| import "package:inventree/inventree/sales_order.dart"; | ||||
| import "package:inventree/preferences.dart"; | ||||
| import "package:inventree/widget/order/sales_order_detail.dart"; | ||||
| @@ -46,17 +47,20 @@ Future<void> barcodeFailure(String msg, dynamic extra) async { | ||||
|       msg, | ||||
|       success: false, | ||||
|     onAction: () { | ||||
|         OneContext().showDialog( | ||||
|           builder: (BuildContext context) => SimpleDialog( | ||||
|             title: Text(L10().barcodeError), | ||||
|             children: <Widget>[ | ||||
|               ListTile( | ||||
|                 title: Text(L10().responseData), | ||||
|                 subtitle: Text(extra.toString()) | ||||
|               ) | ||||
|             ] | ||||
|           ) | ||||
|         ); | ||||
|         if (hasContext()) { | ||||
|           OneContext().showDialog( | ||||
|               builder: (BuildContext context) => | ||||
|                   SimpleDialog( | ||||
|                       title: Text(L10().barcodeError), | ||||
|                       children: <Widget>[ | ||||
|                         ListTile( | ||||
|                             title: Text(L10().responseData), | ||||
|                             subtitle: Text(extra.toString()) | ||||
|                         ) | ||||
|                       ] | ||||
|                   ) | ||||
|           ); | ||||
|         } | ||||
|     } | ||||
|   ); | ||||
| } | ||||
| @@ -277,17 +281,20 @@ class BarcodeScanHandler extends BarcodeHandler { | ||||
|         success: false, | ||||
|         onAction: () { | ||||
|  | ||||
|           OneContext().showDialog( | ||||
|               builder: (BuildContext context) => SimpleDialog( | ||||
|                 title: Text(L10().unknownResponse), | ||||
|                 children: <Widget>[ | ||||
|                   ListTile( | ||||
|                     title: Text(L10().responseData), | ||||
|                     subtitle: Text(data.toString()), | ||||
|                   ) | ||||
|                 ], | ||||
|               ) | ||||
|           ); | ||||
|           if (hasContext()) { | ||||
|             OneContext().showDialog( | ||||
|                 builder: (BuildContext context) => | ||||
|                     SimpleDialog( | ||||
|                       title: Text(L10().unknownResponse), | ||||
|                       children: <Widget>[ | ||||
|                         ListTile( | ||||
|                           title: Text(L10().responseData), | ||||
|                           subtitle: Text(data.toString()), | ||||
|                         ) | ||||
|                       ], | ||||
|                     ) | ||||
|             ); | ||||
|           } | ||||
|         } | ||||
|     ); | ||||
|   } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import "package:flutter/material.dart"; | ||||
| import "package:inventree/helpers.dart"; | ||||
| import "package:one_context/one_context.dart"; | ||||
|  | ||||
| import "package:inventree/preferences.dart"; | ||||
| @@ -43,7 +44,7 @@ class InvenTreeBarcodeControllerState extends State<InvenTreeBarcodeController> | ||||
|    * Barcode data should be passed as a string | ||||
|    */ | ||||
|   Future<void> handleBarcodeData(String? data) async { | ||||
|      | ||||
|  | ||||
|     // Check that the data is valid, and this view is still mounted | ||||
|     if (!mounted || data == null || data.isEmpty) { | ||||
|       return; | ||||
| @@ -58,7 +59,11 @@ class InvenTreeBarcodeControllerState extends State<InvenTreeBarcodeController> | ||||
|       processingBarcode = true; | ||||
|     }); | ||||
|  | ||||
|     BuildContext? context = OneContext.hasContext ? OneContext().context : null; | ||||
|     BuildContext? context; | ||||
|  | ||||
|     if (hasContext()) { | ||||
|       context = OneContext.hasContext ? OneContext().context : null; | ||||
|     } | ||||
|  | ||||
|     showLoadingOverlay(context); | ||||
|     await pauseScan(); | ||||
|   | ||||
| @@ -1,175 +0,0 @@ | ||||
|  | ||||
| /* | ||||
|  * Custom keyboard listener which allows the app to act as a keyboard "wedge", | ||||
|  * and intercept barcodes from any compatible scanner. | ||||
|  * | ||||
|  * Note: This code was copied from https://github.com/fuadreza/flutter_barcode_listener/blob/master/lib/flutter_barcode_listener.dart | ||||
|  * | ||||
|  * If that code becomes available on pub.dev, we can remove this file and reference that library | ||||
|  */ | ||||
|  | ||||
| import "dart:async"; | ||||
|  | ||||
| import "package:flutter/material.dart"; | ||||
| import "package:flutter/services.dart"; | ||||
|  | ||||
| typedef BarcodeScannedCallback = void Function(String barcode); | ||||
|  | ||||
| /// This widget will listen for raw PHYSICAL keyboard events | ||||
| /// even when other controls have primary focus. | ||||
| /// It will buffer all characters coming in specifed `bufferDuration` time frame | ||||
| /// that end with line feed character and call callback function with result. | ||||
| /// Keep in mind this widget will listen for events even when not visible. | ||||
| /// Windows seems to be using the [RawKeyDownEvent] instead of the | ||||
| /// [RawKeyUpEvent], this behaviour can be managed by setting [useKeyDownEvent]. | ||||
| class BarcodeKeyboardListener extends StatefulWidget { | ||||
|  | ||||
|   /// This widget will listen for raw PHYSICAL keyboard events | ||||
|   /// even when other controls have primary focus. | ||||
|   /// It will buffer all characters coming in specifed `bufferDuration` time frame | ||||
|   /// that end with line feed character and call callback function with result. | ||||
|   /// Keep in mind this widget will listen for events even when not visible. | ||||
|   const BarcodeKeyboardListener( | ||||
|       {Key? key, | ||||
|  | ||||
|         /// Child widget to be displayed. | ||||
|         required this.child, | ||||
|  | ||||
|         /// Callback to be called when barcode is scanned. | ||||
|         required Function(String) onBarcodeScanned, | ||||
|  | ||||
|         /// When experiencing issueswith empty barcodes on Windows, | ||||
|         /// set this value to true. Default value is `false`. | ||||
|         this.useKeyDownEvent = false, | ||||
|  | ||||
|         /// Maximum time between two key events. | ||||
|         /// If time between two key events is longer than this value | ||||
|         /// previous keys will be ignored. | ||||
|         Duration bufferDuration = hundredMs}) | ||||
|       : _onBarcodeScanned = onBarcodeScanned, | ||||
|         _bufferDuration = bufferDuration, | ||||
|         super(key: key); | ||||
|  | ||||
|   final Widget child; | ||||
|   final BarcodeScannedCallback _onBarcodeScanned; | ||||
|   final Duration _bufferDuration; | ||||
|   final bool useKeyDownEvent; | ||||
|  | ||||
|   @override | ||||
|   _BarcodeKeyboardListenerState createState() => _BarcodeKeyboardListenerState( | ||||
|       _onBarcodeScanned, _bufferDuration, useKeyDownEvent); | ||||
| } | ||||
|  | ||||
| const Duration aSecond = Duration(seconds: 1); | ||||
| const Duration hundredMs = Duration(milliseconds: 100); | ||||
| const String lineFeed = "\n"; | ||||
|  | ||||
| class _BarcodeKeyboardListenerState extends State<BarcodeKeyboardListener> { | ||||
|  | ||||
|   _BarcodeKeyboardListenerState(this._onBarcodeScannedCallback, | ||||
|       this._bufferDuration, this._useKeyDownEvent) { | ||||
|     RawKeyboard.instance.addListener(_keyBoardCallback); | ||||
|     _keyboardSubscription = | ||||
|         _controller.stream.where((char) => char != null).listen(onKeyEvent); | ||||
|   } | ||||
|  | ||||
|   List<String> _scannedChars = []; | ||||
|   DateTime? _lastScannedCharCodeTime; | ||||
|   late StreamSubscription<String?> _keyboardSubscription; | ||||
|  | ||||
|   final BarcodeScannedCallback _onBarcodeScannedCallback; | ||||
|   final Duration _bufferDuration; | ||||
|  | ||||
|   final _controller = StreamController<String?>(); | ||||
|  | ||||
|   final bool _useKeyDownEvent; | ||||
|  | ||||
|   bool _isShiftPressed = false; | ||||
|   void onKeyEvent(String? char) { | ||||
|     //remove any pending characters older than bufferDuration value | ||||
|     checkPendingCharCodesToClear(); | ||||
|     _lastScannedCharCodeTime = DateTime.now(); | ||||
|     if (char == lineFeed) { | ||||
|       _onBarcodeScannedCallback.call(_scannedChars.join()); | ||||
|       resetScannedCharCodes(); | ||||
|     } else { | ||||
|       //add character to list of scanned characters; | ||||
|       _scannedChars.add(char!); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void checkPendingCharCodesToClear() { | ||||
|     if (_lastScannedCharCodeTime != null) { | ||||
|       if (_lastScannedCharCodeTime! | ||||
|           .isBefore(DateTime.now().subtract(_bufferDuration))) { | ||||
|         resetScannedCharCodes(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void resetScannedCharCodes() { | ||||
|     _lastScannedCharCodeTime = null; | ||||
|     _scannedChars = []; | ||||
|   } | ||||
|  | ||||
|   void addScannedCharCode(String charCode) { | ||||
|     _scannedChars.add(charCode); | ||||
|   } | ||||
|  | ||||
|   void _keyBoardCallback(RawKeyEvent keyEvent) { | ||||
|     if (keyEvent.logicalKey.keyId > 255 && | ||||
|         keyEvent.data.logicalKey != LogicalKeyboardKey.enter && | ||||
|         keyEvent.data.logicalKey != LogicalKeyboardKey.shiftLeft) return; | ||||
|     if ((!_useKeyDownEvent && keyEvent is RawKeyUpEvent) || | ||||
|         (_useKeyDownEvent && keyEvent is RawKeyDownEvent)) { | ||||
|       if (keyEvent.data is RawKeyEventDataAndroid) { | ||||
|         if (keyEvent.data.logicalKey == LogicalKeyboardKey.shiftLeft) { | ||||
|           _isShiftPressed = true; | ||||
|         } else { | ||||
|           if (_isShiftPressed) { | ||||
|             _isShiftPressed = false; | ||||
|             _controller.sink.add(String.fromCharCode( | ||||
|                 ((keyEvent.data) as RawKeyEventDataAndroid).codePoint).toUpperCase()); | ||||
|           } else { | ||||
|             _controller.sink.add(String.fromCharCode( | ||||
|                 ((keyEvent.data) as RawKeyEventDataAndroid).codePoint)); | ||||
|           } | ||||
|         } | ||||
|       } else if (keyEvent.data is RawKeyEventDataFuchsia) { | ||||
|         _controller.sink.add(String.fromCharCode( | ||||
|             ((keyEvent.data) as RawKeyEventDataFuchsia).codePoint)); | ||||
|       } else if (keyEvent.data.logicalKey == LogicalKeyboardKey.enter) { | ||||
|         _controller.sink.add(lineFeed); | ||||
|       } else if (keyEvent.data is RawKeyEventDataWeb) { | ||||
|         _controller.sink.add(((keyEvent.data) as RawKeyEventDataWeb).keyLabel); | ||||
|       } else if (keyEvent.data is RawKeyEventDataLinux) { | ||||
|         _controller.sink | ||||
|             .add(((keyEvent.data) as RawKeyEventDataLinux).keyLabel); | ||||
|       } else if (keyEvent.data is RawKeyEventDataWindows) { | ||||
|         _controller.sink.add(String.fromCharCode( | ||||
|             ((keyEvent.data) as RawKeyEventDataWindows).keyCode)); | ||||
|       } else if (keyEvent.data is RawKeyEventDataMacOs) { | ||||
|         _controller.sink | ||||
|             .add(((keyEvent.data) as RawKeyEventDataMacOs).characters); | ||||
|       } else if (keyEvent.data is RawKeyEventDataIos) { | ||||
|         _controller.sink | ||||
|             .add(((keyEvent.data) as RawKeyEventDataIos).characters); | ||||
|       } else { | ||||
|         _controller.sink.add(keyEvent.character); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return widget.child; | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     _keyboardSubscription.cancel(); | ||||
|     _controller.close(); | ||||
|     RawKeyboard.instance.removeListener(_keyBoardCallback); | ||||
|     super.dispose(); | ||||
|   } | ||||
| } | ||||
| @@ -42,7 +42,7 @@ class BarcodeScanStockLocationHandler extends BarcodeHandler { | ||||
|  | ||||
|         final bool result = await onLocationScanned(_loc); | ||||
|  | ||||
|         if (result && OneContext.hasContext) { | ||||
|         if (result && hasContext()) { | ||||
|           OneContext().pop(); | ||||
|         } | ||||
|         return; | ||||
|   | ||||
| @@ -1,11 +1,12 @@ | ||||
|  | ||||
| import "package:flutter/material.dart"; | ||||
| import "package:flutter/services.dart"; | ||||
| import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; | ||||
|  | ||||
| import "package:inventree/app_colors.dart"; | ||||
| import "package:inventree/barcode/controller.dart"; | ||||
| import "package:inventree/barcode/handler.dart"; | ||||
| import "package:inventree/barcode/flutter_barcode_listener.dart"; | ||||
|  | ||||
| import "package:inventree/l10.dart"; | ||||
| import "package:inventree/helpers.dart"; | ||||
|  | ||||
| @@ -31,6 +32,12 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState { | ||||
|  | ||||
|   bool get scanning => mounted && canScan; | ||||
|  | ||||
|   final FocusNode _focusNode = FocusNode(); | ||||
|  | ||||
|   List<String> _scannedCharacters = []; | ||||
|  | ||||
|   DateTime? _lastScanTime; | ||||
|  | ||||
|   @override | ||||
|   Future<void> pauseScan() async { | ||||
|  | ||||
| @@ -51,6 +58,45 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Callback for a single key press / scan | ||||
|   void handleKeyEvent(KeyEvent event) { | ||||
|  | ||||
|     if (!scanning) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     // Look only for key-down events | ||||
|     if (event is! KeyDownEvent) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     // Ignore events without a character code | ||||
|     if (event.character == null) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     DateTime now = DateTime.now(); | ||||
|  | ||||
|     // Throw away old characters | ||||
|     if (_lastScanTime == null || _lastScanTime!.isBefore(now.subtract(Duration(milliseconds: 250)))) { | ||||
|       _scannedCharacters.clear(); | ||||
|     } | ||||
|  | ||||
|     _lastScanTime = now; | ||||
|  | ||||
|     if (event.character == "\n") { | ||||
|       if (_scannedCharacters.isNotEmpty) { | ||||
|         // Debug output required for unit testing | ||||
|         debug("scanned: ${_scannedCharacters.join()}"); | ||||
|         handleBarcodeData(_scannedCharacters.join()); | ||||
|       } | ||||
|  | ||||
|       _scannedCharacters.clear(); | ||||
|     } else { | ||||
|       _scannedCharacters.add(event.character!); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|  | ||||
| @@ -66,8 +112,9 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState { | ||||
|             Spacer(flex: 5), | ||||
|             Icon(TablerIcons.barcode, size: 64), | ||||
|             Spacer(flex: 5), | ||||
|             BarcodeKeyboardListener( | ||||
|               useKeyDownEvent: true, | ||||
|             KeyboardListener( | ||||
|               autofocus: true, | ||||
|               focusNode: _focusNode, | ||||
|               child: SizedBox( | ||||
|                 child: CircularProgressIndicator( | ||||
|                   color: scanning ? COLOR_ACTION : COLOR_PROGRESS | ||||
| @@ -75,13 +122,16 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState { | ||||
|                 width: 64, | ||||
|                 height: 64, | ||||
|               ), | ||||
|               onBarcodeScanned: (String barcode) { | ||||
|                 debug("scanned: ${barcode}"); | ||||
|                 if (scanning) { | ||||
|                   // Process the barcode data | ||||
|                   handleBarcodeData(barcode); | ||||
|                 } | ||||
|               onKeyEvent: (event) { | ||||
|                 handleKeyEvent(event); | ||||
|               }, | ||||
|               // onBarcodeScanned: (String barcode) { | ||||
|               //   debug("scanned: ${barcode}"); | ||||
|               //   if (scanning) { | ||||
|               //     // Process the barcode data | ||||
|               //     handleBarcodeData(barcode); | ||||
|               //   } | ||||
|               // }, | ||||
|             ), | ||||
|             Spacer(flex: 5), | ||||
|             Padding( | ||||
|   | ||||
| @@ -39,13 +39,32 @@ bool debugContains(String msg, {bool raiseAssert = true}) { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (!result) { | ||||
|     print("Debug does not contain expected string: '${msg}'"); | ||||
|   } | ||||
|  | ||||
|   if (raiseAssert) { | ||||
|  | ||||
|     assert(result); | ||||
|   } | ||||
|  | ||||
|   return result; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool isTesting() { | ||||
|   return Platform.environment.containsKey("FLUTTER_TEST"); | ||||
| } | ||||
|  | ||||
| bool hasContext() { | ||||
|   try { | ||||
|     return !isTesting() && OneContext.hasContext; | ||||
|   } catch (error) { | ||||
|     return false; | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Display a debug message if we are in testing mode, or running in debug mode | ||||
|  */ | ||||
| @@ -83,7 +102,7 @@ Future<void> playAudioFile(String path) async { | ||||
|   // Debug message for unit testing | ||||
|   debug("Playing audio file: '${path}'"); | ||||
|  | ||||
|   if (!OneContext.hasContext) { | ||||
|   if (!hasContext()) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
| @@ -117,21 +136,13 @@ String renderCurrency(double? amount, String currency, {int decimals = 2}) { | ||||
|  | ||||
|   if (currency.isEmpty) return "-"; | ||||
|  | ||||
|   CurrencyFormatterSettings backupSettings = CurrencyFormatterSettings( | ||||
|     symbol: "\$", | ||||
|     symbolSide: SymbolSide.left, | ||||
|   ); | ||||
|   CurrencyFormat fmt = CurrencyFormat.fromCode(currency.toLowerCase()) ?? CurrencyFormat.usd; | ||||
|  | ||||
|   String value = CurrencyFormatter.format( | ||||
|     amount, | ||||
|     CurrencyFormatter.majors[currency.toLowerCase()] ?? backupSettings | ||||
|     fmt | ||||
|   ); | ||||
|  | ||||
|   // If we were not able to determine the currency | ||||
|   if (!CurrencyFormatter.majors.containsKey(currency.toLowerCase())) { | ||||
|     value += " ${currency}"; | ||||
|   } | ||||
|  | ||||
|   return value; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import "dart:io"; | ||||
|  | ||||
| import "package:device_info_plus/device_info_plus.dart"; | ||||
| import "package:inventree/helpers.dart"; | ||||
| import "package:one_context/one_context.dart"; | ||||
| import "package:package_info_plus/package_info_plus.dart"; | ||||
| import "package:sentry_flutter/sentry_flutter.dart"; | ||||
| @@ -129,16 +130,16 @@ Future<bool> sentryReportMessage(String message, {Map<String, String>? context}) | ||||
|   } | ||||
|  | ||||
|   Sentry.configureScope((scope) { | ||||
|     scope.setExtra("server", server_info); | ||||
|     scope.setExtra("app", app_info); | ||||
|     scope.setExtra("device", device_info); | ||||
|     scope.setContexts("server", server_info); | ||||
|     scope.setContexts("app", app_info); | ||||
|     scope.setContexts("device", device_info); | ||||
|  | ||||
|     if (context != null) { | ||||
|       scope.setExtra("context", context); | ||||
|       scope.setContexts("context", context); | ||||
|     } | ||||
|  | ||||
|     // Catch stacktrace data if possible | ||||
|     scope.setExtra("stacktrace", StackTrace.current.toString()); | ||||
|     scope.setContexts("stacktrace", StackTrace.current.toString()); | ||||
|   }); | ||||
|  | ||||
|   try { | ||||
| @@ -203,7 +204,7 @@ Future<void> sentryReportError(String source, dynamic error, StackTrace? stackTr | ||||
|   // Ensure we pass the 'source' of the error | ||||
|   context["source"] = source; | ||||
|  | ||||
|   if (OneContext.hasContext) { | ||||
|   if (hasContext()) { | ||||
|     final ctx = OneContext().context; | ||||
|  | ||||
|     if (ctx != null) { | ||||
| @@ -213,10 +214,10 @@ Future<void> sentryReportError(String source, dynamic error, StackTrace? stackTr | ||||
|   } | ||||
|  | ||||
|   Sentry.configureScope((scope) { | ||||
|     scope.setExtra("server", server_info); | ||||
|     scope.setExtra("app", app_info); | ||||
|     scope.setExtra("device", device_info); | ||||
|     scope.setExtra("context", context); | ||||
|     scope.setContexts("server", server_info); | ||||
|     scope.setContexts("app", app_info); | ||||
|     scope.setContexts("device", device_info); | ||||
|     scope.setContexts("context", context); | ||||
|   }); | ||||
|  | ||||
|   Sentry.captureException(error, stackTrace: stackTrace).catchError((error) { | ||||
|   | ||||
							
								
								
									
										19
									
								
								lib/l10.dart
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								lib/l10.dart
									
									
									
									
									
								
							| @@ -4,18 +4,23 @@ import "package:flutter_gen/gen_l10n/app_localizations_en.dart"; | ||||
| import "package:one_context/one_context.dart"; | ||||
| import "package:flutter/material.dart"; | ||||
|  | ||||
| import "package:inventree/helpers.dart"; | ||||
|  | ||||
| // Shortcut function to reduce boilerplate! | ||||
| I18N L10() | ||||
| { | ||||
|   if (OneContext.hasContext) { | ||||
|     BuildContext? _ctx = OneContext().context; | ||||
|   // Testing mode - ignore context | ||||
|   if (!hasContext()) { | ||||
|     return I18NEn(); | ||||
|   } | ||||
|  | ||||
|     if (_ctx != null) { | ||||
|       I18N? i18n = I18N.of(_ctx); | ||||
|   BuildContext? _ctx = OneContext().context; | ||||
|  | ||||
|       if (i18n != null) { | ||||
|         return i18n; | ||||
|       } | ||||
|   if (_ctx != null) { | ||||
|     I18N? i18n = I18N.of(_ctx); | ||||
|  | ||||
|     if (i18n != null) { | ||||
|       return i18n; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -162,13 +162,13 @@ class InvenTreeAppState extends State<StatefulWidget> { | ||||
|     return AdaptiveTheme( | ||||
|       light: ThemeData( | ||||
|         brightness: Brightness.light, | ||||
|         primarySwatch: Colors.lightBlue, | ||||
|         secondaryHeaderColor: Colors.blueGrey | ||||
|         colorSchemeSeed: Colors.lightBlueAccent, | ||||
|         useMaterial3: true, | ||||
|       ), | ||||
|       dark: ThemeData( | ||||
|         brightness: Brightness.dark, | ||||
|         primarySwatch: Colors.lightBlue, | ||||
|         secondaryHeaderColor: Colors.blueGrey, | ||||
|         colorSchemeSeed: Colors.blue, | ||||
|         useMaterial3: true, | ||||
|       ), | ||||
|       initial: savedThemeMode ?? AdaptiveThemeMode.light, | ||||
|       builder: (light, dark) =>  MaterialApp( | ||||
|   | ||||
| @@ -3,7 +3,6 @@ import "dart:ui"; | ||||
|  | ||||
| import "package:inventree/l10n/supported_locales.dart"; | ||||
| import "package:path_provider/path_provider.dart"; | ||||
| import "package:sembast/sembast.dart"; | ||||
| import "package:sembast/sembast_io.dart"; | ||||
| import "package:path/path.dart"; | ||||
|  | ||||
|   | ||||
| @@ -31,6 +31,10 @@ Future<void> choiceDialog(String title, List<Widget> items, {Function? onSelecte | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   if (!hasContext()) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   OneContext().showDialog( | ||||
|     builder: (BuildContext context) { | ||||
|       return AlertDialog( | ||||
| @@ -63,6 +67,10 @@ Future<void> confirmationDialog(String title, String text, {Color? color, IconDa | ||||
|   String _accept = acceptText ?? L10().ok; | ||||
|   String _reject = rejectText ?? L10().cancel; | ||||
|  | ||||
|   if (!hasContext()) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   OneContext().showDialog( | ||||
|     builder: (BuildContext context) { | ||||
|       return AlertDialog( | ||||
| @@ -176,6 +184,10 @@ Future<void> showErrorDialog(String title, {String description = "", APIResponse | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (!hasContext()) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   OneContext().showDialog( | ||||
|     builder: (context) => SimpleDialog( | ||||
|       title: ListTile( | ||||
| @@ -196,7 +208,7 @@ Future<void> showErrorDialog(String title, {String description = "", APIResponse | ||||
|  */ | ||||
| Future<void> showServerError(String url, String title, String description) async { | ||||
|  | ||||
|   if (!OneContext.hasContext) { | ||||
|   if (!hasContext()) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,7 @@ void showSnackIcon(String text, {IconData? icon, Function()? onAction, bool? suc | ||||
|   debug("showSnackIcon: '${text}'"); | ||||
|  | ||||
|   // Escape quickly if we do not have context | ||||
|   if (!OneContext.hasContext) { | ||||
|   if (!hasContext()) { | ||||
|     // Debug message for unit testing | ||||
|     return; | ||||
|   } | ||||
|   | ||||
| @@ -301,12 +301,16 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> { | ||||
|     // Serial number field is not required here | ||||
|     fields.remove("serial"); | ||||
|  | ||||
|     Map<String, dynamic> data = {}; | ||||
|  | ||||
|     if (location != null) { | ||||
|       data["location"] = location!.pk; | ||||
|     } | ||||
|  | ||||
|     InvenTreeStockItem().createForm( | ||||
|         context, | ||||
|         L10().stockItemCreate, | ||||
|         data: { | ||||
|           "location": location != null ? location!.pk : null, | ||||
|         }, | ||||
|         data: data, | ||||
|         fields: fields, | ||||
|         onSuccess: (result) async { | ||||
|           Map<String, dynamic> data = result as Map<String, dynamic>; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user