From 41d086e13e7d9c714758a242b0764a5dd157b44c Mon Sep 17 00:00:00 2001 From: Sven Carstensen Date: Wed, 3 Jul 2024 12:47:49 +0200 Subject: [PATCH] Erweiterungen --- lib/content_views/main_page.dart | 15 +- lib/main.dart | 8 +- lib/popups/accept_jobs_popup.dart | 318 ++++++++++++++++-------------- lib/tools/mqtt_core.dart | 39 ++-- lib/tools/util.dart | 17 +- pubspec.lock | 28 ++- pubspec.yaml | 4 +- 7 files changed, 244 insertions(+), 185 deletions(-) diff --git a/lib/content_views/main_page.dart b/lib/content_views/main_page.dart index 3c2e2fd..a59d2fd 100644 --- a/lib/content_views/main_page.dart +++ b/lib/content_views/main_page.dart @@ -327,20 +327,23 @@ class _MainPageState extends State { } } - showLogoutAlertDialog(BuildContext context) { + showLogoutAlertDialog(BuildContext context) async { Widget yesButton = TextButton( child: const Text("Ja"), onPressed: () { Navigator.of(context).pop(); - _sendLogoutMessage(); - Future.delayed(const Duration(seconds: 1), () async { - MessageService().publish(Constants.messageTopicDisconnectClient, arguments: DisconnectClientMessage()); - }); + showMessageDialog(context, "Information", "Sie werden abgemeldet.\nBitte warten..."); + + _sendLogoutMessage(); _loggedOut = true; - setState(() {}); + Future.delayed(const Duration(seconds: 5), () async { + Navigator.of(context).pop(); + + setState(() {}); + }); }, ); diff --git a/lib/main.dart b/lib/main.dart index c73eef1..bcffa47 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -449,10 +449,12 @@ class _MainState extends State
with SingleTickerProviderStateMixin { }); } - void _connectionStatusChanged(ConnectionStatus connectionStatus) { - Util.logInfo("_connectionStatusChanged called: $connectionStatus"); + void _connectionStatusChanged(ConnectionStatus newConnectionStatus) { + var currentConnectionStatus = newConnectionStatus.status; - _connectionLostValueNotifier.value = connectionStatus.status == ConnectionStatusEnum.notConnected; + Util.logInfo("_connectionStatusChanged called: $currentConnectionStatus"); + + _connectionLostValueNotifier.value = newConnectionStatus.status == ConnectionStatusEnum.notConnected; } void _jobReceived(Job job) async { diff --git a/lib/popups/accept_jobs_popup.dart b/lib/popups/accept_jobs_popup.dart index 25753b7..e65e86a 100644 --- a/lib/popups/accept_jobs_popup.dart +++ b/lib/popups/accept_jobs_popup.dart @@ -1,10 +1,8 @@ - import 'dart:math'; import 'package:expandable/expandable.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; -import 'package:isar/isar.dart'; import 'package:mqtt_client/mqtt_client.dart'; import 'package:stadtbote/main.dart'; import 'package:stadtbote/protocoll_messages/accept_job_message.dart'; @@ -57,169 +55,182 @@ Future showAcceptJobsPopup(BuildContext context, List jobs, Function( } }); - MessageService().subscribe(Constants.messageTopicJobReceived, jobs, (jobdMessage) async { - jobs.removeWhere((value) => value.id == jobdMessage.job.id); + MessageService().subscribe(Constants.messageTopicJobReceived, jobs, (jobMessage) async { + jobs.removeWhere((value) => value.id == jobMessage.job.id); - jobs.add(jobdMessage.job); + if (jobMessage.job.state == 0) { + jobs.add(jobMessage.job); - if (context.mounted) { - setState(() {}); + if (context.mounted) { + setState(() {}); + } } }); return SizedBox( - key: GlobalKey(), - height: MediaQuery.of(context).size.height * 1, - width: MediaQuery.of(context).size.width * 1.2, - child: Container( + key: GlobalKey(), + height: MediaQuery.of(context).size.height * 1, + width: MediaQuery.of(context).size.width * 1.2, + child: Container( height: 100, padding: const EdgeInsets.all(5), - child: ListView.builder( - itemCount: jobs.length, - itemBuilder: (_, i) { - var job = jobs[i]; - - for (var job in jobs) { - int enableDelay = job.delayOrderTime; - if (job.firstDisplayTime == null) { - MyApp.isarInstance.writeTxn(() async { - job.firstDisplayTime = DateTime.now(); - - await MyApp.isarInstance.jobs.put(job); - }); - } else { - enableDelay = max(0, DateTime.now().difference(job.firstDisplayTime!).inSeconds); - } - - job.enableDelay = enableDelay; + child: FutureBuilder>( + future: fetchJobWidgets(jobs), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Util.getWaitWidget(context); + } else { + return ListView( + children: snapshot.requireData, + ); } - - return ExpandableNotifier( - child: ListTile( - contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), - title: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - border: Border.all( - color: Constants.colorPrimary, - width: 1, - ), - ), - child: badges.Badge( - position: badges.BadgePosition.topEnd(top: -13, end: -5), - showBadge: false, - badgeContent: const Text( - 'Neu', - style: TextStyle( - color: Colors.white, - fontSize: 18.0, - ), - ), - badgeStyle: badges.BadgeStyle( - shape: badges.BadgeShape.square, - badgeColor: Constants.colorPrimary, - padding: const EdgeInsets.all(5), - borderRadius: BorderRadius.circular(5), - borderSide: const BorderSide(color: Constants.colorPrimary, width: 0), - ), - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - children: [ - SizedBox( - width: double.infinity, - child: Text( - DateFormat('dd.MM.yyyy HH:mm').format(DateTime.parse(job.ordertime).toLocal()).toString(), - textAlign: TextAlign.left, - style: TextStyle(backgroundColor: isJobInFuture(job) ? Colors.yellow : Colors.transparent), - ), - ), - SizedBox(width: double.infinity, child: Text('Auftragsnummer: ${job.id}', textAlign: TextAlign.left)), - SizedBox(width: double.infinity, child: Text(job.tours[0].comp, textAlign: TextAlign.left)), - SizedBox(width: double.infinity, child: Text(job.tours[0].street, textAlign: TextAlign.left)), - SizedBox(width: double.infinity, child: Text(job.tours[0].city, textAlign: TextAlign.left)), - Row( - children: [ - Image.asset("assets/${mapVehicleTypeToImageName(job.vhtId)}", width: 60, height: 60), - const SizedBox(width: 10), - Text(mapVehicleTypeToText(job.vhtId)), - ], - ), - const SizedBox(height: 10), - ScrollOnExpand( - scrollOnExpand: true, - scrollOnCollapse: false, - child: ExpandablePanel( - theme: const ExpandableThemeData( - headerAlignment: ExpandablePanelHeaderAlignment.center, - tapBodyToCollapse: true, - ), - header: const Padding( - padding: EdgeInsets.all(0), - child: Text( - "Weitere Infos....", - style: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.bold), - )), - collapsed: const SizedBox(), - expanded: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Container( - height: 200, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: const Color(0xfffff4e4), - border: Border.all( - color: Colors.grey, - width: 0.5, - ), - ), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text(job.rem), - ), - ), - ], - ), - ), - ), - const SizedBox(height: 20), - Row(children: [ - Expanded( - child: getDefaultButton(!job.global ? 'Ablehnen' : 'Ignorieren', - () { - _sendAnswer(job, !job.global ? 2 : 3); - - jobs.removeWhere((value) => value.id == job.id); - - setState(() {}); - }), - ), - const SizedBox(width: 20), - Expanded( - child: getDefaultButton('Annehmen', enabledTimerActive: job.global == false && job.enableDelay > 0, enabledTimerDelay: job.enableDelay, () { - _sendAnswer(job, 1); - - jobs.removeWhere((value) => value.id == job.id); - - setState(() {}); - }), - ) - ]) - ], - ), - ), - ), - ), - ), - ); }, - )), - ); + ), + )); }))); }); } +Future> fetchJobWidgets(List jobs) async { + List widgets = []; + + for (var job in jobs) { + int enableDelay = 3; //job.delayOrderTime; + if (job.firstDisplayTime == null) { + await MyApp.isarInstance.writeTxn(() async { + job.firstDisplayTime = DateTime.now(); + + await MyApp.isarInstance.jobs.put(job); + }); + } else { + enableDelay = max(0, DateTime.now().difference(job.firstDisplayTime!).inSeconds); + } + + widgets.add(StatefulBuilder(builder: (BuildContext context, StateSetter setState) { + return ExpandableNotifier( + child: ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), + title: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: Constants.colorPrimary, + width: 1, + ), + ), + child: badges.Badge( + position: badges.BadgePosition.topEnd(top: -13, end: -5), + showBadge: false, + badgeContent: const Text( + 'Neu', + style: TextStyle( + color: Colors.white, + fontSize: 18.0, + ), + ), + badgeStyle: badges.BadgeStyle( + shape: badges.BadgeShape.square, + badgeColor: Constants.colorPrimary, + padding: const EdgeInsets.all(5), + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Constants.colorPrimary, width: 0), + ), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + children: [ + SizedBox( + width: double.infinity, + child: Text( + DateFormat('dd.MM.yyyy HH:mm').format(DateTime.parse(job.ordertime).toLocal()).toString(), + textAlign: TextAlign.left, + style: TextStyle(backgroundColor: isJobInFuture(job) ? Colors.yellow : Colors.transparent), + ), + ), + SizedBox(width: double.infinity, child: Text('Auftragsnummer: ${job.id}', textAlign: TextAlign.left)), + SizedBox(width: double.infinity, child: Text(job.tours[0].comp, textAlign: TextAlign.left)), + SizedBox(width: double.infinity, child: Text(job.tours[0].street, textAlign: TextAlign.left)), + SizedBox(width: double.infinity, child: Text(job.tours[0].city, textAlign: TextAlign.left)), + Row( + children: [ + Image.asset("assets/${mapVehicleTypeToImageName(job.vhtId)}", width: 60, height: 60), + const SizedBox(width: 10), + Text(mapVehicleTypeToText(job.vhtId)), + ], + ), + const SizedBox(height: 10), + ScrollOnExpand( + scrollOnExpand: true, + scrollOnCollapse: false, + child: ExpandablePanel( + theme: const ExpandableThemeData( + headerAlignment: ExpandablePanelHeaderAlignment.center, + tapBodyToCollapse: true, + ), + header: const Padding( + padding: EdgeInsets.all(0), + child: Text( + "Weitere Infos....", + style: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.bold), + )), + collapsed: const SizedBox(), + expanded: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + height: 200, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: const Color(0xfffff4e4), + border: Border.all( + color: Colors.grey, + width: 0.5, + ), + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(job.rem), + ), + ), + ], + ), + ), + ), + const SizedBox(height: 20), + Row(children: [ + Expanded( + child: getDefaultButton(!job.global ? 'Ablehnen' : 'Ignorieren', () { + _sendAnswer(job, !job.global ? 2 : 3); + + jobs.removeWhere((value) => value.id == job.id); + + setState(() {}); + }), + ), + const SizedBox(width: 20), + Expanded( + child: getDefaultButton('Annehmen', enabledTimerActive: enableDelay > 0, enabledTimerDelay: enableDelay, () { + _sendAnswer(job, 1); + + jobs.removeWhere((value) => value.id == job.id); + + setState(() {}); + }), + ) + ]) + ], + ), + ), + ), + ), + ), + ); + })); + } + + return widgets; +} + _sendAnswer(Job job, int item) async { var currentPosition = MyApp.currentPosition; @@ -234,9 +245,10 @@ _sendAnswer(Job job, int item) async { message.track.pos!.long = currentPosition != null ? currentPosition.longitude : 0.0; message.track.pos!.acc = currentPosition != null ? currentPosition.accuracy.toInt() : 0; - MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "reaction", message: message.toJson(), qos: MqttQos.exactlyOnce)); + MessageService().publish(Constants.messageTopicPublishPayload, + arguments: PublishPayloadMessage(topic: "reaction", message: message.toJson(), qos: MqttQos.exactlyOnce)); - await MyApp.isarInstance.writeTxn(() async{ + await MyApp.isarInstance.writeTxn(() async { await MyApp.isarInstance.jobs.delete(job.id); }); diff --git a/lib/tools/mqtt_core.dart b/lib/tools/mqtt_core.dart index e4af67c..4b55c56 100644 --- a/lib/tools/mqtt_core.dart +++ b/lib/tools/mqtt_core.dart @@ -52,8 +52,6 @@ import 'util.dart'; enum ConnectionStateEnum { notConnected, connected } -//const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('app_icon'); - class MqttCore { late ConnectionStateEnum connectionState = ConnectionStateEnum.notConnected; @@ -81,18 +79,21 @@ class MqttCore { _processBootstrap(); } - _disconnect() { + _disconnectMqttClient() { + MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.notConnected)); + _disconnectInProgress = true; _cancelReconnectTimer(); _cancelTrackTimer(); - if (_mqttClient != null) { + if (_mqttClient != null && _mqttClient!.connectionStatus == ConnectionStateEnum.connected) { _mqttClient!.disconnect(); } connectionState = ConnectionStateEnum.notConnected; + } _connectToBroker() async { @@ -102,15 +103,17 @@ class MqttCore { Util.logInfo("broker uri: $_brokerUri"); Util.logInfo("broker port: $_brokerPort"); - _mqttClient = MqttServerClient.withPort(_brokerUri, clientId, _brokerPort); - _mqttClient!.logging(on: false); - _mqttClient!.keepAlivePeriod = 60; - _mqttClient!.disconnectOnNoResponsePeriod = 10; - _mqttClient!.autoReconnect = true; - _mqttClient!.onDisconnected = _clientDisconnected; - _mqttClient!.onAutoReconnect = _clientDisconnected; - _mqttClient!.onAutoReconnected = _clientConnected; - _mqttClient!.connectionMessage = MqttConnectMessage().withClientIdentifier(await Util.getAppUuid()).authenticateAs("app", "apppwd"); + if (_mqttClient == null) { + _mqttClient = MqttServerClient.withPort(_brokerUri, clientId, _brokerPort); + _mqttClient!.logging(on: false); + _mqttClient!.keepAlivePeriod = 60; + _mqttClient!.disconnectOnNoResponsePeriod = 10; + _mqttClient!.autoReconnect = true; + _mqttClient!.onDisconnected = _clientDisconnected; + _mqttClient!.onAutoReconnect = _clientDisconnected; + _mqttClient!.onAutoReconnected = _clientConnected; + _mqttClient!.connectionMessage = MqttConnectMessage().withClientIdentifier(await Util.getAppUuid()).authenticateAs("app", "apppwd"); + } await _mqttClient!.connect(); @@ -384,8 +387,10 @@ class MqttCore { } void _cancelReconnectTimer() { - if (_reconnectTimer != null && _reconnectTimer!.isActive) { - _reconnectTimer!.cancel(); + if (!_disconnectInProgress) { + if (_reconnectTimer != null && _reconnectTimer!.isActive) { + _reconnectTimer!.cancel(); + } } } @@ -394,7 +399,7 @@ class MqttCore { connectionState = ConnectionStateEnum.notConnected; - _startTrackTimer(); + _cancelTrackTimer(); MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.notConnected)); } @@ -546,7 +551,7 @@ class MqttCore { MessageService().subscribe(Constants.messageTopicDisconnectClient, this, (DisconnectClientMessage disconnectClientMessage) { - _disconnect(); + _disconnectMqttClient(); }); MessageService().subscribe(Constants.messageTopicConnectClient, this, (ConnectClientMessage connectClientMessage) { diff --git a/lib/tools/util.dart b/lib/tools/util.dart index 97b64fb..b20e1e0 100644 --- a/lib/tools/util.dart +++ b/lib/tools/util.dart @@ -220,8 +220,8 @@ class Util { } } -Widget getDefaultButton(String text, Function() onPressed, {Image? image, double? buttonHeight, bool enabledTimerActive = true, int enabledTimerDelay = 0}) { - bool enabledState = enabledTimerActive; +Widget getDefaultButton(String text, Function() onPressed, {Image? image, double? buttonHeight, bool enabledTimerActive = false, int enabledTimerDelay = 0}) { + bool enabledState = !enabledTimerActive; return StatefulBuilder(builder: (BuildContext context, StateSetter setState) { if (!enabledState) { @@ -411,6 +411,19 @@ Future showAlertDialog(BuildContext context, String title, String message) ); } +Future showMessageDialog(BuildContext context, String title, String message) async { + await showDialog( + barrierDismissible: false, + context: context, + builder: (context) { + return AlertDialog( + title: Text(title), + content: Text(message), + ); + } + ); +} + class DecimalTextInputFormatter extends TextInputFormatter { int maxLength; diff --git a/pubspec.lock b/pubspec.lock index 1c0c0a2..77e6913 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -209,6 +209,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + url: "https://pub.dev" + source: hosted + version: "1.3.1" checked_yaml: dependency: transitive description: @@ -972,10 +980,10 @@ packages: dependency: "direct main" description: name: mqtt_client - sha256: ba10ec490ded55dc4e77bbc992529d823fb15d0d5ec68c2895f960312060c541 + sha256: "001861aba553eaf419aab60b7956227af47a9d1e1efe0d2fcb3957d3fc30d110" url: "https://pub.dev" source: hosted - version: "9.8.1" + version: "10.2.1" open_filex: dependency: "direct main" description: @@ -1429,6 +1437,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1" + universal_html: + dependency: transitive + description: + name: universal_html + sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971" + url: "https://pub.dev" + source: hosted + version: "2.2.4" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + url: "https://pub.dev" + source: hosted + version: "2.2.2" url_launcher: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 4d56f26..d0ac3e7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 317.0.0+317 +version: 319.0.0+319 environment: sdk: '>=2.19.6 <3.0.0' @@ -43,7 +43,7 @@ dependencies: open_filex: ^4.3.2 path_provider: ^2.0.14 split_view: ^3.2.1 - mqtt_client: ^9.8.1 + mqtt_client: ^10.2.1 isar_flutter_libs: ^3.1.0+1 isar: ^3.1.0+1 json_serializable: ^6.6.2