diff --git a/assets/release_notes.md b/assets/release_notes.md index 1b6d67c0..ddb3e521 100644 --- a/assets/release_notes.md +++ b/assets/release_notes.md @@ -9,6 +9,7 @@ - Indicate available quantity in stock detail view - Adds configurable filtering to various list views - Allow stock location to be "scanned" into another location using barcode +- Improves server connection status indicator on home screen ### 0.7.3 - June 2022 --- diff --git a/lib/api.dart b/lib/api.dart index 3851659c..8ab68d01 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -135,6 +135,9 @@ class InvenTreeFileService extends FileService { */ +/* + * API class which manages all communication with the InvenTree server + */ class InvenTreeAPI { factory InvenTreeAPI() { @@ -143,6 +146,19 @@ class InvenTreeAPI { InvenTreeAPI._internal(); + // List of callback functions to trigger when the connection status changes + List _statusCallbacks = []; + + // Register a callback function to be notified when the connection status changes + void registerCallback(Function() func) => _statusCallbacks.add(func); + + void _connectionStatusChanged() { + for (Function() func in _statusCallbacks) { + // Call the function + func(); + } + } + // Minimum required API version for server static const _minApiVersion = 20; @@ -202,6 +218,10 @@ class InvenTreeAPI { // Authentication token (initially empty, must be requested) String _token = ""; + String? get serverAddress { + return profile?.server; + } + bool get hasToken => _token.isNotEmpty; /* @@ -457,6 +477,8 @@ class InvenTreeAPI { // Clear received settings _globalSettings.clear(); _userSettings.clear(); + + _connectionStatusChanged(); } /* @@ -481,6 +503,8 @@ class InvenTreeAPI { _connecting = true; + _connectionStatusChanged(); + _connected = await _connect(); _connecting = false; @@ -493,6 +517,8 @@ class InvenTreeAPI { ); } + _connectionStatusChanged(); + return _connected; } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 638975ae..ecbb6f94 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -908,6 +908,9 @@ "serverNotConnected": "Server not connected", "@serverNotConnected": {}, + "serverNotSelected": "Server not selected", + "@serverNotSelected": {}, + "sounds": "Sounds", "@sounds": {}, diff --git a/lib/settings/login.dart b/lib/settings/login.dart index e61c08b4..743ba9ad 100644 --- a/lib/settings/login.dart +++ b/lib/settings/login.dart @@ -29,6 +29,10 @@ class _InvenTreeLoginSettingsState extends State { profiles = await UserProfileDBManager().getAllProfiles(); + if (!mounted) { + return; + } + setState(() { }); } @@ -58,6 +62,10 @@ class _InvenTreeLoginSettingsState extends State { await UserProfileDBManager().selectProfile(key); + if (!mounted) { + return; + } + _reload(); // Attempt server login (this will load the newly selected profile @@ -72,6 +80,10 @@ class _InvenTreeLoginSettingsState extends State { await UserProfileDBManager().deleteProfile(profile); + if (!mounted) { + return; + } + _reload(); if (InvenTreeAPI().isConnected() && profile.key == (InvenTreeAPI().profile?.key ?? "")) { diff --git a/lib/widget/home.dart b/lib/widget/home.dart index 0870b02c..dc4333b7 100644 --- a/lib/widget/home.dart +++ b/lib/widget/home.dart @@ -23,6 +23,7 @@ import "package:inventree/widget/part_list.dart"; import "package:inventree/widget/purchase_order_list.dart"; import "package:inventree/widget/search.dart"; import "package:inventree/widget/snacks.dart"; +import "package:inventree/widget/spinner.dart"; class InvenTreeHomePage extends StatefulWidget { @@ -51,6 +52,13 @@ class _InvenTreeHomePageState extends State { ), (timer) { _refreshNotifications(); }); + + InvenTreeAPI().registerCallback(() { + setState(() { + // Reload the widget + }); + }); + } // Index of bottom navigation bar @@ -352,6 +360,24 @@ class _InvenTreeHomePageState extends State { * display a connection status widget */ Widget _connectionStatusWidget(BuildContext context) { + + String? serverAddress = InvenTreeAPI().serverAddress; + bool validAddress = serverAddress != null; + bool connecting = !InvenTreeAPI().isConnected() && InvenTreeAPI().isConnecting(); + + Widget leading = FaIcon(FontAwesomeIcons.exclamationCircle, color: COLOR_DANGER); + Widget trailing = FaIcon(FontAwesomeIcons.server, color: COLOR_CLICK); + String title = L10().serverNotConnected; + String subtitle = L10().profileSelectOrCreate; + + if (!validAddress) { + title = L10().serverNotSelected; + } else if (connecting) { + title = L10().serverConnecting; + subtitle = serverAddress; + leading = Spinner(icon: FontAwesomeIcons.spinner, color: COLOR_PROGRESS); + } + return Center( child: Column( children: [ @@ -363,10 +389,10 @@ class _InvenTreeHomePageState extends State { ), Spacer(), ListTile( - title: Text(L10().serverNotConnected), - subtitle: Text(L10().profileSelectOrCreate), - trailing: FaIcon(FontAwesomeIcons.server, color: COLOR_CLICK), - leading: FaIcon(FontAwesomeIcons.exclamationCircle, color: COLOR_DANGER), + title: Text(title), + subtitle: Text(subtitle), + trailing: trailing, + leading: leading, onTap: _selectProfile, ) ] @@ -456,6 +482,7 @@ class _InvenTreeHomePageState extends State { Widget build(BuildContext context) { var connected = InvenTreeAPI().isConnected(); + var connecting = !connected && InvenTreeAPI().isConnecting(); return Scaffold( key: _homeKey, @@ -465,7 +492,7 @@ class _InvenTreeHomePageState extends State { IconButton( icon: FaIcon( FontAwesomeIcons.server, - color: connected ? COLOR_SUCCESS : COLOR_DANGER, + color: connected ? COLOR_SUCCESS : (connecting ? COLOR_PROGRESS: COLOR_DANGER), ), onPressed: _selectProfile, )