Unterschriftenerfassung dauerhaft auf Deutsch anzeigen, lokale VHS-Buildkonfiguration und Regressionstests ergänzen sowie Version auf 357.0.0+357 anheben.
326 lines
10 KiB
Dart
326 lines
10 KiB
Dart
import 'package:dart_mq/dart_mq.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
|
|
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
|
|
import 'package:stadtbote/content_views/utils/content_view_widget.dart';
|
|
import 'package:stadtbote/main.dart';
|
|
import 'package:stadtbote/messages/pop_content_view_message.dart';
|
|
import 'package:stadtbote/model/chat_message.dart' as sbmessage;
|
|
import 'package:stadtbote/model/session_status.dart';
|
|
import 'package:stadtbote/objectbox.g.dart';
|
|
import 'package:stadtbote/network_messages/messages_showed.dart';
|
|
import 'package:stadtbote/tools/constants.dart';
|
|
import 'package:stadtbote/tools/messages/publish_payload_message.dart';
|
|
|
|
import '../tools/app_localizations.dart';
|
|
import '../tools/util.dart';
|
|
|
|
class MessagesPage extends ContentViewWidget {
|
|
const MessagesPage({super.key});
|
|
|
|
@override
|
|
State<MessagesPage> createState() => _MessagesPageState();
|
|
|
|
@override
|
|
Future<Widget> getHeaderWidget(BuildContext context) async {
|
|
await Future.delayed(const Duration(microseconds: 0));
|
|
|
|
return _HeaderWidget();
|
|
}
|
|
}
|
|
|
|
class _MessagesPageState extends State<MessagesPage> with ProducerMixin {
|
|
bool _initCompleted = false;
|
|
|
|
late String _sid;
|
|
|
|
late final List<types.Message> _messages = [];
|
|
|
|
final _user = const types.User(
|
|
id: '82091008-a484-4a89-ae75-a22bf8d6f3ac',
|
|
);
|
|
|
|
final _central = const types.User(
|
|
id: '82091008-a484-4a89-ae75-a22bf8d6f3ad',
|
|
);
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
Future.delayed(const Duration(seconds: 0), () async {
|
|
final box = MyApp.objectBoxStore.box<SessionStatus>();
|
|
final sessions = box.getAll();
|
|
var status = sessions.isNotEmpty ? sessions.first : null;
|
|
_sid = status != null ? status.sid : 'unknown'.tr;
|
|
|
|
_sendMessagesReadMessage();
|
|
|
|
await _fetchMessages();
|
|
|
|
_initCompleted = true;
|
|
|
|
if (mounted) {
|
|
setState(() {});
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (!_initCompleted) {
|
|
return Util.getWaitWidget(context);
|
|
}
|
|
|
|
return Stack(
|
|
children: [
|
|
Chat(
|
|
messages: _messages,
|
|
textMessageOptions: const TextMessageOptions(isTextSelectable: false),
|
|
onSendPressed: _handleSendPressed,
|
|
showUserAvatars: false,
|
|
showUserNames: false,
|
|
user: _user,
|
|
onMessageTap: (context, message) =>
|
|
{_showDeleteMessageDialog(context, message)},
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
void _handleSendPressed(types.PartialText text) async {
|
|
var now = DateTime.now();
|
|
|
|
final message = sbmessage.ChatMessage.outgoing(
|
|
externalId: await sbmessage.ChatMessage.getNextId(),
|
|
time: Util.nowUnixSecondsUtc(),
|
|
from: _sid,
|
|
text: text.text,
|
|
);
|
|
|
|
final box = MyApp.objectBoxStore.box<sbmessage.ChatMessage>();
|
|
box.put(message);
|
|
|
|
sendMessage(
|
|
routingKey: Constants.messageTopicPublishPayload,
|
|
payload: PublishPayloadMessage(
|
|
topic: "message",
|
|
message: message.toJson(),
|
|
acknowledgeRequired: true),
|
|
);
|
|
|
|
_messages.insert(
|
|
0,
|
|
types.TextMessage(
|
|
author: _user,
|
|
createdAt: now.millisecondsSinceEpoch,
|
|
id: message.id.toString(),
|
|
text: text.text,
|
|
));
|
|
|
|
setState(() {});
|
|
}
|
|
|
|
Future<void> _fetchMessages() async {
|
|
_messages.clear();
|
|
|
|
final box = MyApp.objectBoxStore.box<sbmessage.ChatMessage>();
|
|
var messages = box.getAll();
|
|
|
|
for (var message in messages) {
|
|
_messages.add(types.TextMessage(
|
|
author: message.from == _sid ? _user : _central,
|
|
createdAt: message.time * 1000,
|
|
id: message.id.toString(),
|
|
text: "${message.subj}\n${message.text}",
|
|
));
|
|
|
|
_messages.sort((a, b) => b.createdAt! > a.createdAt! ? 1 : 0);
|
|
|
|
message.read = true;
|
|
box.put(message);
|
|
}
|
|
|
|
setState(() {});
|
|
}
|
|
|
|
_showDeleteMessageDialog(BuildContext context, types.Message message) 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) {
|
|
return SizedBox(
|
|
key: GlobalKey(),
|
|
height: 120,
|
|
width: MediaQuery.of(context).size.width * 1.2,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Container(
|
|
height: 100,
|
|
padding: const EdgeInsets.all(5),
|
|
child: Column(
|
|
children: [
|
|
Text('delete_message_confirm'.tr),
|
|
const SizedBox(height: 20),
|
|
Row(children: [
|
|
Expanded(
|
|
child: getDefaultButton('no'.tr,
|
|
() => {Navigator.of(context).pop()}),
|
|
),
|
|
const SizedBox(width: 20),
|
|
Expanded(
|
|
child: getDefaultButton(
|
|
'yes'.tr,
|
|
() async => {
|
|
MyApp.objectBoxStore.box<sbmessage.ChatMessage>()
|
|
.remove(int.parse(message.id)),
|
|
_fetchMessages(),
|
|
setState(() {}),
|
|
Navigator.of(MyApp.appNavigatorKey
|
|
.currentContext!)
|
|
.pop()
|
|
}),
|
|
)
|
|
])
|
|
],
|
|
),
|
|
),
|
|
));
|
|
}));
|
|
});
|
|
}
|
|
|
|
void _sendMessagesReadMessage() async {
|
|
final box = MyApp.objectBoxStore.box<sbmessage.ChatMessage>();
|
|
final query = box.query(ChatMessage_.read.equals(false)).build();
|
|
var unreadChatMessages = query.find();
|
|
query.close();
|
|
|
|
if (unreadChatMessages.isNotEmpty) {
|
|
List<String> chatMessageIds = [];
|
|
for (var message in unreadChatMessages) {
|
|
chatMessageIds.add(message.externalId.toString());
|
|
}
|
|
|
|
var chatMessagesShowed = ChatMessagesReceived();
|
|
chatMessagesShowed.ids = chatMessageIds;
|
|
chatMessagesShowed.time = Util.nowUnixSecondsUtc();
|
|
|
|
sendMessage(
|
|
routingKey: Constants.messageTopicPublishPayload,
|
|
payload: PublishPayloadMessage(
|
|
topic: "mssg_received",
|
|
message: chatMessagesShowed.toJson(),
|
|
acknowledgeRequired: false),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
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,
|
|
"Nachrichten",
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 18,
|
|
color: Colors.white),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|