Files
stadtbote-app/lib/content_views/messages_page.dart
T

327 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) {
// Der Server erwartet seine Nachrichten-ID, nicht die lokale ObjectBox-ID.
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),
),
),
),
],
),
);
}
}