Feature "Wunschziel für heute" entfernen und BWV-Seite ergänzen
Entfernt den Button und die komplette Funktionalität "Wunschziel für heute" (desired_zip): Seite, Netzwerk-Nachricht, Preference-Konstante, Übermittlung beim Session-Start sowie alle Übersetzungen. Ergänzt die neue BWV-Seite samt SetBwvLocationMessage und preferencesRemove-Helfer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
10b3a647e7
commit
e36fc69edc
12 files changed
+626
-278
No files matched your search
@@ -0,0 +1,544 @@
|
||||
import 'package:dart_mq/dart_mq.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:stadtbote/content_views/utils/content_view_widget.dart';
|
||||
import 'package:stadtbote/messages/pop_content_view_message.dart';
|
||||
import 'package:stadtbote/network_messages/set_bwv_location_message.dart';
|
||||
import 'package:stadtbote/tools/messages/publish_payload_message.dart';
|
||||
|
||||
import '../tools/app_localizations.dart';
|
||||
import '../tools/constants.dart';
|
||||
import '../tools/util.dart';
|
||||
|
||||
class BWVPage extends ContentViewWidget {
|
||||
const BWVPage({super.key});
|
||||
|
||||
@override
|
||||
State<BWVPage> createState() => _BWVPageState();
|
||||
|
||||
@override
|
||||
Future<Widget> getHeaderWidget(BuildContext context) async {
|
||||
await Future.delayed(const Duration(microseconds: 0));
|
||||
|
||||
return _HeaderWidget();
|
||||
}
|
||||
}
|
||||
|
||||
class _BWVPageState extends State<BWVPage> with ProducerMixin {
|
||||
// Wann bin ich wo zum Start verfügbar (Uhrzeiten, Datum = heute)
|
||||
TimeOfDay? _startFrom;
|
||||
TimeOfDay? _startTo;
|
||||
final TextEditingController _startFromController = TextEditingController();
|
||||
final TextEditingController _startToController = TextEditingController();
|
||||
final TextEditingController _startPlaceController = TextEditingController();
|
||||
|
||||
// Wo möchte ich mit dem nächsten BWV Auftrag enden oder beginnen (Datum + Uhrzeit)
|
||||
DateTime? _nextFrom;
|
||||
DateTime? _nextTo;
|
||||
final TextEditingController _nextFromController = TextEditingController();
|
||||
final TextEditingController _nextToController = TextEditingController();
|
||||
final TextEditingController _nextPlaceController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadPersisted();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_startFromController.dispose();
|
||||
_startToController.dispose();
|
||||
_startPlaceController.dispose();
|
||||
_nextFromController.dispose();
|
||||
_nextToController.dispose();
|
||||
_nextPlaceController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildCard(
|
||||
title: 'bwv_start_availability'.tr,
|
||||
onClear: _clearStartBlock,
|
||||
children: [
|
||||
_buildFieldRow(
|
||||
label: 'bwv_from'.tr,
|
||||
child: _buildPickerField(
|
||||
_startFromController,
|
||||
() => _pickTime(_startFromController, (t) => _startFrom = t),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildFieldRow(
|
||||
label: 'bwv_to'.tr,
|
||||
child: _buildPickerField(
|
||||
_startToController,
|
||||
() => _pickTime(_startToController, (t) => _startTo = t),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildFieldRow(
|
||||
label: 'bwv_place'.tr,
|
||||
child: _buildZipField(_startPlaceController),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildCard(
|
||||
title: 'bwv_next_job_location'.tr,
|
||||
onClear: _clearNextBlock,
|
||||
children: [
|
||||
_buildFieldRow(
|
||||
label: 'bwv_from'.tr,
|
||||
child: _buildPickerField(
|
||||
_nextFromController,
|
||||
() => _pickDateTime(_nextFromController, (d) => _nextFrom = d),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildFieldRow(
|
||||
label: 'bwv_to'.tr,
|
||||
child: _buildPickerField(
|
||||
_nextToController,
|
||||
() => _pickDateTime(_nextToController, (d) => _nextTo = d),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildFieldRow(
|
||||
label: 'bwv_place'.tr,
|
||||
child: _buildZipField(_nextPlaceController),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
getDefaultButton('apply'.tr, buttonHeight: 50, () => _apply()),
|
||||
const SizedBox(height: 24),
|
||||
Image.asset(
|
||||
'assets/sb_logo.png',
|
||||
width: 80,
|
||||
height: 80,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCard({
|
||||
required String title,
|
||||
required VoidCallback onClear,
|
||||
required List<Widget> children,
|
||||
}) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color.fromARGB(255, 245, 245, 245),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: const Border(
|
||||
left: BorderSide(color: Constants.colorPrimary, width: 4),
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style:
|
||||
const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
tooltip: 'bwv_clear_block'.tr,
|
||||
visualDensity: VisualDensity.compact,
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints(),
|
||||
icon: const Icon(Icons.backspace_outlined,
|
||||
size: 20, color: Constants.colorPrimary),
|
||||
onPressed: onClear,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
...children,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFieldRow({required String label, required Widget child}) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 50,
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 13, color: Colors.black54),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(child: child),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Lesefeld mit Picker (per onTap).
|
||||
Widget _buildPickerField(TextEditingController controller, VoidCallback onTap) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
readOnly: true,
|
||||
onTap: onTap,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||
border: Util.getDefaultBorder(),
|
||||
enabledBorder: Util.getDefaultBorder(),
|
||||
focusedBorder: Util.getDefaultFocusedBorder(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildZipField(TextEditingController controller) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
LengthLimitingTextInputFormatter(5),
|
||||
],
|
||||
maxLength: 5,
|
||||
decoration: InputDecoration(
|
||||
counterText: '',
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||
border: Util.getDefaultBorder(),
|
||||
enabledBorder: Util.getDefaultBorder(),
|
||||
focusedBorder: Util.getDefaultFocusedBorder(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _pickTime(
|
||||
TextEditingController controller, void Function(TimeOfDay) onPicked) async {
|
||||
final picked = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: TimeOfDay.now(),
|
||||
builder: (context, child) => MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true),
|
||||
child: child!,
|
||||
),
|
||||
);
|
||||
|
||||
if (picked != null) {
|
||||
onPicked(picked);
|
||||
controller.text = '${_two(picked.hour)}:${_two(picked.minute)}';
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _pickDateTime(
|
||||
TextEditingController controller, void Function(DateTime) onPicked) async {
|
||||
final now = DateTime.now();
|
||||
final date = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: now,
|
||||
firstDate: now.subtract(const Duration(days: 1)),
|
||||
lastDate: now.add(const Duration(days: 365)),
|
||||
);
|
||||
|
||||
if (date == null || !mounted) return;
|
||||
|
||||
final time = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: TimeOfDay.now(),
|
||||
builder: (context, child) => MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true),
|
||||
child: child!,
|
||||
),
|
||||
);
|
||||
|
||||
if (time == null) return;
|
||||
|
||||
final dt =
|
||||
DateTime(date.year, date.month, date.day, time.hour, time.minute);
|
||||
onPicked(dt);
|
||||
controller.text =
|
||||
'${_two(dt.day)}.${_two(dt.month)}.${dt.year} ${_two(dt.hour)}:${_two(dt.minute)}';
|
||||
}
|
||||
|
||||
String _two(int value) => value.toString().padLeft(2, '0');
|
||||
|
||||
void _clearStartBlock() {
|
||||
setState(() {
|
||||
_startFrom = null;
|
||||
_startTo = null;
|
||||
_startFromController.clear();
|
||||
_startToController.clear();
|
||||
_startPlaceController.clear();
|
||||
});
|
||||
}
|
||||
|
||||
void _clearNextBlock() {
|
||||
setState(() {
|
||||
_nextFrom = null;
|
||||
_nextTo = null;
|
||||
_nextFromController.clear();
|
||||
_nextToController.clear();
|
||||
_nextPlaceController.clear();
|
||||
});
|
||||
}
|
||||
|
||||
/// Anzahl der gefüllten Felder eines Blocks (für die "Alles oder nichts"-Regel).
|
||||
int _filledCount(List<bool> filled) => filled.where((f) => f).length;
|
||||
|
||||
void _apply() {
|
||||
// Block "Verfügbarkeit": entweder alle Felder gefüllt oder alle leer.
|
||||
final startFilled = [
|
||||
_startFrom != null,
|
||||
_startTo != null,
|
||||
_startPlaceController.text.isNotEmpty,
|
||||
];
|
||||
final startCount = _filledCount(startFilled);
|
||||
if (startCount != 0 && startCount != startFilled.length) {
|
||||
showAlertDialog('error'.tr, 'bwv_incomplete_block'.tr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Block "nächster Auftrag": entweder alle Felder gefüllt oder alle leer.
|
||||
final nextFilled = [
|
||||
_nextFrom != null,
|
||||
_nextTo != null,
|
||||
_nextPlaceController.text.isNotEmpty,
|
||||
];
|
||||
final nextCount = _filledCount(nextFilled);
|
||||
if (nextCount != 0 && nextCount != nextFilled.length) {
|
||||
showAlertDialog('error'.tr, 'bwv_incomplete_block'.tr);
|
||||
return;
|
||||
}
|
||||
|
||||
// PLZ-Validierung (fünfstellig, sofern gefüllt).
|
||||
for (final zip in [_startPlaceController.text, _nextPlaceController.text]) {
|
||||
if (zip.isNotEmpty && zip.length != 5) {
|
||||
showAlertDialog('error'.tr, 'zip_validation_error'.tr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final now = DateTime.now();
|
||||
|
||||
// Verfügbarkeit: Uhrzeiten beziehen sich auf das heutige Datum.
|
||||
BwvTimeWindow? availabilityStart;
|
||||
if (startCount == startFilled.length) {
|
||||
final from = DateTime(
|
||||
now.year, now.month, now.day, _startFrom!.hour, _startFrom!.minute);
|
||||
final to = DateTime(
|
||||
now.year, now.month, now.day, _startTo!.hour, _startTo!.minute);
|
||||
// Limit nur im logischen Sinne: von < bis.
|
||||
if (!from.isBefore(to)) {
|
||||
showAlertDialog('error'.tr, 'bwv_invalid_range'.tr);
|
||||
return;
|
||||
}
|
||||
availabilityStart = BwvTimeWindow(
|
||||
fromUtc: Util.toUnixSecondsUtc(from),
|
||||
toUtc: Util.toUnixSecondsUtc(to),
|
||||
postalCode: _startPlaceController.text,
|
||||
);
|
||||
}
|
||||
|
||||
BwvTimeWindow? bwvLocationPreference;
|
||||
if (nextCount == nextFilled.length) {
|
||||
// Limit nur im logischen Sinne: von < bis.
|
||||
if (!_nextFrom!.isBefore(_nextTo!)) {
|
||||
showAlertDialog('error'.tr, 'bwv_invalid_range'.tr);
|
||||
return;
|
||||
}
|
||||
bwvLocationPreference = BwvTimeWindow(
|
||||
fromUtc: Util.toUnixSecondsUtc(_nextFrom!),
|
||||
toUtc: Util.toUnixSecondsUtc(_nextTo!),
|
||||
postalCode: _nextPlaceController.text,
|
||||
);
|
||||
}
|
||||
|
||||
_persist();
|
||||
|
||||
final message = SetBwvLocationMessage(
|
||||
timestamp: Util.nowUnixSecondsUtc(),
|
||||
availabilityStart: availabilityStart,
|
||||
bwvLocationPreference: bwvLocationPreference,
|
||||
);
|
||||
|
||||
sendMessage(
|
||||
routingKey: Constants.messageTopicPublishPayload,
|
||||
payload: PublishPayloadMessage(
|
||||
topic: "set_bwv_location",
|
||||
message: message.toJson(),
|
||||
acknowledgeRequired: false,
|
||||
),
|
||||
);
|
||||
|
||||
sendMessage(
|
||||
routingKey: Constants.messageTopicPopContentView,
|
||||
payload: PopContentViewMessage(count: 1),
|
||||
);
|
||||
}
|
||||
|
||||
/// Lädt die zuletzt gespeicherten Werte (App ist datenführend).
|
||||
/// Ist der Zeitraum des unteren Blocks abgelaufen, werden dessen Felder genullt.
|
||||
void _loadPersisted() {
|
||||
final startFrom =
|
||||
Util.preferencesGetString(Constants.prefsBwvStartFrom, null);
|
||||
final startTo = Util.preferencesGetString(Constants.prefsBwvStartTo, null);
|
||||
final startPlace =
|
||||
Util.preferencesGetString(Constants.prefsBwvStartPlace, null);
|
||||
if (startFrom != null && startTo != null) {
|
||||
_startFrom = _parseTime(startFrom);
|
||||
_startTo = _parseTime(startTo);
|
||||
_startFromController.text = startFrom;
|
||||
_startToController.text = startTo;
|
||||
_startPlaceController.text = startPlace ?? '';
|
||||
}
|
||||
|
||||
final nextFromMillis =
|
||||
Util.preferencesGetInt(Constants.prefsBwvNextFrom, null);
|
||||
final nextToMillis = Util.preferencesGetInt(Constants.prefsBwvNextTo, null);
|
||||
final nextPlace =
|
||||
Util.preferencesGetString(Constants.prefsBwvNextPlace, null);
|
||||
if (nextFromMillis != null && nextToMillis != null) {
|
||||
final from = DateTime.fromMillisecondsSinceEpoch(nextFromMillis);
|
||||
final to = DateTime.fromMillisecondsSinceEpoch(nextToMillis);
|
||||
|
||||
if (to.isBefore(DateTime.now())) {
|
||||
// Zeitraum abgelaufen -> Felder nullen und Persistenz bereinigen.
|
||||
_clearPersistedNextBlock();
|
||||
} else {
|
||||
_nextFrom = from;
|
||||
_nextTo = to;
|
||||
_nextFromController.text =
|
||||
'${_two(from.day)}.${_two(from.month)}.${from.year} ${_two(from.hour)}:${_two(from.minute)}';
|
||||
_nextToController.text =
|
||||
'${_two(to.day)}.${_two(to.month)}.${to.year} ${_two(to.hour)}:${_two(to.minute)}';
|
||||
_nextPlaceController.text = nextPlace ?? '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimeOfDay? _parseTime(String value) {
|
||||
final parts = value.split(':');
|
||||
if (parts.length != 2) return null;
|
||||
final hour = int.tryParse(parts[0]);
|
||||
final minute = int.tryParse(parts[1]);
|
||||
if (hour == null || minute == null) return null;
|
||||
return TimeOfDay(hour: hour, minute: minute);
|
||||
}
|
||||
|
||||
void _persist() {
|
||||
// Block "Verfügbarkeit"
|
||||
if (_startFrom != null && _startTo != null) {
|
||||
Util.preferencesSaveString(Constants.prefsBwvStartFrom,
|
||||
'${_two(_startFrom!.hour)}:${_two(_startFrom!.minute)}');
|
||||
Util.preferencesSaveString(Constants.prefsBwvStartTo,
|
||||
'${_two(_startTo!.hour)}:${_two(_startTo!.minute)}');
|
||||
Util.preferencesSaveString(
|
||||
Constants.prefsBwvStartPlace, _startPlaceController.text);
|
||||
} else {
|
||||
Util.preferencesRemove(Constants.prefsBwvStartFrom);
|
||||
Util.preferencesRemove(Constants.prefsBwvStartTo);
|
||||
Util.preferencesRemove(Constants.prefsBwvStartPlace);
|
||||
}
|
||||
|
||||
// Block "nächster Auftrag"
|
||||
if (_nextFrom != null && _nextTo != null) {
|
||||
Util.preferencesSaveInt(
|
||||
Constants.prefsBwvNextFrom, _nextFrom!.millisecondsSinceEpoch);
|
||||
Util.preferencesSaveInt(
|
||||
Constants.prefsBwvNextTo, _nextTo!.millisecondsSinceEpoch);
|
||||
Util.preferencesSaveString(
|
||||
Constants.prefsBwvNextPlace, _nextPlaceController.text);
|
||||
} else {
|
||||
_clearPersistedNextBlock();
|
||||
}
|
||||
}
|
||||
|
||||
void _clearPersistedNextBlock() {
|
||||
Util.preferencesRemove(Constants.prefsBwvNextFrom);
|
||||
Util.preferencesRemove(Constants.prefsBwvNextTo);
|
||||
Util.preferencesRemove(Constants.prefsBwvNextPlace);
|
||||
}
|
||||
}
|
||||
|
||||
class _HeaderWidget extends StatefulWidget {
|
||||
@override
|
||||
State<_HeaderWidget> createState() => _HeaderWidgetState();
|
||||
}
|
||||
|
||||
class _HeaderWidgetState extends State<_HeaderWidget> with ProducerMixin {
|
||||
@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: [
|
||||
Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.white,
|
||||
),
|
||||
child: FittedBox(
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
sendMessage(
|
||||
routingKey: Constants.messageTopicPopContentView,
|
||||
payload: PopContentViewMessage(count: 1),
|
||||
);
|
||||
},
|
||||
child: const CircleAvatar(
|
||||
radius: 10,
|
||||
backgroundColor: Constants.colorPrimary,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(1.0),
|
||||
child: Icon(Icons.arrow_back,
|
||||
size: 20.0, color: Colors.white)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 60),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 100,
|
||||
width: cellWidth * 2,
|
||||
child: Center(
|
||||
child: Text(
|
||||
textAlign: TextAlign.center,
|
||||
'bwv'.tr,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ 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/bwv_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';
|
||||
@@ -245,13 +245,13 @@ class _MainPageState extends State<MainPage> with ConsumerMixin, ProducerMixin {
|
||||
Visibility(
|
||||
visible: !_loggedOut && _showBWVButton,
|
||||
child: getDefaultButton(
|
||||
'desired_zip'.tr,
|
||||
'bwv'.tr,
|
||||
image: Image.asset('assets/wunsch_plz.png',
|
||||
width: 30, height: 30),
|
||||
() async => {
|
||||
sendMessage(
|
||||
routingKey: Constants.messageTopicPushContentView,
|
||||
payload: const SetDesiredZipPage(),
|
||||
payload: const BWVPage(),
|
||||
)
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
import 'package:dart_mq/dart_mq.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:stadtbote/content_views/utils/content_view_widget.dart';
|
||||
import 'package:stadtbote/messages/pop_content_view_message.dart';
|
||||
import 'package:stadtbote/network_messages/set_desired_zip_message.dart';
|
||||
import 'package:stadtbote/tools/messages/publish_payload_message.dart';
|
||||
|
||||
import '../tools/app_localizations.dart';
|
||||
import '../tools/constants.dart';
|
||||
import '../tools/util.dart';
|
||||
|
||||
class SetDesiredZipPage extends ContentViewWidget {
|
||||
const SetDesiredZipPage({super.key});
|
||||
|
||||
@override
|
||||
State<SetDesiredZipPage> createState() => _SetDesiredZipPageState();
|
||||
|
||||
@override
|
||||
Future<Widget> getHeaderWidget(BuildContext context) async {
|
||||
await Future.delayed(const Duration(microseconds: 0));
|
||||
|
||||
return _HeaderWidget();
|
||||
}
|
||||
}
|
||||
|
||||
class _SetDesiredZipPageState extends State<SetDesiredZipPage>
|
||||
with ProducerMixin {
|
||||
TextEditingController textEditController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
var savedDesiredZip =
|
||||
Util.preferencesGetString(Constants.prefsDesiredZip, null);
|
||||
if (savedDesiredZip != null) {
|
||||
textEditController.text = savedDesiredZip;
|
||||
}
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(children: [
|
||||
Text('enter_desired_zip'.tr),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center, // Zentriert das Textfeld horizontal
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 120, // Festlegen der Breite des Textfelds
|
||||
child: Util.getDefaultTextField('PLZ',
|
||||
textInputType: TextInputType.number,
|
||||
formatter: [
|
||||
// Erlaubt nur Ziffern
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
// Begrenzt die maximale Eingabelänge auf 5 Zeichen
|
||||
LengthLimitingTextInputFormatter(5),
|
||||
],
|
||||
controller: textEditController,
|
||||
maxLength: 5,
|
||||
autofocus: true),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
getDefaultButton(
|
||||
'apply'.tr,
|
||||
() => {
|
||||
if (textEditController.text.isEmpty ||
|
||||
textEditController.text.length == 5)
|
||||
{
|
||||
Util.preferencesSaveString(
|
||||
Constants.prefsDesiredZip, textEditController.text),
|
||||
_sendDesiredZipMessage(textEditController.text),
|
||||
sendMessage(
|
||||
routingKey: Constants.messageTopicPopContentView,
|
||||
payload: PopContentViewMessage(count: 1),
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
showAlertDialog('error'.tr,
|
||||
'zip_validation_error'.tr)
|
||||
}
|
||||
})
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
_sendDesiredZipMessage(String desiredZip) {
|
||||
var desiredZipMessage = SetDesiredZipMessage();
|
||||
desiredZipMessage.desiredZip = desiredZip;
|
||||
|
||||
sendMessage(
|
||||
routingKey: Constants.messageTopicPublishPayload,
|
||||
payload: PublishPayloadMessage(
|
||||
topic: "set_desired_zip",
|
||||
message: desiredZipMessage.toJson(),
|
||||
acknowledgeRequired: true),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
textEditController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class _HeaderWidget extends StatefulWidget {
|
||||
@override
|
||||
State<_HeaderWidget> createState() => _HeaderWidgetState();
|
||||
}
|
||||
|
||||
class _HeaderWidgetState extends State<_HeaderWidget> with ProducerMixin {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@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: true,
|
||||
child: Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.white,
|
||||
),
|
||||
child: FittedBox(
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
sendMessage(
|
||||
routingKey: Constants.messageTopicPopContentView,
|
||||
payload: PopContentViewMessage(count: 1),
|
||||
);
|
||||
},
|
||||
child: const CircleAvatar(
|
||||
radius: 10,
|
||||
backgroundColor: Constants.colorPrimary,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(1.0),
|
||||
child: Icon(Icons.arrow_back,
|
||||
size: 20.0, color: Colors.white)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: false,
|
||||
child: Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.red,
|
||||
),
|
||||
child: FittedBox(
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
sendMessage(
|
||||
routingKey: Constants.messageTopicPopContentView,
|
||||
payload: PopContentViewMessage(count: 1),
|
||||
);
|
||||
},
|
||||
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: const Center(
|
||||
child: Text(
|
||||
textAlign: TextAlign.center,
|
||||
"Wunschziel für heute",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/// Zeitfenster mit Postleitzahl für das Setzen der BWV-Location.
|
||||
class BwvTimeWindow {
|
||||
final int fromUtc;
|
||||
final int toUtc;
|
||||
final String postalCode;
|
||||
|
||||
BwvTimeWindow({
|
||||
required this.fromUtc,
|
||||
required this.toUtc,
|
||||
required this.postalCode,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'fromUtc': fromUtc,
|
||||
'toUtc': toUtc,
|
||||
'postalCode': postalCode,
|
||||
};
|
||||
}
|
||||
|
||||
/// Payload für das Topic `set_bwv_location`.
|
||||
class SetBwvLocationMessage {
|
||||
final int timestamp;
|
||||
final BwvTimeWindow? availabilityStart;
|
||||
final BwvTimeWindow? bwvLocationPreference;
|
||||
|
||||
SetBwvLocationMessage({
|
||||
required this.timestamp,
|
||||
this.availabilityStart,
|
||||
this.bwvLocationPreference,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{'timestamp': timestamp};
|
||||
|
||||
if (availabilityStart != null) {
|
||||
map['availabilityStart'] = availabilityStart!.toJson();
|
||||
}
|
||||
if (bwvLocationPreference != null) {
|
||||
map['bwvLocationPreference'] = bwvLocationPreference!.toJson();
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'set_desired_zip_message.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class SetDesiredZipMessage {
|
||||
@JsonKey(name: "desired_zip")
|
||||
late String desiredZip;
|
||||
|
||||
SetDesiredZipMessage();
|
||||
|
||||
factory SetDesiredZipMessage.fromJson(Map<String, dynamic> json) => _$SetDesiredZipMessageFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$SetDesiredZipMessageToJson(this);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'set_desired_zip_message.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
SetDesiredZipMessage _$SetDesiredZipMessageFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
SetDesiredZipMessage()..desiredZip = json['desired_zip'] as String;
|
||||
|
||||
Map<String, dynamic> _$SetDesiredZipMessageToJson(
|
||||
SetDesiredZipMessage instance) =>
|
||||
<String, dynamic>{
|
||||
'desired_zip': instance.desiredZip,
|
||||
};
|
||||
@@ -4,9 +4,6 @@ part 'start_session_message.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class StartSessionMessage {
|
||||
@JsonKey(name: "desired_zip")
|
||||
late String desiredZip;
|
||||
|
||||
StartSessionMessage();
|
||||
|
||||
factory StartSessionMessage.fromJson(Map<String, dynamic> json) => _$StartSessionMessageFromJson(json);
|
||||
|
||||
@@ -7,10 +7,8 @@ part of 'start_session_message.dart';
|
||||
// **************************************************************************
|
||||
|
||||
StartSessionMessage _$StartSessionMessageFromJson(Map<String, dynamic> json) =>
|
||||
StartSessionMessage()..desiredZip = json['desired_zip'] as String;
|
||||
StartSessionMessage();
|
||||
|
||||
Map<String, dynamic> _$StartSessionMessageToJson(
|
||||
StartSessionMessage instance) =>
|
||||
<String, dynamic>{
|
||||
'desired_zip': instance.desiredZip,
|
||||
};
|
||||
<String, dynamic>{};
|
||||
@@ -72,7 +72,7 @@ class AppLocalizations {
|
||||
'finished_jobs': 'Erledigte Aufträge',
|
||||
'messages': 'Nachrichten',
|
||||
'approach_area': 'Anfahrtsbereich',
|
||||
'desired_zip': 'Wunschziel für heute',
|
||||
'bwv': 'BWV',
|
||||
'login': 'Anmelden',
|
||||
'logout': 'Abmelden',
|
||||
'courier_id': 'Kurierkennung',
|
||||
@@ -172,7 +172,6 @@ class AppLocalizations {
|
||||
'release': 'Freimelden',
|
||||
'zip': 'Postleitzahl',
|
||||
'enter_zip': 'Bitte geben Sie eine fünfstellige Postleitzahl ein',
|
||||
'enter_desired_zip': 'Bitte geben Sie Ihr Wunschziel für heute ein',
|
||||
'zip_hint': 'PLZ',
|
||||
'zip_validation_error': 'Das Feld muss leer sein, oder es müssen fünf Zahlen eingegeben werden',
|
||||
'enter_commission_number': 'Bitte geben Sie die Kommisionsnummer ein',
|
||||
@@ -185,6 +184,16 @@ class AppLocalizations {
|
||||
|
||||
// Liste schließen
|
||||
'close_list': 'Liste schliessen',
|
||||
|
||||
// BWV / Vorbestellungen
|
||||
'bwv_start_availability': 'Wann bin ich wo zum Start verfügbar',
|
||||
'bwv_next_job_location': 'Wo möchte ich mit dem nächsten BWV Auftrag enden oder beginnen',
|
||||
'bwv_from': 'von',
|
||||
'bwv_to': 'bis',
|
||||
'bwv_place': 'Ort',
|
||||
'bwv_clear_block': 'Block leeren',
|
||||
'bwv_incomplete_block': 'Bitte alle Felder eines Blocks ausfüllen oder den Block leeren.',
|
||||
'bwv_invalid_range': 'Der „von"-Zeitpunkt muss vor dem „bis"-Zeitpunkt liegen.',
|
||||
},
|
||||
|
||||
// English
|
||||
@@ -195,7 +204,6 @@ class AppLocalizations {
|
||||
'finished_jobs': 'Finished Jobs',
|
||||
'messages': 'Messages',
|
||||
'approach_area': 'Approach Area',
|
||||
'desired_zip': 'Desired Location for Today',
|
||||
'login': 'Login',
|
||||
'logout': 'Logout',
|
||||
'courier_id': 'Courier ID',
|
||||
@@ -295,7 +303,6 @@ class AppLocalizations {
|
||||
'release': 'Release',
|
||||
'zip': 'ZIP Code',
|
||||
'enter_zip': 'Please enter a five-digit ZIP code',
|
||||
'enter_desired_zip': 'Please enter your desired location for today',
|
||||
'zip_hint': 'ZIP',
|
||||
'zip_validation_error': 'The field must be empty or five digits must be entered',
|
||||
'enter_commission_number': 'Please enter the commission number',
|
||||
@@ -308,6 +315,16 @@ class AppLocalizations {
|
||||
|
||||
// Close List
|
||||
'close_list': 'Close List',
|
||||
|
||||
// BWV / Pre-orders
|
||||
'bwv_start_availability': 'When and where am I available to start',
|
||||
'bwv_next_job_location': 'Where do I want to end or begin with the next BWV job',
|
||||
'bwv_from': 'from',
|
||||
'bwv_to': 'to',
|
||||
'bwv_place': 'Place',
|
||||
'bwv_clear_block': 'Clear block',
|
||||
'bwv_incomplete_block': 'Please fill in all fields of a block or clear the block.',
|
||||
'bwv_invalid_range': 'The "from" time must be before the "to" time.',
|
||||
},
|
||||
|
||||
// Spanish
|
||||
@@ -318,7 +335,6 @@ class AppLocalizations {
|
||||
'finished_jobs': 'Trabajos Terminados',
|
||||
'messages': 'Mensajes',
|
||||
'approach_area': 'Área de Aproximación',
|
||||
'desired_zip': 'Ubicación Deseada para Hoy',
|
||||
'login': 'Iniciar Sesión',
|
||||
'logout': 'Cerrar Sesión',
|
||||
'courier_id': 'ID de Mensajero',
|
||||
@@ -418,7 +434,6 @@ class AppLocalizations {
|
||||
'release': 'Liberar',
|
||||
'zip': 'Código Postal',
|
||||
'enter_zip': 'Por favor ingrese un código postal de cinco dígitos',
|
||||
'enter_desired_zip': 'Por favor ingrese su ubicación deseada para hoy',
|
||||
'zip_hint': 'CP',
|
||||
'zip_validation_error': 'El campo debe estar vacío o deben ingresarse cinco dígitos',
|
||||
'enter_commission_number': 'Por favor ingrese el número de comisión',
|
||||
@@ -441,7 +456,6 @@ class AppLocalizations {
|
||||
'finished_jobs': 'Tâches Terminées',
|
||||
'messages': 'Messages',
|
||||
'approach_area': "Zone d'Approche",
|
||||
'desired_zip': 'Lieu Souhaité pour Aujourd\'hui',
|
||||
'login': 'Connexion',
|
||||
'logout': 'Déconnexion',
|
||||
'courier_id': 'ID Coursier',
|
||||
@@ -541,7 +555,6 @@ class AppLocalizations {
|
||||
'release': 'Libérer',
|
||||
'zip': 'Code Postal',
|
||||
'enter_zip': 'Veuillez saisir un code postal à cinq chiffres',
|
||||
'enter_desired_zip': 'Veuillez saisir votre lieu souhaité pour aujourd\'hui',
|
||||
'zip_hint': 'CP',
|
||||
'zip_validation_error': 'Le champ doit être vide ou cinq chiffres doivent être saisis',
|
||||
'enter_commission_number': 'Veuillez saisir le numéro de commission',
|
||||
@@ -564,7 +577,6 @@ class AppLocalizations {
|
||||
'finished_jobs': 'Zakończone Zadania',
|
||||
'messages': 'Wiadomości',
|
||||
'approach_area': 'Strefa Dojazdu',
|
||||
'desired_zip': 'Pożądana Lokalizacja na Dziś',
|
||||
'login': 'Zaloguj',
|
||||
'logout': 'Wyloguj',
|
||||
'courier_id': 'ID Kuriera',
|
||||
@@ -664,7 +676,6 @@ class AppLocalizations {
|
||||
'release': 'Zwolnij',
|
||||
'zip': 'Kod Pocztowy',
|
||||
'enter_zip': 'Proszę wprowadzić pięciocyfrowy kod pocztowy',
|
||||
'enter_desired_zip': 'Proszę wprowadzić pożądaną lokalizację na dziś',
|
||||
'zip_hint': 'Kod',
|
||||
'zip_validation_error': 'Pole musi być puste lub muszą zostać wprowadzone pięć cyfr',
|
||||
'enter_commission_number': 'Proszę wprowadzić numer zlecenia',
|
||||
@@ -687,7 +698,6 @@ class AppLocalizations {
|
||||
'finished_jobs': 'Завершенные Задания',
|
||||
'messages': 'Сообщения',
|
||||
'approach_area': 'Зона Доступа',
|
||||
'desired_zip': 'Желаемое Местоположение на Сегодня',
|
||||
'login': 'Войти',
|
||||
'logout': 'Выйти',
|
||||
'courier_id': 'ID Курьера',
|
||||
@@ -787,7 +797,6 @@ class AppLocalizations {
|
||||
'release': 'Освободить',
|
||||
'zip': 'Почтовый Индекс',
|
||||
'enter_zip': 'Пожалуйста, введите пятизначный почтовый индекс',
|
||||
'enter_desired_zip': 'Пожалуйста, введите желаемое местоположение на сегодня',
|
||||
'zip_hint': 'Индекс',
|
||||
'zip_validation_error': 'Поле должно быть пустым или должны быть введены пять цифр',
|
||||
'enter_commission_number': 'Пожалуйста, введите номер комиссии',
|
||||
@@ -810,7 +819,6 @@ class AppLocalizations {
|
||||
'finished_jobs': 'Tamamlanan Görevler',
|
||||
'messages': 'Mesajlar',
|
||||
'approach_area': 'Yaklaşım Bölgesi',
|
||||
'desired_zip': 'Bugün İçin İstenen Konum',
|
||||
'login': 'Giriş',
|
||||
'logout': 'Çıkış',
|
||||
'courier_id': 'Kurye ID',
|
||||
@@ -910,7 +918,6 @@ class AppLocalizations {
|
||||
'release': 'Serbest Bırak',
|
||||
'zip': 'Posta Kodu',
|
||||
'enter_zip': 'Lütfen beş haneli bir posta kodu girin',
|
||||
'enter_desired_zip': 'Lütfen bugün için istediğiniz konumu girin',
|
||||
'zip_hint': 'PK',
|
||||
'zip_validation_error': 'Alan boş olmalı veya beş rakam girilmelidir',
|
||||
'enter_commission_number': 'Lütfen komisyon numarasını girin',
|
||||
@@ -933,7 +940,6 @@ class AppLocalizations {
|
||||
'finished_jobs': 'Lõpetatud Ülesanded',
|
||||
'messages': 'Sõnumid',
|
||||
'approach_area': 'Ligipääsu Piirkond',
|
||||
'desired_zip': 'Tänane Soovitud Asukoht',
|
||||
'login': 'Logi Sisse',
|
||||
'logout': 'Logi Välja',
|
||||
'courier_id': 'Kulleri ID',
|
||||
@@ -1033,7 +1039,6 @@ class AppLocalizations {
|
||||
'release': 'Vabasta',
|
||||
'zip': 'Postiindeks',
|
||||
'enter_zip': 'Palun sisestage viiekohaline postiindeks',
|
||||
'enter_desired_zip': 'Palun sisestage tänane soovitud asukoht',
|
||||
'zip_hint': 'Indeks',
|
||||
'zip_validation_error': 'Väli peab olema tühi või tuleb sisestada viis numbrit',
|
||||
'enter_commission_number': 'Palun sisestage komisjoni number',
|
||||
@@ -1056,7 +1061,6 @@ class AppLocalizations {
|
||||
'finished_jobs': 'Pabeigtie Uzdevumi',
|
||||
'messages': 'Ziņojumi',
|
||||
'approach_area': 'Piekļuves Zona',
|
||||
'desired_zip': 'Vēlamā Atrašanās Vieta Šodien',
|
||||
'login': 'Pieteikties',
|
||||
'logout': 'Iziet',
|
||||
'courier_id': 'Kurjera ID',
|
||||
@@ -1156,7 +1160,6 @@ class AppLocalizations {
|
||||
'release': 'Atbrīvot',
|
||||
'zip': 'Pasta Indekss',
|
||||
'enter_zip': 'Lūdzu, ievadiet piecciparu pasta indeksu',
|
||||
'enter_desired_zip': 'Lūdzu, ievadiet vēlamo atrašanās vietu šodien',
|
||||
'zip_hint': 'Indekss',
|
||||
'zip_validation_error': 'Lauks jābūt tukšam vai jāievada pieci cipari',
|
||||
'enter_commission_number': 'Lūdzu, ievadiet komisijas numuru',
|
||||
@@ -1179,7 +1182,6 @@ class AppLocalizations {
|
||||
'finished_jobs': 'Baigtos Užduotys',
|
||||
'messages': 'Žinutės',
|
||||
'approach_area': 'Prieigos Zona',
|
||||
'desired_zip': 'Pageidaujama Vieta Šiandien',
|
||||
'login': 'Prisijungti',
|
||||
'logout': 'Atsijungti',
|
||||
'courier_id': 'Kurjerio ID',
|
||||
@@ -1279,7 +1281,6 @@ class AppLocalizations {
|
||||
'release': 'Atleisti',
|
||||
'zip': 'Pašto Kodas',
|
||||
'enter_zip': 'Prašome įvesti penkiaženklį pašto kodą',
|
||||
'enter_desired_zip': 'Prašome įvesti pageidaujamą vietą šiandien',
|
||||
'zip_hint': 'Kodas',
|
||||
'zip_validation_error': 'Laukas turi būti tuščias arba turi būti įvesti penki skaitmenys',
|
||||
'enter_commission_number': 'Prašome įvesti komisijos numerį',
|
||||
|
||||
@@ -31,9 +31,16 @@ class Constants {
|
||||
|
||||
static const String prefsPreviousMqttSubscription = "previous_mqtt_subscription";
|
||||
static const String prefsBWVEnabled = "bwv_enabled";
|
||||
static const String prefsDesiredZip = "desired_zip";
|
||||
static const String prefsAppLanguage = "app_language";
|
||||
|
||||
// BWV / Vorbestellungen (App ist datenführend, lokal persistiert)
|
||||
static const String prefsBwvStartFrom = "bwv_start_from"; // "HH:mm"
|
||||
static const String prefsBwvStartTo = "bwv_start_to"; // "HH:mm"
|
||||
static const String prefsBwvStartPlace = "bwv_start_place"; // PLZ
|
||||
static const String prefsBwvNextFrom = "bwv_next_from"; // epoch millis (lokal)
|
||||
static const String prefsBwvNextTo = "bwv_next_to"; // epoch millis (lokal)
|
||||
static const String prefsBwvNextPlace = "bwv_next_place"; // PLZ
|
||||
|
||||
static const int stateAssigned = 0;
|
||||
static const int stateAccepted = 1;
|
||||
static const int stateFinished = 2;
|
||||
|
||||
@@ -588,7 +588,6 @@ class MqttCore with ProducerMixin, ConsumerMixin {
|
||||
|
||||
_initSession() async {
|
||||
var startSessionMessage = StartSessionMessage();
|
||||
startSessionMessage.desiredZip = (Util.preferencesGetString(Constants.prefsDesiredZip, ""))!;
|
||||
|
||||
var jsonMap = startSessionMessage.toJson();
|
||||
|
||||
|
||||
@@ -68,6 +68,10 @@ class Util {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
static void preferencesRemove(String key) async {
|
||||
await prefs.remove(key);
|
||||
}
|
||||
|
||||
static String getAppUuid() {
|
||||
var currentUuid = preferencesGetString('uuid', null);
|
||||
|
||||
|
||||
Reference in new issue
Block a user