import 'package:flutter/material.dart'; import '../../../../api/marianumcloud/talk/chat/get_chat_response.dart'; import '../../../../api/marianumcloud/talk/chat/rich_object_string_processor.dart'; import '../../../../utils/emoji_detection.dart'; import '../../../../utils/url_opener.dart'; import '../widgets/chat_file_attachment.dart'; import '../widgets/highlighted_linkify.dart'; import '../widgets/message_markdown.dart'; class ChatMessage { final String originalMessage; final Map? originalData; final RichObjectString? file; final String content; ChatMessage({required this.originalMessage, this.originalData}) : file = originalData?['file'], content = RichObjectStringProcessor.parseToString( originalMessage, originalData, ); Widget getWidget({ String? highlightQuery, TextStyle? style, bool renderMarkdown = false, }) { final poll = originalData?['object']; if (poll?.type == RichObjectStringObjectType.talkPoll) { return ListTile( leading: const Icon(Icons.poll_outlined), title: Text(poll!.name), contentPadding: const EdgeInsets.only(left: 10), ); } final emojiFontSize = standaloneEmojiFontSize(content); final effectiveStyle = emojiFontSize == null ? style : (style ?? const TextStyle()).copyWith( fontSize: emojiFontSize, height: 1.15, ); // Markdown replaces the plain linkified text only for regular messages. // Emoji-only bodies keep their enlarged rendering, and while searching we // fall back to the linkify path so matches can still be highlighted (the // search highlight can't be threaded through Markdown formatting). final hasQuery = highlightQuery?.trim().isNotEmpty ?? false; final useMarkdown = renderMarkdown && emojiFontSize == null && !hasQuery; var contentWidget = useMarkdown ? MessageMarkdown(data: content, style: effectiveStyle) : HighlightedLinkify( text: content, onOpen: UrlOpener.onOpen, highlight: highlightQuery, style: effectiveStyle, ); // Enlarged emoji glyphs otherwise sit flush against the bubble edges. if (emojiFontSize != null) { contentWidget = Padding( padding: const EdgeInsets.symmetric(horizontal: 6), child: contentWidget, ); } final file = this.file; if (file == null) return contentWidget; return Padding( padding: const EdgeInsets.only(top: 5), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ChatFileAttachment(file: file), if (originalMessage != '{file}') ...[ SizedBox(height: 5), contentWidget, ], ], ), ); } }