import 'package:bubble/bubble.dart'; import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; import 'package:marianum_mobile/api/marianumcloud/talk/chat/richObjectStringProcessor.dart'; import 'package:marianum_mobile/api/marianumcloud/talk/room/getRoomResponse.dart'; import 'package:marianum_mobile/api/marianumcloud/talk/sendMessage/sendMessage.dart'; import 'package:marianum_mobile/api/marianumcloud/talk/sendMessage/sendMessageParams.dart'; import 'package:marianum_mobile/data/chatList/chatProps.dart'; import 'package:provider/provider.dart'; class ChatView extends StatefulWidget { final GetRoomResponseObject room; final String selfId; final CircleAvatar avatar; const ChatView({Key? key, required this.room, required this.selfId, required this.avatar}) : super(key: key); @override State<ChatView> createState() => _ChatViewState(); } class _ChatViewState extends State<ChatView> { static const styleSystem = BubbleStyle( color: Color(0xffd4eaf4), borderWidth: 1, elevation: 2, margin: BubbleEdges.only(top: 15), alignment: Alignment.center, ); static BubbleStyle getStyleOther(bool seamless) { return BubbleStyle( nip: BubbleNip.leftTop, color: seamless ? Colors.transparent : Colors.white, borderWidth: seamless ? 0 : 1, elevation: seamless ? 0 : 1, margin: const BubbleEdges.only(top: 15, left: 10, right: 50), alignment: Alignment.topLeft, ); } static BubbleStyle getStyleSelf(bool seamless) { return BubbleStyle( nip: BubbleNip.rightBottom, color: seamless ? Colors.transparent : const Color(0xffd9fdd3), borderWidth: seamless ? 0 : 1, elevation: seamless ? 0 : 1, margin: const BubbleEdges.only(top: 15, right: 10, left: 50), alignment: Alignment.topRight, ); } final ScrollController _listController = ScrollController(); final TextEditingController _textBoxController = TextEditingController(); bool sending = false; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { Provider.of<ChatProps>(context, listen: false).setQueryToken(widget.room.token); }); } @override Widget build(BuildContext context) { return Consumer<ChatProps>( builder: (context, data, child) { List<Bubble> messages = List<Bubble>.empty(growable: true); if(!data.primaryLoading()) { bool showActorDisplayName = true; bool showBubbleTime = true; data.getChatResponse.sortByTimestamp().forEach((element) { showActorDisplayName = element.messageType == GetRoomResponseObjectMessageType.comment && widget.room.type != GetRoomResponseObjectConversationType.oneToOne; showBubbleTime = element.messageType != GetRoomResponseObjectMessageType.system; BubbleStyle currentStyle; if(element.messageType == GetRoomResponseObjectMessageType.comment) { if(element.actorId == widget.selfId) { currentStyle = getStyleSelf(element.messageParameters?.containsKey("file") ?? false); } else { currentStyle = getStyleOther(element.messageParameters?.containsKey("file") ?? false); } } else { currentStyle = styleSystem; } var _actorTextStyle = TextStyle(color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold); messages.add(Bubble( margin: BubbleEdges.only(bottom: element == data.getChatResponse.sortByTimestamp().last ? 20 : 0), style: currentStyle, child: Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width * 0.9, minWidth: showActorDisplayName ? _textSize(element.actorDisplayName, _actorTextStyle).width : 30, ), child: Stack( children: [ Padding( padding: EdgeInsets.only(bottom: showBubbleTime ? 18 : 0, top: showActorDisplayName ? 18 : 0), child: FutureBuilder( future: RichObjectStringProcessor.parseAnyToWidget(element.message, element.messageParameters), builder: (context, snapshot) { if(!snapshot.hasData) return const CircularProgressIndicator(); return snapshot.data ?? const Icon(Icons.error); }, ) ), Visibility( visible: showActorDisplayName, child: Positioned( top: 0, left: 0, child: Text( element.actorDisplayName, textAlign: TextAlign.start, style: _actorTextStyle, ), ), ), Visibility( visible: showBubbleTime, child: Positioned( bottom: 0, right: 0, child: Text( Jiffy.unixFromSecondsSinceEpoch(element.timestamp).format("HH:mm"), textAlign: TextAlign.end, style: const TextStyle(color: Colors.grey, fontSize: 12), ), ), ), ], ), ), // Stack( // children: [ // Visibility( // visible: showMetadata, // child: Positioned( // top: 0, // left: 0, // child: Expanded(child: Text(element.actorDisplayName, style: TextStyle(fontWeight: FontWeight.bold, color: Theme.of(context).primaryColor))), // ), // ), // Padding( // padding: EdgeInsets.symmetric(vertical: showMetadata ? 18 : 0), // child: Text(RichObjectStringProcessor.parse(element.message, element.messageParameters)), // ), // Visibility( // visible: showMetadata, // child: Positioned( // bottom: 0, // right: 0, // child: Text( // "${Jiffy.unixFromSecondsSinceEpoch(element.timestamp).yMMMMd} - ${Jiffy.unixFromSecondsSinceEpoch(element.timestamp).format("HH:mm")}", // style: TextStyle(color: Theme.of(context).disabledColor), // ), // ), // ), // ], // ), )); }); } return Scaffold( backgroundColor: const Color(0xffefeae2), appBar: AppBar( title: Row( children: [ widget.avatar, const SizedBox(width: 10), Expanded( child: Text(widget.room.displayName, overflow: TextOverflow.ellipsis, maxLines: 1), ) ], ), ), body: Container( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage("assets/background/chat.png"), scale: 1.5, opacity: 0.5, repeat: ImageRepeat.repeat, colorFilter: ColorFilter.linearToSrgbGamma() ) ), child: data.primaryLoading() ? const Center(child: CircularProgressIndicator()) : Column( children: [ Expanded( child: ListView( reverse: true, controller: _listController, children: messages.reversed.toList(), ), ), Container( color: Theme.of(context).dividerColor, padding: const EdgeInsets.all(10), child: Row( children: [ Expanded( child: TextField( controller: _textBoxController, readOnly: sending, maxLines: null, decoration: const InputDecoration( hintText: "Nachricht", border: OutlineInputBorder(), labelText: "", ), ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: sending ? const Center(child: CircularProgressIndicator()) : IconButton( onPressed: () { if(_textBoxController.text.isEmpty) return; setState(() { sending = true; }); SendMessage(widget.room.token, SendMessageParams(_textBoxController.text)).run().then((value) => { Provider.of<ChatProps>(context, listen: false).run(), _textBoxController.text = "", setState(() { sending = false; }), }); }, icon: const Icon(Icons.send) ), ) ], ), ) ], ) ), ); }, ); } Size _textSize(String text, TextStyle style) { final TextPainter textPainter = TextPainter( text: TextSpan(text: text, style: style), maxLines: 1, textDirection: TextDirection.ltr) ..layout(minWidth: 0, maxWidth: double.infinity); return textPainter.size; } }