hardened API decoding against server fields the models did not expect
The Talk room list crashed for a new account because the server omits
lastMessage for rooms without a visible message and the model required it.
Beyond that single field, a review of the Talk models against the OpenAPI
spec of the installed version (23.0.8) found the same crash class in several
places: closed enums over open server values (inCall bitmask, rich-object
types, actor and message types), a deleted parent sent as {id, deleted},
and the autocomplete status arriving as an object.
- json_serializable `checked: true`: decode errors now name class and field
- lists (rooms, messages, participants, autocomplete) decode per entry;
a malformed entry is skipped and reported via ClientErrorReporter
- unknownEnumValue fallbacks for all response enums of Talk and breakers
- lastMessage nullable; inCall as bitmask; deleted parent placeholder;
autocomplete status object/string; fixed shareWithDisplayNameUnique key
- GetRoomParams no longer sends null query parameters
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HrepDTL4qDe35TuS29sLW3
This commit is contained in:
@@ -121,6 +121,7 @@ class _ChatBubbleState extends State<ChatBubble>
|
||||
case GetRoomResponseObjectMessageType.comment:
|
||||
case GetRoomResponseObjectMessageType.voiceMessage:
|
||||
case GetRoomResponseObjectMessageType.command:
|
||||
case GetRoomResponseObjectMessageType.unknown:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../api/marianumcloud/talk/actions/talk_actions.dart';
|
||||
import '../../../../api/marianumcloud/talk/chat/get_chat_response.dart';
|
||||
import '../../../../api/marianumcloud/talk/chat/rich_object_string_processor.dart';
|
||||
import '../../../../api/marianumcloud/talk/room/get_room_response.dart';
|
||||
import '../../../../api/marianumcloud/talk/room/notification_level.dart';
|
||||
@@ -69,8 +70,7 @@ class _ChatTileState extends State<ChatTile> {
|
||||
/// Memoised per last message: the tile rebuilds on every chat-list emit and
|
||||
/// the Markdown strip is a full parse. Keyed by content, since every refresh
|
||||
/// delivers new message objects.
|
||||
String _lastMessagePreview() {
|
||||
final last = widget.data.lastMessage;
|
||||
String _lastMessagePreview(GetChatResponseObject last) {
|
||||
final key = (last.id, last.message, last.markdown);
|
||||
if (key == _previewFor) return _preview;
|
||||
final text = RichObjectStringProcessor.parseToString(
|
||||
@@ -84,9 +84,16 @@ class _ChatTileState extends State<ChatTile> {
|
||||
Object? _previewFor;
|
||||
String _preview = '';
|
||||
|
||||
String _subtitle() {
|
||||
final last = widget.data.lastMessage;
|
||||
if (last == null) return 'Noch keine Nachrichten';
|
||||
return '${last.sentAt.formatRelative()}: ${_lastMessagePreview(last)}';
|
||||
}
|
||||
|
||||
Future<void> _setCurrentAsRead() async {
|
||||
final token = widget.data.token;
|
||||
final lastId = widget.data.lastMessage.id;
|
||||
final lastId = widget.data.lastMessage?.id;
|
||||
if (lastId == null) return;
|
||||
context.read<ChatListBloc>().markRoomAsRead(token, lastId);
|
||||
unawaited(NotificationTasks.clearNotificationsForChat(token));
|
||||
await context.read<ChatBloc>().sendServerReadMarker(token, lastId);
|
||||
@@ -173,11 +180,7 @@ class _ChatTileState extends State<ChatTile> {
|
||||
],
|
||||
],
|
||||
),
|
||||
subtitle: Text(
|
||||
'${widget.data.lastMessage.sentAt.formatRelative()}: '
|
||||
'${_lastMessagePreview()}',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
subtitle: Text(_subtitle(), overflow: TextOverflow.ellipsis),
|
||||
trailing: widget.data.unreadMessages <= 0
|
||||
? null
|
||||
: Semantics(
|
||||
|
||||
Reference in New Issue
Block a user