Merge branch 'main' into new_appserver

# Conflicts:
#	lib/content_views/approach_area_page.dart
#	lib/content_views/assigned_jobs_page.dart
#	lib/content_views/main_page.dart
#	lib/content_views/task_list_page.dart
#	lib/main.dart
#	lib/popups/accept_jobs_popup.dart
#	lib/protocoll_messages/logout_message.dart
#	lib/protocoll_messages/logout_message.g.dart
#	lib/tools/constants.dart
#	lib/tools/mqtt_core.dart
#	pubspec.lock
#	pubspec.yaml
This commit is contained in:
Sven committed 2024-03-26 12:56:04 +01:00
commit 880cdd65ff
74 files changed
+2463 -441

No files matched your search

+174 -124
View File
@@ -1,5 +1,3 @@
import 'dart:convert';
import 'package:expandable/expandable.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
@@ -8,8 +6,6 @@ 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/refresh_badges_message.dart';
import 'package:stadtbote/tools/mqtt_core.dart';
import 'package:stadtbote/tools/util.dart';
import 'package:badges/badges.dart' as badges;
@@ -20,138 +16,192 @@ Future<void> showAcceptJobsPopup(BuildContext context, List<Job> jobs) async {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
shadowColor: Colors.transparent,
backgroundColor: Colors.white,
content: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
if (jobsSet.isEmpty) {
Navigator.of(context).pop();
}
return WillPopScope(
onWillPop: () async {
MessageService().unsubscribe(Constants.messageTopicJobDeleted, jobsMap);
MessageService().unsubscribe(Constants.messageTopicJobReceived, jobsMap);
return SizedBox(
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: jobsSet.length,
itemBuilder: (_, i) {
var job = jobsSet.toList()[i];
Navigator.of(context).pop();
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,
onFinished();
return true;
},
child: AlertDialog(
contentPadding: EdgeInsets.zero,
shadowColor: Colors.transparent,
backgroundColor: Colors.white,
content: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
if (jobsMap.isEmpty) {
Navigator.of(context).pop();
onFinished();
}
MessageService().unsubscribe(Constants.messageTopicJobDeleted, jobsMap);
MessageService().unsubscribe(Constants.messageTopicJobReceived, jobsMap);
MessageService().subscribe<JobDeletedMessage>(Constants.messageTopicJobDeleted, jobsMap, (jobDeletedMessage) async {
jobsMap.removeWhere((key, value) => key == jobDeletedMessage.jobId);
if (context.mounted) {
setState(() {});
}
});
MessageService().subscribe<JobReceivedMessage>(Constants.messageTopicJobReceived, jobsMap, (jobdMessage) async {
if (jobsMap.containsKey(jobdMessage.job.id)) {
jobsMap.removeWhere((key, value) => key == jobdMessage.job.id);
}
jobsMap[jobdMessage.job.id] = jobdMessage.job;
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(
height: 100,
padding: const EdgeInsets.all(5),
child: ListView.builder(
itemCount: jobsMap.length,
itemBuilder: (_, i) {
var job = jobsMap.values.toList()[i];
String remarkString = '';
for (var remark in job.rem) {
remarkString += '$remark\n';
}
remarkString = remarkString.trim();
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,
),
),
),
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)).toString(),
textAlign: TextAlign.left,
style: TextStyle(backgroundColor: isJobInFuture(job) ? Colors.yellow : Colors.white),
),
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,
),
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(
),
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: [
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,
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),
),
),
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: <Widget>[
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),
),
),
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: <Widget>[
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(remarkString),
),
),
],
),
),
),
const SizedBox(height: 20),
Row(children: [
Expanded(
child: getDefaultButton(!job.global ? 'Ablehnen' : 'Ignorieren',
() {
_sendAnswer(job, !job.global ? 2 : 3);
jobsMap.removeWhere((key, value) => key == job.id);
setState(() {});
}),
),
const SizedBox(width: 20),
Expanded(
child: getDefaultButton('Annehmen', () {
_sendAnswer(job, 1);
jobsMap.removeWhere((key, value) => key == job.id);
setState(() {});
}),
)
])
],
),
const SizedBox(height: 20),
Row(children: [
Expanded(
child: getDefaultButton(!job.global ? 'Ablehnen' : 'Ignorieren',
() => {_sendAnswer(job, !job.global ? 2 : 3), jobsSet.remove(job), setState(() {})}),
),
const SizedBox(width: 20),
Expanded(
child: getDefaultButton('Annehmen', () => {_sendAnswer(job, 1), jobsSet.remove(job), setState(() {})}),
)
])
],
),
),
),
),
),
),
);
},
)),
);
}));
);
},
)),
);
})));
});
}
+3 -1
View File
@@ -5,7 +5,9 @@ import 'package:stadtbote/tools/mqtt_core.dart';
import 'package:stadtbote/tools/util.dart';
void showInfosPopup(BuildContext context, SessionStatus? status) async {
var text = "Versionsnummer: a203\n";
var version = await Util.getAppVersion();
var text = "Versionsnummer: a$version\n";
text += "Verbindungsstatus: ${MqttCore.instance.connectionState == ConnectionStateEnum.connected ? "Verbunden" : "NICHT verbunden"}\n";
text += "GPS-Status: ${MyApp.currentPosition != null ? MyApp.currentPosition!.timestamp.toString() : "Unbekannt"}\n";
text += "\n";
+16 -8
View File
@@ -4,6 +4,7 @@ import 'package:animated_toggle_switch/animated_toggle_switch.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:stadtbote/tools/util.dart';
import '../model/job_tour.dart';
@@ -27,12 +28,16 @@ showMapPopup(BuildContext context, JobTour currentTour, List<JobTour> tourList)
tourWidgets.add(
GestureDetector(
onTap: () async {
mapController.move(LatLng(tour.pos!.lat, tour.pos!.long), mapController.zoom);
if (tour.pos != null) {
mapController.move(LatLng(tour.pos!.lat, tour.pos!.long), mapController.zoom);
streamController[currentTour.sort]!.sink.add(false);
streamController[currentTour.sort]!.sink.add(false);
currentTour = tour;
streamController[tour.sort]!.sink.add(true);
currentTour = tour;
streamController[tour.sort]!.sink.add(true);
} else {
showAlertDialog(context, 'Information', 'Es liegt keine Position für diese Station vor');
}
},
child: SizedBox(
width: 70,
@@ -87,17 +92,20 @@ showMapPopup(BuildContext context, JobTour currentTour, List<JobTour> tourList)
List<Marker> markers = [];
for (var tour in tourList) {
markers.add(Marker(
builder: (context) => const Icon(Icons.person_pin_circle, size: 56, color: Colors.blue),
point: LatLng(tour.pos!.lat, tour.pos!.long)));
if (tour.pos != null) {
markers.add(Marker(
builder: (context) => const Icon(Icons.person_pin_circle, size: 56, color: Colors.blue),
point: LatLng(tour.pos!.lat, tour.pos!.long)));
}
}
return Container(
padding: const EdgeInsets.fromLTRB(30, 0, 30, 0),
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
key: GlobalKey(),
height: MediaQuery.of(context).size.height * 0.6,
child: Container(
height: 100,
width: MediaQuery.of(context).size.width * 1,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
@@ -40,10 +40,15 @@ showReceiptGiverPopup(BuildContext context, TaskInfo taskInfo, Function(String)
controller: textEditingController,
autofocus: true),
const SizedBox(height: 20),
getDefaultButton('Übernehmen', () => {
Navigator.of(context).pop(),
getDefaultButton('Übernehmen', () {
if (textEditingController.text.isNotEmpty) {
Navigator.of(context).pop();
onFinished(textEditingController.text)})
onFinished(textEditingController.text);
} else {
showAlertDialog(context, "Fehler", "Sie müssen einen Text eingeben");
}
})
],
),
),
+36 -5
View File
@@ -3,11 +3,19 @@ import 'package:signature/signature.dart';
import 'package:stadtbote/model/task_info.dart';
import 'package:stadtbote/tools/util.dart';
showSignaturePopup(BuildContext context, TaskInfo taskInfo, Function() onFinished) async {
showSignaturePopup(BuildContext context, TaskInfo taskInfo, Function(String?) onFinished) async {
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
return WillPopScope(
onWillPop: () async {
Navigator.of(context).pop();
onFinished(null);
return true;
},// False will prevent and true will allow to dismiss
child: AlertDialog(
contentPadding: EdgeInsets.zero,
shadowColor: Colors.transparent,
backgroundColor: Colors.white,
@@ -18,6 +26,24 @@ showSignaturePopup(BuildContext context, TaskInfo taskInfo, Function() onFinishe
var popupHeight = MediaQuery.of(context).size.height * 0.85;
var popupWidth = MediaQuery.of(context).size.width * 0.85;
String convertPoints(List<Point> points) {
if (points.isNotEmpty) {
String result = "";
for (var point in points) {
if (point.type != PointType.tap) {
result += "${point.offset.dx.toInt()},${point.offset.dy.toInt()};";
} else {
result += "A";
}
}
return result.replaceAll("AA", "A");
}
return "";
}
return SizedBox(
key: GlobalKey(),
height: popupHeight,
@@ -46,14 +72,19 @@ showSignaturePopup(BuildContext context, TaskInfo taskInfo, Function() onFinishe
),
),
getDefaultButton('Übernehmen', buttonHeight: 50, () => {
Navigator.of(context).pop(),
if (controller.points.length > 50) {
Navigator.of(context).pop(),
onFinished()})
onFinished(convertPoints(controller.points))
} else {
showAlertDialog(context, "Fehler", "Die Unterschrift ist zu kurz.")
}
})
],
),
),
));
},
));
)));
});
}
+27 -13
View File
@@ -1,10 +1,11 @@
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:image/image.dart' as imageLib;
import 'package:stadtbote/model/task_info.dart';
import 'package:stadtbote/tools/util.dart';
Future<void> showTakePhotosPopup(BuildContext context, TaskInfo taskInfo, Function() onFinished) async {
Future<void> showTakePhotosPopup(BuildContext context, TaskInfo taskInfo, Function(List<Uint8List>) onFinished) async {
List<Uint8List> imageDataList = [];
List<Widget> imageWidgetsList = [];
@@ -19,22 +20,35 @@ Future<void> showTakePhotosPopup(BuildContext context, TaskInfo taskInfo, Functi
backgroundColor: Colors.white,
content: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
void capturePhoto() async {
var photo = await Util.capturePhoto();
try {
var photo = await Util.capturePhoto();
if (photo != null) {
var imageData = await photo.readAsBytes();
photo.delete();
if (photo != null) {
var fullsizeImageData = await photo.readAsBytes();
imageDataList.add(imageData);
imageLib.Image? image = imageLib.decodeImage(fullsizeImageData);
imageWidgetsList.add(GestureDetector(
onTap: () {},
child: Image.memory(imageData),
));
if (image != null) {
var thumbnail = imageLib.copyResize(image, width: 800);
imageWidgetsListNotifier.value = imageWidgetsList;
var resizedImageData = imageLib.encodeJpg(thumbnail, quality: 75);
setState(() {});
photo.delete();
imageDataList.add(resizedImageData);
imageWidgetsList.add(GestureDetector(
onTap: () {},
child: Image.memory(resizedImageData),
));
imageWidgetsListNotifier.value = imageWidgetsList;
setState(() {});
}
}
} catch (ex) {
Util.logException(ex.toString());
}
}
@@ -90,7 +104,7 @@ Future<void> showTakePhotosPopup(BuildContext context, TaskInfo taskInfo, Functi
buttonHeight: 50,
() => {
if (imageDataList.length == taskInfo.task.min! && imageDataList.length <= taskInfo.task.max!)
{Navigator.of(context).pop(), onFinished()}
{Navigator.of(context).pop(), onFinished(imageDataList)}
else
{showAlertDialog(context, 'Fehler', 'Sie müssen mindestens ${taskInfo.task.min} Foto(s) aufnehmen')}
})