import 'package:dart_mq/dart_mq.dart'; import 'package:flutter/material.dart'; import 'package:stadtbote/content_views/accepted_jobs_page.dart'; import 'package:stadtbote/content_views/approach_area_page.dart'; import 'package:stadtbote/objectbox.g.dart'; import 'package:stadtbote/content_views/assigned_jobs_page.dart'; import 'package:stadtbote/content_views/finished_jobs_page.dart'; import 'package:stadtbote/content_views/messages_page.dart'; import 'package:stadtbote/content_views/set_desired_zip_page.dart'; import 'package:stadtbote/content_views/utils/content_view_widget.dart'; import 'package:stadtbote/main.dart'; import 'package:stadtbote/model/job.dart'; import 'package:stadtbote/model/location_status.dart'; import 'package:stadtbote/model/chat_message.dart'; import 'package:stadtbote/model/session_status.dart'; import 'package:stadtbote/network_messages/logout_message.dart'; import 'package:stadtbote/popups/infos_popup.dart'; import 'package:stadtbote/tools/app_localizations.dart'; import 'package:stadtbote/tools/constants.dart'; import 'package:stadtbote/tools/messages/job_received_message.dart'; import 'package:stadtbote/tools/messages/publish_payload_message.dart'; import 'package:stadtbote/tools/messages/connect_client_message.dart'; import 'package:stadtbote/tools/messages/disconnect_client_message.dart'; import 'package:badges/badges.dart' as badges; import 'package:stadtbote/tools/util.dart'; class MainPage extends ContentViewWidget { const MainPage({super.key}); @override State createState() => _MainPageState(); @override Future getHeaderWidget(BuildContext context) async { await Future.delayed(const Duration(microseconds: 0)); return _HeaderWidget(); } } class _MainPageState extends State with ConsumerMixin, ProducerMixin { final ValueNotifier _assignedJobsValueNotifier = ValueNotifier(0); final ValueNotifier _acceptedJobsValueNotifier = ValueNotifier(0); final ValueNotifier _messagesValueNotifier = ValueNotifier(0); late bool _loggedOut = false; late bool _showBWVButton = false; @override void initState() { super.initState(); _showBWVButton = Util.preferencesGetBool(Constants.prefsBWVEnabled, false); _refreshBadges(); _initMessages(); // Listener für Sprachänderungen AppLocalizations().languageNotifier.addListener(_onLanguageChanged); } void _onLanguageChanged() { if (mounted) { setState(() { // UI wird neu aufgebaut mit neuer Sprache }); } } @override void dispose() { // Unsubscribe from all message topics unsubscribe(queueId: Constants.messageTopicRemoveJob); unsubscribe(queueId: Constants.messageTopicJobReceived); unsubscribe(queueId: Constants.messageTopicMessageReceived); unsubscribe(queueId: Constants.messageTopicJobDeleted); unsubscribe(queueId: Constants.messageTopicUpdateBadges); unsubscribe(queueId: Constants.messageTopicBWVEnabled); // Entferne Listener für Sprachänderungen AppLocalizations().languageNotifier.removeListener(_onLanguageChanged); super.dispose(); } @override Widget build(BuildContext context) { return Stack( children: [ Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Visibility( visible: !_loggedOut, child: ValueListenableBuilder( valueListenable: _assignedJobsValueNotifier, builder: (context, value, child) { return badges.Badge( showBadge: value > 0, position: badges.BadgePosition.topEnd(top: -12, end: -5), badgeContent: Text( value.toString(), style: const TextStyle( color: Colors.white, fontSize: 13.0, ), ), badgeStyle: badges.BadgeStyle( badgeColor: Colors.red, padding: const EdgeInsets.all(10), borderRadius: BorderRadius.circular(4), borderSide: const BorderSide(color: Colors.white, width: 0), elevation: 0, ), child: getDefaultButton( 'assigned_jobs'.tr, image: Image.asset('assets/zugewiesene_auftraege.png', width: 30, height: 30), () async => { sendMessage( routingKey: Constants.messageTopicPushContentView, payload: const AssignedJobsPage(), ) }), ); }, ), ), SizedBox(height: _loggedOut == true ? 0 : 20), Visibility( visible: !_loggedOut, child: ValueListenableBuilder( valueListenable: _acceptedJobsValueNotifier, builder: (context, value, child) { return badges.Badge( showBadge: value > 0, position: badges.BadgePosition.topEnd(top: -12, end: -5), badgeContent: Text( value.toString(), style: const TextStyle( color: Colors.white, fontSize: 13.0, ), ), badgeStyle: badges.BadgeStyle( badgeColor: Colors.red, padding: const EdgeInsets.all(10), borderRadius: BorderRadius.circular(4), borderSide: const BorderSide(color: Colors.white, width: 0), elevation: 0, ), child: getDefaultButton( 'accepted_jobs'.tr, image: Image.asset( 'assets/angenommene_auftraege.png', width: 30, height: 30), () async => { sendMessage( routingKey: Constants.messageTopicPushContentView, payload: const AcceptedJobsPage(), ) })); }, ), ), SizedBox(height: _loggedOut == true ? 0 : 20), Visibility( visible: !_loggedOut, child: getDefaultButton( 'finished_jobs'.tr, image: Image.asset('assets/erledigte_auftraege.png', width: 30, height: 30), () async => { sendMessage( routingKey: Constants.messageTopicPushContentView, payload: const FinishedJobsPage(), ) }), ), SizedBox(height: _loggedOut == true ? 0 : 20), Visibility( visible: !_loggedOut, child: ValueListenableBuilder( valueListenable: _messagesValueNotifier, builder: (context, value, child) { return badges.Badge( showBadge: value > 0, position: badges.BadgePosition.topEnd(top: -12, end: -5), badgeContent: Text( value.toString(), style: const TextStyle( color: Colors.white, fontSize: 13.0, ), ), badgeStyle: badges.BadgeStyle( badgeColor: Colors.red, padding: const EdgeInsets.all(10), borderRadius: BorderRadius.circular(4), borderSide: const BorderSide(color: Colors.white, width: 0), elevation: 0, ), child: getDefaultButton( 'messages'.tr, image: Image.asset('assets/nachrichten.png', width: 30, height: 30), () async => { sendMessage( routingKey: Constants.messageTopicPushContentView, payload: const MessagesPage(), ) })); }, ), ), SizedBox(height: _loggedOut == true ? 0 : 20), Visibility( visible: !_loggedOut, child: getDefaultButton( 'approach_area'.tr, image: Image.asset('assets/anfahrtsbereich.png', width: 30, height: 30), () async => { sendMessage( routingKey: Constants.messageTopicPushContentView, payload: const ApproachAreaPage(), ) }), ), SizedBox(height: _loggedOut == true ? 0 : 20), Visibility( visible: !_loggedOut && _showBWVButton, child: getDefaultButton( 'desired_zip'.tr, image: Image.asset('assets/wunsch_plz.png', width: 30, height: 30), () async => { sendMessage( routingKey: Constants.messageTopicPushContentView, payload: const SetDesiredZipPage(), ) }), ), SizedBox(height: _loggedOut == true ? 0 : 20), getDefaultButton(_loggedOut ? 'login'.tr : 'logout'.tr, image: Image.asset( _loggedOut ? 'assets/anmelden.png' : 'assets/abmelden.png', width: 30, height: 30), () async { _logout(context); }), Expanded( child: Container( decoration: BoxDecoration( color: Colors.transparent, border: Border.all( color: Colors.transparent, width: 2.0, ), ), ), ), Image.asset( 'assets/sb_logo.png', width: 100, height: 100, ), ], ), ), ], ); } _initMessages() { subscribe( queueId: Constants.messageTopicRemoveJob, callback: (message) { _refreshBadges(); }, ); subscribe( queueId: Constants.messageTopicJobReceived, callback: (message) { _jobReceived((message.payload as JobReceivedMessage).job); }, ); subscribe( queueId: Constants.messageTopicMessageReceived, callback: (message) { _refreshBadges(); }, ); subscribe( queueId: Constants.messageTopicJobDeleted, callback: (message) { _refreshBadges(); }, ); subscribe( queueId: Constants.messageTopicUpdateBadges, callback: (message) { _refreshBadges(); }, ); subscribe( queueId: Constants.messageTopicBWVEnabled, callback: (message) { if (mounted) { setState(() { _showBWVButton = Util.preferencesGetBool(Constants.prefsBWVEnabled, false); }); } }); } void _refreshBadges() async { final jobBox = MyApp.objectBoxStore.box(); final assignedQuery = jobBox.query(Job_.state.equals(0)).build(); _assignedJobsValueNotifier.value = assignedQuery.count(); assignedQuery.close(); final acceptedQuery = jobBox.query(Job_.state.equals(1)).build(); _acceptedJobsValueNotifier.value = acceptedQuery.count(); acceptedQuery.close(); final messageBox = MyApp.objectBoxStore.box(); final messageQuery = messageBox.query(ChatMessage_.read.equals(false)).build(); _messagesValueNotifier.value = messageQuery.count(); messageQuery.close(); } void _jobReceived(Job job) { _refreshBadges(); } _logout(context) async { if (!_loggedOut) { showLogoutAlertDialog(context); } else { sendMessage( routingKey: Constants.messageTopicConnectClient, payload: ConnectClientMessage(), ); _loggedOut = false; if (mounted) { setState(() {}); } } } showLogoutAlertDialog(BuildContext context) async { Widget yesButton = TextButton( child: Text('yes'.tr), onPressed: () async { Navigator.of(context).pop(); showMessageDialog( context, 'information'.tr, 'logging_out'.tr); _sendLogoutMessage(); _loggedOut = true; Future.delayed(const Duration(seconds: 2), () async { sendMessage( routingKey: Constants.messageTopicDisconnectClient, payload: DisconnectClientMessage(), ); Future.delayed(const Duration(seconds: 3), () async { sendMessage( routingKey: Constants.messageTopicDisconnectClient, payload: DisconnectClientMessage(), ); Navigator.of(MyApp.appNavigatorKey.currentContext!).pop(); if (mounted) { setState(() {}); } }); }); }, ); Widget noButton = TextButton( child: Text('no'.tr), onPressed: () { Navigator.of(context).pop(); }, ); // set up the AlertDialog AlertDialog alert = AlertDialog( title: Text('warning'.tr), content: Text('logout_confirm'.tr), actions: [ yesButton, noButton, ], ); // show the dialog showDialog( context: context, builder: (BuildContext context) { return alert; }, ); } _sendLogoutMessage() async { var position = await MyApp.getCurrentPosition(); var logoutMessage = LogoutMessage(); if (position != null) { logoutMessage.pos = LogoutMessagePosition(); logoutMessage.pos!.time = Util.toUnixSecondsUtc(position.timestamp); logoutMessage.pos!.lat = position.latitude; logoutMessage.pos!.long = position.longitude; logoutMessage.pos!.acc = position.accuracy.toInt(); } sendMessage( routingKey: Constants.messageTopicPublishPayload, payload: PublishPayloadMessage( topic: "logout", message: logoutMessage.toJson(), acknowledgeRequired: false), ); } } class _HeaderWidget extends StatefulWidget { @override State<_HeaderWidget> createState() => _HeaderWidgetState(); } class _HeaderWidgetState extends State<_HeaderWidget> with ConsumerMixin { final ValueNotifier _zipValueNotifier = ValueNotifier("-----"); final ValueNotifier _zipRealValueNotifier = ValueNotifier("-----"); final ValueNotifier _sidValueNotifier = ValueNotifier("-----"); @override void initState() { super.initState(); _updateHeader(); _initMessages(); // Listener für Sprachänderungen AppLocalizations().languageNotifier.addListener(_onLanguageChanged); } void _onLanguageChanged() { if (mounted) { setState(() { // UI wird neu aufgebaut mit neuer Sprache }); } } @override void dispose() { // Unsubscribe from all message topics unsubscribe(queueId: Constants.messageTopicSessionStatusInfoUpdate); unsubscribe(queueId: Constants.messageTopicLocationStatusUpdated); // Entferne Listener für Sprachänderungen AppLocalizations().languageNotifier.removeListener(_onLanguageChanged); super.dispose(); } @override Widget build(BuildContext context) { var cellWidth = (MediaQuery.of(context).size.width - 60 - 60 - 25) / 2; return Padding( padding: const EdgeInsets.all(8), child: Stack( alignment: Alignment.center, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Visibility( visible: false, child: Container( width: 60, height: 60, decoration: const BoxDecoration( shape: BoxShape.circle, color: Colors.white, ), child: FittedBox( child: GestureDetector( onTap: () async {}, child: const CircleAvatar( radius: 10, backgroundColor: Constants.colorPrimary, child: Padding( padding: EdgeInsets.all(1.0), child: Icon(Icons.info_outline, size: 20.0, color: Colors.white)), ), ), ), ), ), Visibility( visible: true, child: Container( width: 60, height: 60, decoration: const BoxDecoration( shape: BoxShape.circle, color: Colors.red, ), child: FittedBox( child: GestureDetector( onTap: () async { final box = MyApp.objectBoxStore.box(); final sessions = box.getAll(); var sessionStatus = sessions.isNotEmpty ? sessions.first : null; showInfosPopup(sessionStatus); }, child: const CircleAvatar( backgroundColor: Constants.colorPrimary, radius: 10, child: Padding( padding: EdgeInsets.all(1.0), child: Icon(Icons.info_outline, size: 20.0, color: Colors.white))), ), ), ), ), ], ), SizedBox( height: 100, width: cellWidth * 2, child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment .spaceBetween, // Verteilt die Texte gleichmäßig in der Zeile children: [ SizedBox( width: cellWidth, child: Text( 'message'.tr, textAlign: TextAlign.center, style: const TextStyle(fontSize: 11, color: Colors.white), ), ), SizedBox( width: cellWidth, child: Text( 'location'.tr, textAlign: TextAlign.center, style: const TextStyle(fontSize: 11, color: Colors.white), ), ), ], ), Row( mainAxisAlignment: MainAxisAlignment .spaceBetween, // Verteilt die Texte gleichmäßig in der Zeile children: [ SizedBox( width: cellWidth, child: ValueListenableBuilder( valueListenable: _zipValueNotifier, builder: (context, value, child) { return Text( textAlign: TextAlign.center, value, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white), ); }, )), SizedBox( width: cellWidth, child: ValueListenableBuilder( valueListenable: _zipRealValueNotifier, builder: (context, value, child) { return Text( textAlign: TextAlign.center, value, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white), ); }, ), ), ], ), Column( children: [ Text( 'courier_id'.tr, textAlign: TextAlign.center, style: const TextStyle(fontSize: 11, color: Colors.white), ), ValueListenableBuilder( valueListenable: _sidValueNotifier, builder: (context, value, child) { return Text( textAlign: TextAlign.center, value, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white), ); }, ), ], ) ], ), ), ], ), ); } _initMessages() { subscribe( queueId: Constants.messageTopicSessionStatusInfoUpdate, callback: (message) { _updateHeader(); }, ); subscribe( queueId: Constants.messageTopicLocationStatusUpdated, callback: (message) { _updateHeader(); }, ); } _updateHeader() async { final locationBox = MyApp.objectBoxStore.box(); final locations = locationBox.getAll(); var status = locations.isNotEmpty ? locations.first : null; final sessionBox = MyApp.objectBoxStore.box(); final sessions = sessionBox.getAll(); var sessionStatus = sessions.isNotEmpty ? sessions.first : null; if (status != null) { _zipValueNotifier.value = status.zip; _zipRealValueNotifier.value = status.zipReal; } else if (sessionStatus != null) { _zipValueNotifier.value = sessionStatus.locZip; _zipRealValueNotifier.value = '---'; } else { _zipValueNotifier.value = '---'; _zipRealValueNotifier.value = '---'; } if (sessionStatus != null) { _sidValueNotifier.value = sessionStatus.sid; } /* rightButtonImage: Util.fetchFutureIcon(const Icon(Icons.info_outline, size: 20.0, color: Colors.white)), rightButtonOnPressed: () async { final box = MyApp.objectBoxStore.box(); final sessions = box.getAll(); showInfosPopup(sessions.isNotEmpty ? sessions.first : null); }); */ } }