From 7015d83fd17c6a6956b007828530b222ca62e445 Mon Sep 17 00:00:00 2001 From: Sven Carstensen Date: Mon, 29 Jul 2024 10:46:55 +0200 Subject: [PATCH] Erweiterungen --- lib/content_views/accepted_jobs_page.dart | 4 +- lib/content_views/approach_area_page.dart | 4 +- lib/content_views/assigned_jobs_page.dart | 4 +- lib/content_views/finished_jobs_page.dart | 4 +- lib/content_views/main_page.dart | 25 ++--- lib/content_views/messages_page.dart | 2 +- lib/content_views/task_list_page.dart | 12 +- lib/content_views/tour_details_page.dart | 4 +- lib/content_views/tour_list_page.dart | 4 +- lib/main.dart | 77 +++++++------ lib/model/job.dart | 6 - lib/model/job.g.dart | 6 +- lib/popups/accept_jobs_popup.dart | 105 +++++++++--------- lib/popups/infos_popup.dart | 3 +- .../tasks_popups/receipt_giver_popup.dart | 1 + lib/tools/message_service.dart | 9 +- lib/tools/mqtt_core.dart | 59 +++++----- lib/tools/util.dart | 2 + pubspec.lock | 8 ++ pubspec.yaml | 1 + 20 files changed, 182 insertions(+), 158 deletions(-) diff --git a/lib/content_views/accepted_jobs_page.dart b/lib/content_views/accepted_jobs_page.dart index 8603d38..ed9f7f2 100644 --- a/lib/content_views/accepted_jobs_page.dart +++ b/lib/content_views/accepted_jobs_page.dart @@ -142,7 +142,7 @@ class _AcceptedJobsPageState extends State { return jobWidgets; } - void _clicked(Job job) { - MessageService().publish(Constants.messageTopicPushContentView, arguments: TourListPage(job: job)); + void _clicked(Job job) async { + await MessageService().publish(Constants.messageTopicPushContentView, arguments: TourListPage(job: job)); } } diff --git a/lib/content_views/approach_area_page.dart b/lib/content_views/approach_area_page.dart index 53b6af5..5d79ffe 100644 --- a/lib/content_views/approach_area_page.dart +++ b/lib/content_views/approach_area_page.dart @@ -197,7 +197,7 @@ class _ApproachAreaPageState extends State { return columns; } - _sendZip(Rank rank, String zip) { + _sendZip(Rank rank, String zip) async { var position = MyApp.currentPosition; var setPosMessage = ZipSetPos(); @@ -212,7 +212,7 @@ class _ApproachAreaPageState extends State { setPosMessage.track!.pos!.acc = position.accuracy.toInt(); } - MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "set_zip", message: setPosMessage.toJson(), qos: MqttQos.exactlyOnce)); + await MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "set_zip", message: setPosMessage.toJson(), qos: MqttQos.exactlyOnce)); } _initMessages() { diff --git a/lib/content_views/assigned_jobs_page.dart b/lib/content_views/assigned_jobs_page.dart index b4e91d3..b35cee5 100644 --- a/lib/content_views/assigned_jobs_page.dart +++ b/lib/content_views/assigned_jobs_page.dart @@ -200,13 +200,13 @@ class _AssignedJobsPageState extends State { 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)); + await MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "reaction", message: message.toJson(), qos: MqttQos.exactlyOnce)); await MyApp.isarInstance.writeTxn(() async { await MyApp.isarInstance.jobs.delete(job.id); }); - MessageService().publish(Constants.messageTopicUpdateBadges, arguments: RefreshBadgesMessage()); + await MessageService().publish(Constants.messageTopicUpdateBadges, arguments: RefreshBadgesMessage()); _entries = await _createListEntries(); diff --git a/lib/content_views/finished_jobs_page.dart b/lib/content_views/finished_jobs_page.dart index a5a74ba..401e056 100644 --- a/lib/content_views/finished_jobs_page.dart +++ b/lib/content_views/finished_jobs_page.dart @@ -130,7 +130,7 @@ class _FinishedJobsPageState extends State { return jobWidgets; } - void _clicked(Job job) { - MessageService().publish(Constants.messageTopicPushContentView, arguments: TourListPage(job: job, readOnly: true,)); + void _clicked(Job job) async { + await MessageService().publish(Constants.messageTopicPushContentView, arguments: TourListPage(job: job, readOnly: true,)); } } diff --git a/lib/content_views/main_page.dart b/lib/content_views/main_page.dart index a59d2fd..57311bc 100644 --- a/lib/content_views/main_page.dart +++ b/lib/content_views/main_page.dart @@ -143,8 +143,8 @@ class _MainPageState extends State { child: getDefaultButton( 'Zugewiesene Aufträge', image: Image.asset('assets/zugewiesene_auftraege.png', width: 30, height: 30), - () => { - MessageService().publish(Constants.messageTopicPushContentView, arguments: const AssignedJobsPage()) + () async => { + await MessageService().publish(Constants.messageTopicPushContentView, arguments: const AssignedJobsPage()) }), ); }, @@ -176,9 +176,8 @@ class _MainPageState extends State { child: getDefaultButton( 'Angenommene Aufträge', image: Image.asset('assets/angenommene_auftraege.png', width: 30, height: 30), - () => { - MessageService() - .publish(Constants.messageTopicPushContentView, arguments: const AcceptedJobsPage()) + () async => { + await MessageService().publish(Constants.messageTopicPushContentView, arguments: const AcceptedJobsPage()) })); }, ), @@ -189,7 +188,7 @@ class _MainPageState extends State { child: getDefaultButton( 'Erledigte Aufträge', image: Image.asset('assets/erledigte_auftraege.png', width: 30, height: 30), - () => {MessageService().publish(Constants.messageTopicPushContentView, arguments: const FinishedJobsPage())}), + () async => {await MessageService().publish(Constants.messageTopicPushContentView, arguments: const FinishedJobsPage())}), ), SizedBox(height: _loggedOut == true ? 0 : 20), Visibility( @@ -217,8 +216,8 @@ class _MainPageState extends State { child: getDefaultButton( 'Nachrichten', image: Image.asset('assets/nachrichten.png', width: 30, height: 30), - () => - {MessageService().publish(Constants.messageTopicPushContentView, arguments: const MessagesPage())})); + () async => + {await MessageService().publish(Constants.messageTopicPushContentView, arguments: const MessagesPage())})); }, ), ), @@ -228,7 +227,7 @@ class _MainPageState extends State { child: getDefaultButton( 'Anfahrtsbereich', image: Image.asset('assets/anfahrtsbereich.png', width: 30, height: 30), - () => {MessageService().publish(Constants.messageTopicPushContentView, arguments: const ApproachAreaPage())}), + () async => {await MessageService().publish(Constants.messageTopicPushContentView, arguments: const ApproachAreaPage())}), ), SizedBox(height: _loggedOut == true ? 0 : 20), getDefaultButton( @@ -315,11 +314,11 @@ class _MainPageState extends State { } } - _logout(context) { + _logout(context) async { if (!_loggedOut) { showLogoutAlertDialog(context); } else { - MessageService().publish(Constants.messageTopicConnectClient, arguments: ConnectClientMessage()); + await MessageService().publish(Constants.messageTopicConnectClient, arguments: ConnectClientMessage()); _loggedOut = false; @@ -372,7 +371,7 @@ class _MainPageState extends State { ); } - _sendLogoutMessage() { + _sendLogoutMessage() async { var position = MyApp.currentPosition; var logoutMessage = LogoutMessage(); @@ -384,6 +383,6 @@ class _MainPageState extends State { logoutMessage.pos!.acc = position.accuracy.toInt(); } - MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "logout", message: logoutMessage.toJson(), qos: MqttQos.exactlyOnce)); + await MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "logout", message: logoutMessage.toJson(), qos: MqttQos.exactlyOnce)); } } diff --git a/lib/content_views/messages_page.dart b/lib/content_views/messages_page.dart index d2009bd..d427518 100644 --- a/lib/content_views/messages_page.dart +++ b/lib/content_views/messages_page.dart @@ -110,7 +110,7 @@ class _MessagesPageState extends State { await MyApp.isarInstance.messages.put(message); }); - MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "message", message: message.toJson(), qos: MqttQos.exactlyOnce)); + await MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "message", message: message.toJson(), qos: MqttQos.exactlyOnce)); _messages.insert(0, types.TextMessage( author: _user, diff --git a/lib/content_views/task_list_page.dart b/lib/content_views/task_list_page.dart index c5bfc59..29b7bcb 100644 --- a/lib/content_views/task_list_page.dart +++ b/lib/content_views/task_list_page.dart @@ -415,7 +415,7 @@ class _TaskListPageState extends State { SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]); Future.delayed(const Duration(seconds: 1), () async { - showSignaturePopup(context, taskInfo, (String? signature) { + showSignaturePopup(context, taskInfo, (String? signature) async { MyApp.suppressAcceptDialog = false; SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); @@ -432,7 +432,7 @@ class _TaskListPageState extends State { _finishTaskProcessing(taskInfo); - MessageService().publish(Constants.messageTopicPopContentView, arguments: PopContentViewMessage(count: 2)); + await MessageService().publish(Constants.messageTopicPopContentView, arguments: PopContentViewMessage(count: 2)); } }); }); @@ -467,7 +467,7 @@ class _TaskListPageState extends State { return false; } - _sendArrivedMessage(TaskInfo taskInfo) { + _sendArrivedMessage(TaskInfo taskInfo) async { var message = FinTask(); message.trId = widget.tour.id; message.task = Task(); @@ -483,7 +483,7 @@ class _TaskListPageState extends State { message.task.pos = null; } - MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "fin_task", message: message.toJson(), qos: MqttQos.exactlyOnce)); + await MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "fin_task", message: message.toJson(), qos: MqttQos.exactlyOnce)); } FinTourTask _createAnswerHeader(TaskInfo taskInfo) { @@ -527,7 +527,7 @@ class _TaskListPageState extends State { } } - MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "fin_tour", message: finTourMessage.toJson(), qos: MqttQos.exactlyOnce)); + await MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "fin_tour", message: finTourMessage.toJson(), qos: MqttQos.exactlyOnce)); widget.tour.state = Constants.stateFinishedNotConfirmed; @@ -552,7 +552,7 @@ class _TaskListPageState extends State { message.time = DateTime.now().millisecondsSinceEpoch; message.data = picture.data; - MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "pic", message: message.toJson(), qos: MqttQos.exactlyOnce)); + await MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "pic", message: message.toJson(), qos: MqttQos.exactlyOnce)); await MyApp.isarInstance.writeTxn(() async { await MyApp.isarInstance.pictures.delete(picture.id); diff --git a/lib/content_views/tour_details_page.dart b/lib/content_views/tour_details_page.dart index b064c88..6625300 100644 --- a/lib/content_views/tour_details_page.dart +++ b/lib/content_views/tour_details_page.dart @@ -142,9 +142,9 @@ class _TourDetailsPageState extends State { ), ), const SizedBox(height: 10), - getDefaultButton('Weiter', () => + getDefaultButton('Weiter', () async => { - MessageService().publish(Constants.messageTopicPushContentView, arguments: TaskListPage(job: widget.job, tour: widget.tour, readOnly: widget.readOnly)) + await MessageService().publish(Constants.messageTopicPushContentView, arguments: TaskListPage(job: widget.job, tour: widget.tour, readOnly: widget.readOnly)) }), ], ), diff --git a/lib/content_views/tour_list_page.dart b/lib/content_views/tour_list_page.dart index 1ce35c8..71c4566 100644 --- a/lib/content_views/tour_list_page.dart +++ b/lib/content_views/tour_list_page.dart @@ -208,7 +208,7 @@ class _TourListPageState extends State { return readOnly; } - void _clicked(JobTour tour, bool readOnly) { - MessageService().publish(Constants.messageTopicPushContentView, arguments: TourDetailsPage(job: widget.job, tour: tour, readOnly: readOnly)); + Future _clicked(JobTour tour, bool readOnly) async { + await MessageService().publish(Constants.messageTopicPushContentView, arguments: TourDetailsPage(job: widget.job, tour: tour, readOnly: readOnly)); } } diff --git a/lib/main.dart b/lib/main.dart index bcffa47..ef4e9cb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:isar/isar.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:shared_map/shared_map.dart'; import 'package:stadtbote/messages/pop_content_view_message.dart'; import 'package:stadtbote/model/enqueued_message.dart'; import 'package:stadtbote/model/log_entry.dart'; @@ -17,6 +18,7 @@ import 'package:stadtbote/tools/colored_safe_area.dart'; import 'package:stadtbote/model/session_status.dart'; import 'package:stadtbote/tools/connection_status.dart'; import 'package:stadtbote/tools/message_service.dart'; +import 'package:stadtbote/tools/messages/job_deleted_message.dart'; import 'package:stadtbote/tools/messages/job_received_message.dart'; import 'package:stadtbote/tools/messages/refresh_badges_message.dart'; import 'package:stadtbote/tools/mqtt_core.dart'; @@ -29,28 +31,28 @@ import 'tools/constants.dart'; import 'tools/util.dart'; Future main() async { - WidgetsFlutterBinding.ensureInitialized(); + runZonedGuarded(() async { + WidgetsFlutterBinding.ensureInitialized(); - final dir = await getApplicationDocumentsDirectory(); - MyApp.isarInstance = await Isar.open([ - SessionStatusSchema, - LocationStatusSchema, - JobSchema, - TaskInfoSchema, - MessageSchema, - EnqueuedMessageSchema, - LogEntrySchema, - RankingSchema, - PictureSchema - ], directory: dir.path, name: "database_v1"); + final dir = await getApplicationDocumentsDirectory(); + MyApp.isarInstance = await Isar.open([ + SessionStatusSchema, + LocationStatusSchema, + JobSchema, + TaskInfoSchema, + MessageSchema, + EnqueuedMessageSchema, + LogEntrySchema, + RankingSchema, + PictureSchema + ], directory: dir.path, name: "database_v1"); - await _clearLog(); + await _clearLog(); - runZonedGuarded(() {}, (error, stackTrace) { + runApp(MyApp()); + }, (error, stackTrace) { _traceUncaughtExceptions(error, stackTrace); }); - - runApp(MyApp()); } Future _clearLog() async { @@ -177,8 +179,12 @@ class _MainState extends State
with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation _animation; + Timer? _checkForNewState0JobsJobsTimer; + bool _dialogOpened = false; + late SharedMap? _jobMap; + @override void initState() { super.initState(); @@ -188,8 +194,13 @@ class _MainState extends State
with SingleTickerProviderStateMixin { _animation = Tween(begin: 0.5, end: 1.0).animate(_animationController); WidgetsBinding.instance.addPostFrameCallback((_) async { + var store1 = SharedStore('jobs'); + _jobMap = await store1.getSharedMap('m1'); + _initMessages(); + _startCheckForNewState0JobsJobsTimer(); + _currentContent = MainPage(); var headerInfo = await _currentContent.getHeaderInfo(context); @@ -366,8 +377,15 @@ class _MainState extends State
with SingleTickerProviderStateMixin { MessageService().subscribe(Constants.messageTopicConnectionStatusChanged, this, (ConnectionStatus connectionStatus) { _connectionStatusChanged(connectionStatus); }); - MessageService().subscribe(Constants.messageTopicJobReceived, this, (JobReceivedMessage jobReceivedMessage) { - _jobReceived(jobReceivedMessage.job); + + MessageService().subscribe(Constants.messageTopicJobReceived, this, (JobReceivedMessage jobReceivedMessage) async { + if (jobReceivedMessage.job.state == 0) { + await _jobMap!.put(jobReceivedMessage.job.id, jobReceivedMessage.job); + } + }); + + MessageService().subscribe(Constants.messageTopicJobDeleted, this, (JobDeletedMessage jobDeletedMessage) async { + await _jobMap!.remove(jobDeletedMessage.id); }); } @@ -457,19 +475,14 @@ class _MainState extends State
with SingleTickerProviderStateMixin { _connectionLostValueNotifier.value = newConnectionStatus.status == ConnectionStatusEnum.notConnected; } - void _jobReceived(Job job) async { - if (!MyApp.suppressAcceptDialog) { - if (job.state == 0) { - if (!_dialogOpened) { - _dialogOpened = true; - - await showAcceptJobsPopup(context, await MyApp.isarInstance.jobs.filter().stateEqualTo(0).findAll(), () { - _dialogOpened = false; - - MessageService().publish(Constants.messageTopicUpdateBadges, arguments: RefreshBadgesMessage()); - }); - } + void _startCheckForNewState0JobsJobsTimer() { + _checkForNewState0JobsJobsTimer ??= Timer.periodic(const Duration(seconds: 5), (Timer t) async { + if (await _jobMap!.length() > 0 && !_dialogOpened) { + _dialogOpened = true; + await showAcceptJobsPopup(context, _jobMap!.sharedReference(), () async { + _dialogOpened = false; + }); } - } + }); } } diff --git a/lib/model/job.dart b/lib/model/job.dart index 2b3351e..3518eee 100644 --- a/lib/model/job.dart +++ b/lib/model/job.dart @@ -31,12 +31,6 @@ class Job { @JsonKey(name: 'tours') late List tours; - @Ignore() - late DateTime? firstDisplayTime = null; - - @Ignore() - late int enableDelay; - Job(); factory Job.fromJson(Map json) => _$JobFromJson(json); diff --git a/lib/model/job.g.dart b/lib/model/job.g.dart index e04ea3b..6714833 100644 --- a/lib/model/job.g.dart +++ b/lib/model/job.g.dart @@ -1558,10 +1558,7 @@ Job _$JobFromJson(Map json) => Job() ..delayOrderTime = json['delayOrderTime'] as int ..tours = (json['tours'] as List) .map((e) => JobTour.fromJson(e as Map)) - .toList() - ..firstDisplayTime = json['firstDisplayTime'] == null - ? null - : DateTime.parse(json['firstDisplayTime'] as String); + .toList(); Map _$JobToJson(Job instance) => { 'id': instance.id, @@ -1575,5 +1572,4 @@ Map _$JobToJson(Job instance) => { 'rem': instance.rem, 'delayOrderTime': instance.delayOrderTime, 'tours': instance.tours, - 'firstDisplayTime': instance.firstDisplayTime?.toIso8601String(), }; diff --git a/lib/popups/accept_jobs_popup.dart b/lib/popups/accept_jobs_popup.dart index e65e86a..0183bfe 100644 --- a/lib/popups/accept_jobs_popup.dart +++ b/lib/popups/accept_jobs_popup.dart @@ -1,31 +1,34 @@ +import 'dart:async'; import 'dart:math'; import 'package:expandable/expandable.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:mqtt_client/mqtt_client.dart'; +import 'package:shared_map/shared_map.dart'; import 'package:stadtbote/main.dart'; import 'package:stadtbote/protocoll_messages/accept_job_message.dart'; import 'package:stadtbote/model/job.dart'; import 'package:stadtbote/protocoll_messages/fin_task_message.dart'; import 'package:stadtbote/tools/constants.dart'; import 'package:stadtbote/tools/message_service.dart'; -import 'package:stadtbote/tools/messages/job_deleted_message.dart'; -import 'package:stadtbote/tools/messages/job_received_message.dart'; import 'package:stadtbote/tools/messages/refresh_badges_message.dart'; import 'package:stadtbote/tools/util.dart'; import 'package:badges/badges.dart' as badges; import '../tools/messages/publish_payload_message.dart'; -Future showAcceptJobsPopup(BuildContext context, List jobs, Function() onFinished) async { +Timer? _checkForNewJobsTimer; + +Map _firstDelayTimeMap = {}; + +Future showAcceptJobsPopup(BuildContext context, SharedMapReference mapReference, Function() onFinished) async { await showDialog( context: context, builder: (BuildContext context) { return WillPopScope( onWillPop: () async { - MessageService().unsubscribe(Constants.messageTopicJobDeleted, jobs); - MessageService().unsubscribe(Constants.messageTopicJobReceived, jobs); + _stopTimer(); Navigator.of(context).pop(); @@ -38,34 +41,7 @@ Future showAcceptJobsPopup(BuildContext context, List jobs, Function( shadowColor: Colors.transparent, backgroundColor: Colors.white, content: StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - if (jobs.isEmpty) { - Navigator.of(context).pop(); - - onFinished(); - } - - MessageService().unsubscribe(Constants.messageTopicJobDeleted, jobs); - MessageService().unsubscribe(Constants.messageTopicJobReceived, jobs); - - MessageService().subscribe(Constants.messageTopicJobDeleted, jobs, (jobDeletedMessage) async { - jobs.removeWhere((value) => value.id == jobDeletedMessage.id); - - if (context.mounted) { - setState(() {}); - } - }); - - MessageService().subscribe(Constants.messageTopicJobReceived, jobs, (jobMessage) async { - jobs.removeWhere((value) => value.id == jobMessage.job.id); - - if (jobMessage.job.state == 0) { - jobs.add(jobMessage.job); - - if (context.mounted) { - setState(() {}); - } - } - }); + _startTimer(mapReference, setState); return SizedBox( key: GlobalKey(), @@ -75,7 +51,7 @@ Future showAcceptJobsPopup(BuildContext context, List jobs, Function( height: 100, padding: const EdgeInsets.all(5), child: FutureBuilder>( - future: fetchJobWidgets(jobs), + future: _fetchJobWidgets(mapReference), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Util.getWaitWidget(context); @@ -91,19 +67,41 @@ Future showAcceptJobsPopup(BuildContext context, List jobs, Function( }); } -Future> fetchJobWidgets(List jobs) async { +void _startTimer(SharedMapReference mapReference, StateSetter setState) { + _checkForNewJobsTimer ??= Timer.periodic(const Duration(seconds: 5), (Timer t) async { + var jobMap = SharedMap.fromSharedReference(mapReference); + + if (await jobMap.length() > 0) { + setState(() { }); + } + }); +} + +void _stopTimer() { + if (_checkForNewJobsTimer != null) { + _checkForNewJobsTimer!.cancel(); + + _checkForNewJobsTimer = null; + } +} + + +Future> _fetchJobWidgets(SharedMapReference mapReference) 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(); + var jobMap = SharedMap.fromSharedReference(mapReference); - await MyApp.isarInstance.jobs.put(job); - }); + var entries = await jobMap.entries(); + for (var jobEntry in entries) { + var job = jobEntry.value; + + int enableDelay = 3; //job.delayOrderTime; + if (!_firstDelayTimeMap.containsKey(job.id)) { + _firstDelayTimeMap[job.id] = DateTime.now(); } else { - enableDelay = max(0, DateTime.now().difference(job.firstDisplayTime!).inSeconds); + DateTime firstDisplayTime = _firstDelayTimeMap[job.id]!; + int diffInSeconds = DateTime.now().difference(firstDisplayTime).inSeconds; + enableDelay = min(0, diffInSeconds); } widgets.add(StatefulBuilder(builder: (BuildContext context, StateSetter setState) { @@ -200,21 +198,21 @@ Future> fetchJobWidgets(List jobs) async { Row(children: [ Expanded( child: getDefaultButton(!job.global ? 'Ablehnen' : 'Ignorieren', () { - _sendAnswer(job, !job.global ? 2 : 3); + setState(() { + _sendAnswer(job, !job.global ? 2 : 3); - jobs.removeWhere((value) => value.id == job.id); - - setState(() {}); + //jobs.removeWhere((value) => value.id == job.id); + }); }), ), const SizedBox(width: 20), Expanded( child: getDefaultButton('Annehmen', enabledTimerActive: enableDelay > 0, enabledTimerDelay: enableDelay, () { - _sendAnswer(job, 1); + setState(() { + _sendAnswer(job, 1); - jobs.removeWhere((value) => value.id == job.id); - - setState(() {}); + //jobs.removeWhere((value) => value.id == job.id); + }); }), ) ]) @@ -245,12 +243,11 @@ _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)); + await MessageService().publish(Constants.messageTopicPublishPayload, arguments: PublishPayloadMessage(topic: "reaction", message: message.toJson(), qos: MqttQos.exactlyOnce)); await MyApp.isarInstance.writeTxn(() async { await MyApp.isarInstance.jobs.delete(job.id); }); - MessageService().publish(Constants.messageTopicUpdateBadges, arguments: RefreshBadgesMessage()); + await MessageService().publish(Constants.messageTopicUpdateBadges, arguments: RefreshBadgesMessage()); } diff --git a/lib/popups/infos_popup.dart b/lib/popups/infos_popup.dart index 917f346..8b2ea77 100644 --- a/lib/popups/infos_popup.dart +++ b/lib/popups/infos_popup.dart @@ -14,8 +14,7 @@ void showInfosPopup(BuildContext context, SessionStatus? status) async { late String connectedString = "Bitte warten..."; Future.delayed(const Duration(seconds: 2), () async { - MessageService() - .publish(Constants.messageTopicRequestConnectionStatus, arguments: RequestConnectionStatusMessage()); + await MessageService().publish(Constants.messageTopicRequestConnectionStatus, arguments: RequestConnectionStatusMessage()); }); void unsubscribeMessages() { diff --git a/lib/popups/tasks_popups/receipt_giver_popup.dart b/lib/popups/tasks_popups/receipt_giver_popup.dart index b6b9a2c..e4bcc00 100644 --- a/lib/popups/tasks_popups/receipt_giver_popup.dart +++ b/lib/popups/tasks_popups/receipt_giver_popup.dart @@ -42,6 +42,7 @@ showReceiptGiverPopup(BuildContext context, TaskInfo taskInfo, Function(String) textInputType: TextInputType.text, formatter: [FilteringTextInputFormatter.singleLineFormatter], controller: textEditingController, + maxLength: 50, autofocus: true), const SizedBox(height: 20), getDefaultButton('Übernehmen', () { diff --git a/lib/tools/message_service.dart b/lib/tools/message_service.dart index e060fd6..ec1bb34 100644 --- a/lib/tools/message_service.dart +++ b/lib/tools/message_service.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + typedef EventHandler = void Function(T args); class _MapEntry { @@ -59,13 +61,18 @@ class MessageService { } } - publish(String topic, {T? arguments}) { + publish(String topic, {T? arguments}) async { + Completer completer = Completer(); var entries = _callbackMaps[topic]; if (entries != null) { for (var entry in entries) { entry.handler(arguments); } + + completer.complete(); } + + await completer.future; } } diff --git a/lib/tools/mqtt_core.dart b/lib/tools/mqtt_core.dart index 4b55c56..0839980 100644 --- a/lib/tools/mqtt_core.dart +++ b/lib/tools/mqtt_core.dart @@ -4,6 +4,7 @@ import 'dart:math'; import 'package:archive/archive.dart'; import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:isar/isar.dart'; import 'package:mqtt_client/mqtt_client.dart'; @@ -79,8 +80,8 @@ class MqttCore { _processBootstrap(); } - _disconnectMqttClient() { - MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.notConnected)); + _disconnectMqttClient() async { + await MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.notConnected)); _disconnectInProgress = true; @@ -93,7 +94,6 @@ class MqttCore { } connectionState = ConnectionStateEnum.notConnected; - } _connectToBroker() async { @@ -128,19 +128,23 @@ class MqttCore { } else { Util.logInfo('Verbindung zum MQTT-Broker fehlgeschlagen!'); - MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.notConnected)); + await MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.notConnected)); } } catch (ex) { Util.logInfo('Verbindung zum MQTT-Broker fehlgeschlagen!: $ex'); - MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.notConnected)); + await MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.notConnected)); } } bool _publishText(String topic, String text, MqttQos qos, {bool enqueueOnError = true}) { try { var buffer = Uint8Buffer(); - buffer.addAll(GZipEncoder().encode(const Utf8Encoder().convert(text)) as Iterable); + if (kDebugMode) { + buffer.addAll(const Utf8Encoder().convert(text)); + } else { + buffer.addAll(GZipEncoder().encode(const Utf8Encoder().convert(text)) as Iterable); + } var messageTopic = "$_toServerTopic$_clientId/$topic"; @@ -174,8 +178,12 @@ class MqttCore { Util.logInfo('message received\ntopic: $topic\n'); Util.logInfo("size in bytes: ${message.payload.message.length.toString()}"); - var messageBytes = message.payload.message; - String payload = utf8.decode(GZipDecoder().decodeBytes(messageBytes)); + String payload; + if (kDebugMode) { + payload = utf8.decode(message.payload.message); + } else { + payload = utf8.decode(GZipDecoder().decodeBytes(message.payload.message)); + } Util.logInfo('$topic\nmessage: $payload'); @@ -244,7 +252,7 @@ class MqttCore { Util.logInfo('subscribed to topic: $fromServerTopic'); - MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.connected)); + await MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.connected)); } void _sessionMessageReceived(payloadData) async { @@ -261,8 +269,7 @@ class MqttCore { await MyApp.isarInstance.sessionStatus.put(sessionStatusMessage); }); - MessageService().publish(Constants.messageTopicSessionStatusInfoUpdate, - arguments: SessionStatusInfoUpdate(status: sessionStatusMessage)); + await MessageService().publish(Constants.messageTopicSessionStatusInfoUpdate, arguments: SessionStatusInfoUpdate(status: sessionStatusMessage)); _startTrackTimer(); } @@ -275,7 +282,7 @@ class MqttCore { await MyApp.isarInstance.status.put(statusMessage); }); - MessageService().publish(Constants.messageTopicStatusInfoUpdated, arguments: StatusInfoUpdate(status: statusMessage)); + await MessageService().publish(Constants.messageTopicStatusInfoUpdated, arguments: StatusInfoUpdate(status: statusMessage)); } void _locationStatusMessageReceived(payloadData) async { @@ -286,7 +293,7 @@ class MqttCore { await MyApp.isarInstance.locationStatus.put(locationStatus); }); - MessageService().publish(Constants.messageTopicLocationStatusUpdated, arguments: LocationStatusUpdate(status: locationStatus)); + await MessageService().publish(Constants.messageTopicLocationStatusUpdated, arguments: LocationStatusUpdate(status: locationStatus)); } void _rankingMessageReceived(payloadData) async { @@ -297,7 +304,7 @@ class MqttCore { await MyApp.isarInstance.rankings.put(ranking); }); - MessageService().publish(Constants.messageTopicRankingUpdated, arguments: RankingUpdated()); + await MessageService().publish(Constants.messageTopicRankingUpdated, arguments: RankingUpdated()); } void _jobMessageReceived(payloadData) async { @@ -318,7 +325,7 @@ class MqttCore { _acknowlegdeJob(newJob); try { - MessageService().publish(Constants.messageTopicJobReceived, arguments: JobReceivedMessage(job: newJob)); + await MessageService().publish(Constants.messageTopicJobReceived, arguments: JobReceivedMessage(job: newJob)); } catch (ex) { Util.logException("publish messageTopicJobReceived failed!"); } @@ -333,7 +340,7 @@ class MqttCore { await MyApp.isarInstance.jobs.delete(jobDelMessage.id); }); - MessageService().publish(Constants.messageTopicJobDeleted, arguments: JobDeletedMessage(id: jobDelMessage.id)); + await MessageService().publish(Constants.messageTopicJobDeleted, arguments: JobDeletedMessage(id: jobDelMessage.id)); } } @@ -352,7 +359,7 @@ class MqttCore { await Util.playMp3('asbeep.mp3'); - MessageService().publish(Constants.messageTopicMessageReceived, arguments: MessageReceivedMessage(message: newMessage)); + await MessageService().publish(Constants.messageTopicMessageReceived, arguments: MessageReceivedMessage(message: newMessage)); } void _messageUploadDeviceLogReceived(payloadData) async { @@ -394,24 +401,24 @@ class MqttCore { } } - void _clientDisconnected() { + Future _clientDisconnected() async { Util.logInfo("_clientDisconnected called"); connectionState = ConnectionStateEnum.notConnected; _cancelTrackTimer(); - MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.notConnected)); + await MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.notConnected)); } - void _clientConnected() { + Future _clientConnected() async { Util.logInfo("_clientConnected called"); connectionState = ConnectionStateEnum.connected; _initSession(); - MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.connected)); + await MessageService().publish(Constants.messageTopicConnectionStatusChanged, arguments: ConnectionStatus(status: ConnectionStatusEnum.connected)); } void _processBootstrap() async { @@ -446,8 +453,8 @@ class MqttCore { var bootstrapResponsePayload = BootstrapResponse.fromJson(jsonDecode(responseText)); var brokerUriAndPortArray = bootstrapResponsePayload.broker.replaceFirst("mqtt://", "").split(":"); - _brokerUri = brokerUriAndPortArray[0]; - _brokerPort = int.parse(brokerUriAndPortArray[1]); + _brokerUri = "192.168.180.27"; //brokerUriAndPortArray[0]; + _brokerPort = 1883; //int.parse(brokerUriAndPortArray[1]); _clientId = bootstrapResponsePayload.id; _fromServerTopic = bootstrapResponsePayload.fromServer; _toServerTopic = bootstrapResponsePayload.toServer; @@ -464,6 +471,7 @@ class MqttCore { var startSessionMessage = StartSessionMessage(); var jsonMap = startSessionMessage.toJson(); var json = jsonEncode(jsonMap); + _publishText("session", json, MqttQos.exactlyOnce); } @@ -559,7 +567,7 @@ class MqttCore { }); MessageService().subscribe(Constants.messageTopicRequestConnectionStatus, this, - (RequestConnectionStatusMessage requestConnectionStatusMessage) { + (RequestConnectionStatusMessage requestConnectionStatusMessage) async { bool connected = false; if (_mqttClient != null && _mqttClient!.connectionStatus != null) { if (_mqttClient!.connectionStatus!.state == MqttConnectionState.connected) { @@ -567,8 +575,7 @@ class MqttCore { } } - MessageService().publish(Constants.messageTopicCurrentConnectionStatus, - arguments: CurrentConnectionStatusMessage(state: connected)); + await MessageService().publish(Constants.messageTopicCurrentConnectionStatus, arguments: CurrentConnectionStatusMessage(state: connected)); }); } } diff --git a/lib/tools/util.dart b/lib/tools/util.dart index b20e1e0..640eee7 100644 --- a/lib/tools/util.dart +++ b/lib/tools/util.dart @@ -152,6 +152,7 @@ class Util { FocusNode? focusNode, IconButton? iconButton, int? maxLines = 1, + int? maxLength = 100, TextInputType? textInputType, List? formatter, Function()? onTap, @@ -172,6 +173,7 @@ class Util { onTap: onTap, keyboardType: textInputType, inputFormatters: formatter, + maxLength: maxLength, autofocus: (autofocus != null) ? autofocus : false, decoration: InputDecoration( hintText: hint, diff --git a/pubspec.lock b/pubspec.lock index 77e6913..040a39b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1216,6 +1216,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + shared_map: + dependency: "direct main" + description: + name: shared_map + sha256: "42608f6958d490174e172e75e6c655dc6b6cd3ce005801daa3ef5a2597470968" + url: "https://pub.dev" + source: hosted + version: "1.1.9" shared_preferences: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d0ac3e7..84acefa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -72,6 +72,7 @@ dependencies: device_info_plus: ^9.1.2 is_lock_screen: ^2.0.0 flutter_local_notifications: ^17.1.2 + shared_map: ^1.1.9 dev_dependencies: flutter_test: