import 'dart:async'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; import 'package:provider/provider.dart'; import 'package:selectable_autolink_text/selectable_autolink_text.dart'; import 'package:telnow_mobile_new/src/api/api_auth_provider.dart'; import 'package:telnow_mobile_new/src/layouts/functions/message.dart'; import 'package:telnow_mobile_new/src/layouts/components/template.dart'; import 'package:telnow_mobile_new/src/utils/C.dart'; import 'package:telnow_mobile_new/src/utils/U.dart'; import 'package:telnow_mobile_new/src/utils/provider.dart'; import 'package:translator/translator.dart'; import 'package:url_launcher/url_launcher.dart'; import 'dart:ui' as ui; import 'package:uuid/uuid.dart'; import '../components/photo_chat.dart'; class WebMessageListPage extends StatefulWidget { final Map? user; const WebMessageListPage(this.user, {super.key}); @override State createState() => _WebMessageListPageState(); } class _WebMessageListPageState extends State { final MessageFunction messageFunc = MessageFunction(); final ApiAuthProvider apiAuthProvider = ApiAuthProvider(); final translator = GoogleTranslator(); StreamSubscription? subscription; String? idChat; String tenant = ''; String opponent = ''; ScrollController scrollController = ScrollController(); ScrollController forumScrollController = ScrollController(); List messageData = []; List forumData = []; int page = 0; bool isAfterLoad = false; String username = ''; bool isLoad = false; bool stopLoad = false; bool scrollBottom = false; bool forumScrollBottom = false; bool isReverse = false; bool isForumReverse = false; bool askBroadcast = false; bool openChat = false; bool isSend = false; double borderRadius = 10.0; Uint8List? _image; String forumHeader = ''; String activeForumId = ''; String forumChatId = ''; TextEditingController controllerForum = new TextEditingController(); String locale = 'id'; late MessageModule messageModule; @override void initState() { messageModule = Provider.of(context, listen: false); messageModule.reset(); if(widget.user == null){ messageFunc.getUser(context); } else { messageModule.setUser(widget.user!); messageFunc.getDataMessages(context); } username = messageModule.user()['userId']; // TODO: implement initState super.initState(); } @override void dispose() { subscription?.cancel(); super.dispose(); } selectMessage(data, isMe) async{ if(idChat != data['chatId']){ setState(() { idChat = data['chatId']; askBroadcast = isMe; opponent = isMe ? data['recipientName'] : data['senderName']; tenant = data['recipient']; messageData = []; page = 0; isAfterLoad = false; isLoad = false; stopLoad = false; scrollBottom = false; isReverse = false; }); scrollController.addListener(() => scrollListener()); if (!isMe) { await apiAuthProvider.patchData('/api/messages/' + data['id'].toString(), {'readStatus': 'READ'}); } getMessage(); getCollectionData(); } } selectMessageForum(dt){ forumChatId = '${U.decodeBase64Url(U.getAccessCode()!)}-${dt['ticketNo']}'; activeForumId = dt['ticketNo']; openChat = !(dt['currentState'] == 'DIBATALKAN' || dt['currentState'] == 'TUNTAS'); forumHeader = dt[U.langColumn(context, 'requestSubject')]; forumScrollController.addListener(() => forumScrollListener()); forumData = []; page = 0; stopLoad = false; // unsub dulu biar ga double2 subscription?.cancel(); subscription = null; getMessageForum(); getCollectionDataForum(); } getMessage() async { String url = 'myMessages/$idChat'; String filter = '{"f":["uniqueId","LIKE","%-%"]}'; if (askBroadcast) { url = 'myBroadcast'; filter = '{"f":["1","EQ","1"]}'; } if (!isLoad && !stopLoad) { isLoad = true; var mymess = await apiAuthProvider.getData('/api/messages/search/$url', {'isPaged': 'true', 'page': page.toString(), 'size': '20', 'filter': filter, 'tenant': tenant}); if (mymess.containsKey('_embedded') && mounted) { List data = mymess['_embedded']['myMessages']; for (int i = 0; i < data.length; i++) { if (username == data[i]['from']['user']) { data[i]['selected'] = false; } else{ if(U.autoTranslate()){ data[i]['translate'] = ''; } } messageData.insert(0, data[i]); } setState(() { if (page == 0) { scrollBottom = true; } isLoad = false; page++; if (messageData.length >= mymess['page']['totalElements']) { stopLoad = true; } }); if(U.autoTranslate()){ translateMessage(0); } } else { setState(() { isLoad = false; stopLoad = true; }); } } } getMessageForum()async{ if(stopLoad) return; try{ var mymess = await apiAuthProvider.getData( '/api/messages/search/myMessages/${forumChatId}', {'isPaged': 'true', 'page': page.toString(), 'size': '20'}); if (mymess.containsKey('_embedded')) { List data = mymess['_embedded']['myMessages']; for (var item in data) { if (username == item['from']['user'] && item['senderType'] != 'SERVANT') { item['selected'] = false; } else { if(U.autoTranslate()){ item['translate'] = ''; } } final exists = forumData.any((e) => e['id'] == item['id']); if (!exists) { forumData.insert(0, item); } } if(mymess['page']['number'] == page) page++; if(mounted) { setState(() { isAfterLoad = true; }); } } if(U.autoTranslate()){ translateMessage(1); } if (forumData.length >= mymess['page']['totalElements']) { stopLoad = true; } } catch(e) { print(e.toString()); } } getCollectionData() { FirebaseFirestore.instance.collection("tmMessages").doc('messages').collection(idChat!).snapshots().listen((querySnapshot) { querySnapshot.docChanges.forEach((result) async { var data = result.doc.data(); if (result.type == DocumentChangeType.added && isAfterLoad) { if ((username == data!['from']['user'] && messageData.where((element) => element['uniqueId'] == data['uniqueId']).isNotEmpty) || (username != data['from']['user'] && data['readStatus'] == 'DELETED')) { int index = messageData.indexWhere((element) => element['uniqueId'] == data['uniqueId']); messageData[index]['read'] = data['read']; messageData[index]['readStatus'] = data['readStatus']; messageData[index]['imageUrl'] = data['imageUrl']; } else { try{ if(U.autoTranslate()){ var loc = locale == 'zh' ? 'zh-cn' : locale; var translate = await translator.translate(data['msg']??'', to: loc); data['translate'] = translate.text; } }catch(e){ print("Error: ${e.toString()}"); } messageData.add(data); } } else if (result.type == DocumentChangeType.modified && isAfterLoad) { // print("modified"); if (messageData.where((element) => element['uniqueId'] == data!['uniqueId']).isNotEmpty) { int index = messageData.indexWhere((element) => element['uniqueId'] == data!['uniqueId']); messageData[index]['read'] = data!['read']; messageData[index]['readStatus'] = data['readStatus']; } } else if (result.type == DocumentChangeType.removed && isAfterLoad) { // print("removed"); } }); if(mounted) { setState(() { isAfterLoad = true; }); } }); } getCollectionDataForum() { subscription = FirebaseFirestore.instance.collection("tmMessages").doc('messages').collection(forumChatId).snapshots().listen((querySnapshot) { querySnapshot.docChanges.forEach((result) async { var data = result.doc.data(); print(data); if(!mounted) return; if (result.type == DocumentChangeType.added && isAfterLoad) { if((username == data!['from']['user'] && forumData.where((element) => element['uniqueId'] == data['uniqueId']).isNotEmpty) || (username != data['from']['user'] && data['readStatus'] == 'DELETED')) { int index = forumData.indexWhere((element) => element['uniqueId'] == data['uniqueId']); forumData[index]['read'] = data['read']; forumData[index]['readStatus'] = data['readStatus']; forumData[index]['imageUrl'] = data['imageUrl']; } else { try{ if(U.autoTranslate()){ var loc = locale == 'zh' ? 'zh-cn' : locale; var translate = await translator.translate(data['msg']??'', to: loc); data['translate'] = translate.text; } }catch(e){ print("Error f: ${e.toString()}"); } forumData.add(data); updateMessageDisplay(data); } } else if (result.type == DocumentChangeType.modified && isAfterLoad) { // print("modified"); if (forumData.where((element) => element['uniqueId'] == data!['uniqueId']).isNotEmpty) { int index = forumData.indexWhere((element) => element['uniqueId'] == data!['uniqueId']); forumData[index]['read'] = data!['read']; forumData[index]['readStatus'] = data['readStatus']; } } else if (result.type == DocumentChangeType.removed && isAfterLoad) { // print("removed"); } if(mounted) setState(() {}); }); if(mounted) { setState(() { isAfterLoad = true; }); } }); } void updateMessageDisplay(Map data) { final forum = messageModule.forum(); final res = >[]; for (int i = 0; i < forum.length; i++) { final tmp = Map.from(forum[i]); if (tmp['ticket']['ticketNo'] == activeForumId) { tmp['msg'] = data['msg']; } res.add(tmp); } messageModule.setForum(res); } translateMessage(int t){ var dt = t == 0 ? messageData : forumData; var loc = locale == 'zh' ? 'zh-cn' : locale; dt.forEach((element) async { if (element['senderType'] == 'SERVANT' && element['translate'] == '') { //&& username != element['from']['user'] try { var translate = await translator.translate( element['msg'] ?? '', to: loc); element['translate'] = translate.text; }catch(e){ element['translate'] = ''; } setState(() {}); } }); } deleteCollection() { var ids = [idChat, forumChatId]; for(int i=0; i < ids.length; i++){ var id = ids[i]??''; if(id != '') { try { FirebaseFirestore.instance.collection("tmMessages").doc('messages') .collection(id).get() .then((value) { for (DocumentSnapshot ds in value.docs) { ds.reference.delete(); } }); } catch (e) {} } } } scrollToBottom() { if(scrollController.hasClients) { scrollController.animateTo(scrollController.position.minScrollExtent, duration: Duration(milliseconds: 1), curve: Curves.decelerate); scrollBottom = false; } } scrollListener() { if (scrollController.hasClients && scrollController.offset >= scrollController.position.maxScrollExtent) { // print('loadMessage'); getMessage(); } } forumScrollListener() { if(forumScrollController.hasClients && forumScrollController.offset >= forumScrollController.position.maxScrollExtent) { getMessageForum(); } } @override Widget build(BuildContext context) { locale = context.locale.toString(); if (messageData.length > 0) { try{ if (scrollController.hasClients && scrollController.position.maxScrollExtent > 0 && !isReverse) { setState(() => isReverse = true); } if (isAfterLoad && scrollBottom && isReverse) { scrollToBottom(); } }catch(e){ print(e.toString()); } } Widget tabForum(){ return Row( mainAxisAlignment: MainAxisAlignment.start, children: [ GestureDetector( onTap: () { Provider.of(context, listen: false).setActiveTab(0); }, child: Container( margin: EdgeInsetsDirectional.only(start: 100), padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16), decoration: BoxDecoration( color: Colors.white, border: Border(bottom: BorderSide(color: Provider.of(context).activeTab() == 0 ? primaryColor : primaryColor.withValues(alpha: 0.3), width: Provider.of(context).activeTab() == 0 ? 2 : 1))), child: Text('chat'.tr(), style: TextStyle(color: Provider.of(context).activeTab() == 0 ? textColor : textColor.withValues(alpha: 0.65), fontSize: 16)), ), ), GestureDetector( onTap: () { Provider.of(context, listen: false).setActiveTab(1); }, child: Container( padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16), decoration: BoxDecoration( color: Colors.white, border: Border(bottom: BorderSide(color: Provider.of(context).activeTab() == 1 ? primaryColor : primaryColor.withValues(alpha: 0.3), width: Provider.of(context).activeTab() == 1 ? 2 : 1))), child: Text('forum'.tr(), style: TextStyle(color: Provider.of(context).activeTab() == 1 ? textColor : textColor.withValues(alpha: 0.65), fontSize: 16)), ), ), ], ); } // Widget chat(){ // final messageModule = Provider.of(context); // final dataList = messageModule.data(); // final currentUser = messageModule.user(); // final forumList = messageModule.forum(); // // return dataList.isEmpty ? Expanded( // child: Center( // child: Text('noMessageText'.tr()), // ), // ) : SingleChildScrollView( // padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10), // child: Column( // mainAxisSize: MainAxisSize.max, // children: List.generate(dataList.length, (i) { // final item = dataList[i]; // final isMe = item['userId'] == currentUser['userId']; // final avatar = item['senderAvatar']; // final chatIdMatch = idChat != null && idChat == item['chatId']; // final recipientColor = U.getColor(isMe ? item['recipient'] : item['recipientId']); // final recipientName = item['recipientName']; // final senderName = item['senderName']; // final lastText = item['lastText'] ?? ''; // final isImage = item['isImage'] ?? false; // final fileType = item['fileType'] ?? ''; // final lastDate = item['lastDateTimeSend']; // final lastReadStatus = item['lastReadStatus']; // final forumReadStatus = forumList[i]['readStatus']; // // return GestureDetector( // onTap: () => selectMessage(item, isMe), // child: Container( // padding: EdgeInsets.all(10), // decoration: BoxDecoration( // color: chatIdMatch ? Color(0xff26DA17).withAlpha(50) : Colors.white, // borderRadius: BorderRadius.circular(12), // ), // child: Row( // children: [ // avatar != null && avatar != '' // ? CircleAvatar( // backgroundImage: NetworkImage(avatar), // radius: 24, // ) // : Container( // height: 48, // width: 48, // decoration: BoxDecoration( // shape: BoxShape.circle, // color: Color(recipientColor), // ), // child: Center( // child: Text( // isMe // ? recipientName == "all_informants" // ? "allInformants".tr()[0] // : recipientName[0] // : senderName[0], // style: TextStyle( // color: Colors.white, // fontWeight: FontWeight.bold, // fontSize: 18, // ), // ), // ), // ), // SizedBox(width: 20), // Expanded( // child: Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Text( // isMe // ? recipientName == "all_informants" // ? "allInformants".tr() // : recipientName // : senderName, // style: TextStyle(fontWeight: FontWeight.w600), // ), // SizedBox(height: 5), // Row( // children: [ // if (isMe) // Padding( // padding: EdgeInsets.only(right: 5), // child: Icon(Icons.done_all, size: 18, color: Colors.blueGrey), // ), // if (isImage) // Row( // children: [ // Icon( // fileType == 'pdf' ? Icons.picture_as_pdf : Icons.image, // color: Colors.black45, // size: 16, // ), // SizedBox(width: 6), // if (lastText == '') // Text(fileType == 'pdf' ? 'pdfFile'.tr() : 'photo'.tr()), // ], // ), // Expanded( // child: Text( // lastText, // style: TextStyle(fontSize: 13), // maxLines: 1, // overflow: TextOverflow.ellipsis, // ), // ), // ], // ), // ], // ), // ), // SizedBox(width: 20), // Column( // crossAxisAlignment: CrossAxisAlignment.end, // children: [ // Text( // convertDate(lastDate, context.locale.toString()), // style: TextStyle( // fontSize: 11, // fontWeight: forumReadStatus == 'UNREAD' ? FontWeight.w600 : FontWeight.w300, // color: lastReadStatus == 'UNREAD' && !isMe ? primaryColor : Colors.black45, // ), // ), // SizedBox(height: 12), // Icon( // Icons.circle, // size: 12, // color: primaryColor.withAlpha(lastReadStatus == 'UNREAD' && !isMe ? 1 : 0), // ), // ], // ), // ], // ), // ), // ); // }), // ), // ); // } Widget webChat(BuildContext context) { final messageModule = Provider.of(context); final dataList = messageModule.data(); final currentUser = messageModule.user(); final isFirstLoad = messageModule.firstLoad(); return Container( padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10), alignment: Alignment.topCenter, child: dataList.isEmpty && !isFirstLoad ? loadingTemplateNoVoid() : dataList.isEmpty ? Center(child: Text('noMessageText'.tr())) : ListView.builder( itemCount: dataList.length, itemBuilder: (context, i) { final item = dataList[i]; final isMe = item['userId'] == currentUser['userId']; final avatar = item['senderAvatar']; final recipientName = item['recipientName']; final senderName = item['senderName']; final lastText = item['lastText'] ?? ''; final isImage = item['isImage'] ?? false; final fileType = item['fileType'] ?? ''; final lastDate = item['lastDateTimeSend']; final lastReadStatus = item['lastReadStatus']; final chatId = item['chatId']; final recipientId = item['recipientId']; final recipient = item['recipient']; // final id = item['id']; return GestureDetector( onTap: () { selectMessage(item, isMe); // navigateTo( // context, // MobMessageChatPage( // currentUser, // chatId, // isMe ? recipientName : senderName, // id.toString(), // isMe, // recipient, // ), // ); messageFunc.onRefresh(context); }, child: Container( padding: EdgeInsets.all(10), margin: EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: idChat != null && idChat == chatId ? Color(0xff26DA17).withAlpha(50) : Colors.white, borderRadius: BorderRadius.circular(12), ), child: Row( children: [ avatar != null && avatar != '' ? CircleAvatar( backgroundImage: NetworkImage(avatar), radius: 24, ) : Container( height: 48, width: 48, decoration: BoxDecoration( shape: BoxShape.circle, color: Color(U.getColor(isMe ? recipient : recipientId)), ), child: Center( child: Text( isMe ? recipientName == "all_informants" ? "allInformants".tr()[0] : recipientName[0] : senderName[0], style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18, ), ), ), ), SizedBox(width: 20), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( isMe ? recipientName == "all_informants" ? "allInformants".tr() : recipientName : senderName, style: TextStyle(fontWeight: FontWeight.w600), ), SizedBox(height: 5), Row( children: [ if (isMe) Padding( padding: EdgeInsets.only(right: 4), child: Icon(Icons.done_all, size: 18, color: Colors.blueGrey), ), if (isImage) Row( children: [ Icon( fileType == 'pdf' ? Icons.picture_as_pdf : Icons.image, color: Colors.black45, size: 16, ), SizedBox(width: 6), if (lastText == '') Text(fileType == 'pdf' ? 'pdfFile'.tr() : 'photo'.tr()), ], ), Expanded( child: Text( lastText, style: TextStyle(fontSize: 13), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), ], ), ), SizedBox(width: 20), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( messageFunc.timeSet(lastDate), style: TextStyle( fontSize: 11, color: lastReadStatus == 'UNREAD' && !isMe ? Colors.green : Colors.black45, ), ), SizedBox(height: 4), Icon( Icons.circle, color: Colors.green.withAlpha(lastReadStatus == 'UNREAD' && !isMe ? 1 : 0), ), ], ), ], ), ), ); }, ), ); } // tampilan list pesan forum di sebelah kiri Widget forum(){ final messageModule = Provider.of(context); return messageModule.forum().isEmpty ? Center( child: Text('noMessageText'.tr()), ) : SingleChildScrollView( padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10), child: Column( mainAxisSize: MainAxisSize.max, children: List.generate(messageModule.forum().length, (i){ bool isMe0 = messageModule.forum()[i]['userId'] == messageModule.user()['userId'] ? true : false; return GestureDetector( onTap: (){ var dt = messageModule.forum()[i]['ticket']; messageFunc.setAsRead(context, dt['ticketNo']); messageModule.forum()[i]['readStatus'] = 'READ'; selectMessageForum(dt); setState(() { isAfterLoad = false; forumData = []; }); }, child: Container( decoration: BoxDecoration( color: activeForumId != '' && messageModule.forum()[i]['ticket']['ticketNo'] == activeForumId ? Color(0xff26DA17).withValues(alpha: 0.2) : Colors.white, borderRadius: BorderRadius.all(Radius.circular(12)), ), width: double.infinity, child: Column( children: [ Padding( padding: const EdgeInsets.all(10.0), child: Row( children: [ Container( height: 48, width: 48, decoration: BoxDecoration( image: DecorationImage(image: NetworkImage(messageModule.forum()[i]['ticket']['_requestImage'])), // color: Colors.green, border: Border.all(color: Colors.black12), borderRadius: BorderRadius.circular(50), ), ), SizedBox(width: 20,), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ Flexible( child: Text( messageModule.forum()[i]['ticket'][U.langColumn(context, 'requestSubject')], maxLines: 1, overflow: TextOverflow.ellipsis, softWrap: false, style: TextStyle( fontWeight: FontWeight.w600, ), ), ), SizedBox(width: 4), Text( convertDate(messageModule.forum()[i]['datetime'], context.locale.toString()), style: TextStyle( fontSize: 11, fontWeight: messageModule.forum()[i]['readStatus']=='UNREAD' ? FontWeight.w600 : FontWeight.w300, color: messageModule.forum()[i]['readStatus']=='UNREAD' && !isMe0 ? primaryColor : Colors.black45, ), ), ], ), SizedBox(height: 8,), Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible( child: Text( messageModule.forum()[i]['msg'], overflow: TextOverflow.ellipsis, maxLines: 2, ), ), SizedBox(width: 12,), Icon( Icons.circle, size: 12, color: messageModule.forum()[i]['readStatus']=='UNREAD' && !isMe0 ? primaryColor : Colors.transparent, ), ], ), ], ), ), ], ), ), ], ) ), ); }), ), ); } Widget chatList(){ return idChat != null ? Column( children: [ Container( width: double.infinity, padding: EdgeInsets.all(20), child: Text(opponent != 'all_informants' ? opponent : "allInformants".tr(), style: TextStyle(color: textColor, fontSize: 16)), ), divider(), Expanded( child: messageData.isEmpty && !isAfterLoad ? loadingTemplateNoVoid() : SingleChildScrollView( padding: const EdgeInsets.fromLTRB(10, 10, 10, 7), controller: scrollController, reverse: isReverse, child: Column( children: List.generate(messageData.length, (i) { bool hideDate = i == 0 ? false : checkDate(messageData[i]['datetime'], messageData[i - 1]['datetime']); bool isNip = i == 0 ? true : !hideDate ? true : messageData[i]['from']['user'] == messageData[i - 1]['from']['user'] ? false : true; return Column( children: [ !hideDate ? Center( child: Padding( padding: const EdgeInsets.all(10), child: bubble_chat(Text(convertDate(messageData[i]['datetime'], context.locale.toString()), textAlign: TextAlign.center, style: TextStyle(fontSize: 12)), null, false, false), ), ) : Container(), Builder(builder: (context) { var isMe = messageData[i]['from']['user'] == Provider.of(context, listen: false).user()['userId']; var isFile = messageData[i]['imageUrl'] != null && messageData[i]['imageUrl'] != '' && messageData[i]['readStatus'] != 'DELETED'; var isPdf = isFile && messageData[i]['imageUrl'].split('.').last == 'pdf'; var isImg = isFile && !isPdf; var isTranslate = U.autoTranslate() && !isMe && messageData[i]['msg'] != messageData[i]['translate']; var wdg = messageData[i]['readStatus'] == 'DELETED' ? Text('deletedMessage'.tr(), style: TextStyle(fontSize: 14, color: Colors.black54, fontStyle: FontStyle.italic)) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ isPdf ? GestureDetector( child: Builder(builder: (context) { var pdfContainer = Container( padding: EdgeInsets.all(6), decoration: BoxDecoration(color: Colors.black12.withValues(alpha: 0.1), borderRadius: BorderRadius.all(Radius.circular(4))), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.picture_as_pdf), SizedBox(width: 6), Flexible(child: Text(getImageName(messageData[i]['imageUrl']))) ], ), ); return pdfContainer; }), onTap: () async { await launchUrl( messageData[i]['imageUrl']); }, ) : isImg ? GestureDetector( child: Container( margin: const EdgeInsets.only(bottom: 5), child: Builder(builder: (context) { return Image.network(messageData[i]['imageUrl'], fit: BoxFit.cover, width: 200, height: 200); }) ), onTap: () => navigateTo(context, PhotoPreview(opponent, messageData[i]['imageUrl'], true)), ) : SizedBox(height: 1), messageData[i]['msg'] != null && messageData[i]['msg'] != '' ? Column( crossAxisAlignment: !isMe?CrossAxisAlignment.start:CrossAxisAlignment.end, children: [ SizedBox(height: 4), SelectableAutoLinkText( messageData[i]['msg'], style: TextStyle(fontSize: 14, color: Colors.black), linkStyle: TextStyle(color: Colors.blueAccent), highlightedLinkStyle: TextStyle(color: Colors.blueAccent, backgroundColor: Colors.blueAccent.withAlpha(0x33)), onTap: (link) async{ if (await canLaunchUrl(Uri.parse(link))) { await launchUrl(Uri.parse(link)); } }, onLongPress: (link) { Clipboard.setData(new ClipboardData(text: link)).then((value){ showSuccess('link_copied'.tr(), context); }); }, enableInteractiveSelection: false, ), isTranslate ? Container( margin: EdgeInsets.only(top: 2), decoration: BoxDecoration(border: Border(top: BorderSide(color: Colors.black.withValues(alpha: 0.2)))), child: Text(messageData[i]['translate'] != '' ? '(${messageData[i]['translate']})' : '...', style: TextStyle(fontSize: 14, color: Colors.black.withValues(alpha: 0.65), fontStyle: FontStyle.italic)), ) : Container(), ], ) : Container() ], ); var timeBubble = Positioned( bottom: 2, right: isNip ? 6 : 0, child: Text(DateFormat('HH:mm').format(DateTime.parse(messageData[i]['datetime'])), style: TextStyle(fontSize: 10, color: Colors.black45)), ); var expander = Expanded( flex: 8, child: Column( crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ bubble_chat(wdg, timeBubble, isNip, isMe), SizedBox(height: 5), ], ) ); return isMe ? Row( children: [ Expanded(flex: 2, child: Container()), expander, ], ) : Row( children: [ expander, Expanded(flex: 2, child: Container()), ], ); }), ], ); }), ), ), ) ], ) : Center( child: Text("noMessageText".tr()), ); } sendMessage(data) async { isSend = false; controllerForum.clear(); forumData.add({ 'msg': data['text'], 'datetime': DateTime.now().toString(), 'read': null, 'imageUrl': data['images'], 'readStatus': '', 'from': {'name': 'my name', 'user': data['userId']}, 'uniqueId': data['uniqueId'], 'selected': false, 'senderType': 'INFORMANT' }); scrollBottom = true; var res = await apiAuthProvider.postData('/api/messages', null, data); if (res != null) { int index = forumData.indexWhere((element) => element['uniqueId'] == data['uniqueId']); forumData[index]['read'] = false; } else { int index = forumData.indexWhere((element) => element['uniqueId'] == data['uniqueId']); forumData[index]['readStatus'] = 'FAILED'; } setState(() {}); } // tampilan list chat forum yg sebelah kanan Widget forumList(){ return activeForumId == '' ? Center(child: Text("noMessageText".tr())) : Column( children: [ Container( width: double.infinity, padding: EdgeInsets.all(20), child: Text(forumHeader, style: TextStyle(color: textColor, fontSize: 16)), ), divider(), Expanded( child: forumData.isEmpty && !isAfterLoad ? loadingTemplateNoVoid() : Align( alignment: Alignment.topCenter, child: SingleChildScrollView( padding: const EdgeInsets.fromLTRB(10, 10, 10, 7), controller: forumScrollController, reverse: true, child: Column( children: List.generate(forumData.length, (i) { bool hideDate = i == 0 ? false : checkDate(forumData[i]['datetime'], forumData[i - 1]['datetime']); bool isNip = i == 0 ? true : !hideDate ? true : forumData[i]['from']['user'] == forumData[i - 1]['from']['user'] ? false : true; return Column( children: [ !hideDate ? Container(margin: EdgeInsets.only(bottom: 5), child: bubble_chat(Text(convertDate(forumData[i]['datetime'], context.locale.toString()), textAlign: TextAlign.center, style: TextStyle(fontSize: 12)), null, false, false)) : Container(), Builder(builder: (context) { var isMe = username == forumData[i]['from']['user'] && forumData[i]['senderType'] == 'INFORMANT'; var isImage = forumData[i]['imageUrl'] != null && forumData[i]['imageUrl'] != '' && forumData[i]['readStatus'] != 'DELETED'; var isTranslate = U.autoTranslate() && !isMe && forumData[i]['msg'] != forumData[i]['translate']; var widget = Row( mainAxisSize: MainAxisSize.min, children: [ forumData[i]['readStatus'] == 'FAILED' ? Padding( padding: const EdgeInsets.only(right: 15), child: PopupMenuButton( itemBuilder: (context) => [ PopupMenuItem(value: 1, height: 40, child: Text("delete".tr(), style: TextStyle(fontSize: 14))), PopupMenuItem(value: 2, height: 40, child: Text("resend".tr(), style: TextStyle(fontSize: 14))), ], offset: Offset(MediaQuery.of(context).size.width, 0), onSelected: (value) { if (value == 1) { forumData.removeWhere((item) => item['uniqueId'] == forumData[i]['uniqueId']); } else if (value == 2) { var text = forumData[i]['msg']; var images = forumData[i]['imageUrl']; forumData.removeWhere((item) => item['uniqueId'] == forumData[i]['uniqueId']); var uuid = Uuid().v1().replaceAll('-', ''); var data = { "uniqueId": uuid, "userId": username, "recipientId": "#forum", "senderType": "INFORMANT", "text": text, "chatId": idChat, "images": images }; sendMessage(data); } }, child: Icon(Icons.refresh, color: Colors.red), ), ) : Container(), Flexible( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ !isMe ? Padding( padding: const EdgeInsets.only(bottom: 6.0,), child: Text( forumData[i]['from']['name'], style: TextStyle(color: Color(U.getColor(forumData[i]['from']['user'])), fontSize: 12.0), textAlign: TextAlign.start, ), ) : SizedBox(), isImage ? Padding( padding: EdgeInsets.only(bottom: 2), child: GestureDetector( child: Container( margin: const EdgeInsets.only(bottom: 5), child: Builder(builder: (context) { return Image.network(forumData[i]['imageUrl']); }), ), onTap: () => navigateTo(context, PhotoPreview('forum'.tr(), forumData[i]['imageUrl'], true)), ), ) : Container(width: 1), forumData[i]['readStatus'] == 'DELETED' ? Text('deletedMessage'.tr(), style: TextStyle(fontSize: 14, color: Colors.white70, fontStyle: FontStyle.italic)) : forumData[i]['msg'] != null && forumData[i]['msg'] != '' ? SelectableAutoLinkText( forumData[i]['msg'], style: TextStyle(fontSize: 14, color: Colors.black), linkStyle: TextStyle(color: Colors.blueAccent), highlightedLinkStyle: TextStyle(color: Colors.blueAccent, backgroundColor: Colors.blueAccent.withAlpha(0x33)), onTap: (link) async{ if (await canLaunchUrl(Uri.parse(link))) { await launchUrl(Uri.parse(link)); } }, onLongPress: (link) { Clipboard.setData(new ClipboardData(text: link)).then((value){ showSuccess('link_copied'.tr(), context); }); }, textAlign: TextAlign.start, ) : Container(width: 1), isTranslate ? Container( margin: EdgeInsets.only(top: 2), decoration: BoxDecoration(border: Border(top: BorderSide(color: Colors.black.withValues(alpha: 0.2)))), child: Text(forumData[i]['translate']!=''?'(${forumData[i]['translate']})':'...', style: TextStyle(fontSize: 14, color: Colors.black.withValues(alpha: 0.65), fontStyle: FontStyle.italic)), ) : Container(), ], ), ) ], ); var timeBubble = RichText( text: TextSpan(children: [ TextSpan(text: DateFormat('HH:mm').format(DateTime.parse(forumData[i]['datetime'])), style: TextStyle(fontSize: 12, color: Colors.black38)), WidgetSpan( child: Padding( padding: const EdgeInsets.only(left: 3), child: isMe ? Icon( forumData[i]['read'] == null ? Icons.done : Icons.done_all, color: forumData[i]['read'] != null && forumData[i]['read'] ? Colors.green : Colors.black38, size: 15, ) : SizedBox() ) ), ]), ); var expander = Expanded( flex: 8, child: Column( crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ bubble_chat(widget, timeBubble, isNip, isMe), SizedBox(height: 5), ], ) ); return isMe ? Row( children: [ Expanded( flex: 2, child: Container(), ), expander, ], ) : Row( children: [ expander, Expanded( flex: 2, child: Container(), ), ], ); }) ], ); }), ), ), ), ), openChat ? divider() : Container(), openChat ? Container( alignment: Alignment.bottomCenter, // color: Colors.white, padding: EdgeInsets.all(12), child: Row( children: [ Expanded( child: SizedBox( width: double.infinity, child: TextField( controller: controllerForum, maxLength: 256, style: const TextStyle(fontSize: 14, color: Colors.black), keyboardType: TextInputType.multiline, minLines: 1, maxLines: 5, decoration: InputDecoration( counterText: '', hintText: 'writeMessage'.tr()+'..', hintStyle: TextStyle(color: textColor.withValues(alpha: 0.5), fontSize: 14), filled: true, fillColor: backgroundColor, hoverColor: Colors.black.withValues(alpha: 0.1), contentPadding: EdgeInsets.symmetric(vertical: 17, horizontal: 20), suffixIcon: GestureDetector( child: Padding( padding: EdgeInsets.only(left: 13, right: 13), child: U.iconsax('paperclip-2', color: textColor, size: 24), ), onTap: ()async{ var uuid = Uuid().v1().replaceAll('-', ''); var data = { "uniqueId": uuid, "userId": username, "recipientId": "#forum", "senderType": "INFORMANT", "text": controllerForum.text.trim(), "chatId": forumChatId, "images": null }; await getImage(ImageSource.gallery).then((value) { if (_image != null) { navigateTo(context, PhotoChat(data, ImageSource.gallery, false, _image!)).then((value) { if (value != null) { sendMessage(value); } }); } }); }, ), border: InputBorder.none, enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(borderRadius), borderSide: BorderSide(color: textColor)), focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(borderRadius), borderSide: BorderSide(color: primaryColor)), isDense: true ), onChanged: (val) { setState(() { if (val.trim() != '') { final tp = TextPainter(text: TextSpan(text: val), textDirection: ui.TextDirection.ltr); tp.layout(maxWidth: 400); final line = tp.computeLineMetrics().length; if (line == 1) { borderRadius = 50.0; } else { borderRadius = 20.0; } isSend = true; } else { isSend = false; } }); }, ), ), ), SizedBox(width: 12), GestureDetector( child: Container( padding: EdgeInsets.all(12), child: Image.asset('assets/image/icon/Send.png', width: 20, height: 20), decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(50))), ), onTap: (){ if(controllerForum.text.isNotEmpty){ var uuid = Uuid().v1().replaceAll('-', ''); var data = { "uniqueId": uuid, "userId": username, "recipientId": "#forum", "senderType": "INFORMANT", "text": controllerForum.text.trim(), "chatId": forumChatId, "images": null }; sendMessage(data); } }, ) ], ), ) : U.getInternetStatus() ? Center(child: Padding( padding: EdgeInsets.symmetric(vertical: 20), child: bubble_chat(Text('chatClosed'.tr(), textAlign: TextAlign.center, style: TextStyle(fontSize: 12)), null, false, false), )):Container() ], ); } return Scaffold( backgroundColor: backgroundColor, appBar: PreferredSize(preferredSize: Size.fromHeight(0), child: AppBar(elevation: 0, backgroundColor: primaryColor)), body: Column( children: [ Container( padding: EdgeInsets.symmetric(vertical: 25, horizontal: 100), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('message'.tr(), style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis), GestureDetector( child: Text('buttonBack'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)), onTap: (){ deleteCollection(); navigateBack(context); }, ) ], ), ), divider(), FutureBuilder(future: U.isCompatibleWith(VersionKey.multiBahasa), builder: (context, snapshot){ if (snapshot.connectionState != ConnectionState.done) { return CircularProgressIndicator(); // atau loading spinner } if (snapshot.hasError) { print("Error in isCompatibleWith: ${snapshot.error}"); return SizedBox(); // atau widget fallback } if (snapshot.data == true) { return tabForum(); } else { return SizedBox(); } }), Expanded( child: Container( width: double.infinity, height: double.infinity, padding: EdgeInsets.symmetric(vertical: 12, horizontal: 100), child: Row( children: [ Expanded( child: Stack( children: [ Container( width: double.infinity, height: double.infinity, decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))), child: EasyRefresh( header: MaterialHeader(clamping: true, color: primaryColor), onRefresh: () => messageFunc.onRefresh(context), child: !Provider.of(context).firstLoad() ? loadingTemplateNoVoid() : Provider.of(context).activeTab() == 0 ? webChat(context) : forum() ), ), Provider.of(context).user().isNotEmpty && Provider.of(context).user()['canSendMessage'] ? Align( alignment: Alignment.bottomRight, child: GestureDetector( child: Container( margin: EdgeInsets.only(right: 20, bottom: 20), width: 50, height: 50, alignment: Alignment.center, decoration: BoxDecoration( color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(50)) ), child: U.iconsax('message', color: Colors.white), ), onTap: ()=>messageFunc.createMessage(context), ), ) : Container() ], ), ), SizedBox(width: 24), Expanded( child: Container( decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))), child: Provider.of(context).activeTab() == 0 ? chatList() : forumList(), ) ) ], ), ), ) ], ), ); } getImageName(imageUrl) { var imgSplit = imageUrl.toString().split('/'); return imgSplit[imgSplit.length - 1]; } Widget bubble_chat(Widget child, Widget? time, bool isNip, bool isMe) { bool isDate = false; if (time == null) { isDate = true; time = Container(); } Color color = isMe ? primaryColor.withValues(alpha: 0.3) : isDate ? Color(0xffD5F5FF) : Color(0xffECECEC); var clipPath = ClipPath( clipper: isMe ? MyClipper(isNip) : YourClipper(isNip), child: ConstrainedBox( constraints: BoxConstraints(minWidth: 50), child: Container( decoration: BoxDecoration( color: color, ), child: Padding(padding: EdgeInsets.all(8), child: isDate ? child : Stack( children: [ Padding( padding: EdgeInsets.only( bottom: 15, right: isNip && isMe ? 6 : 0, left: isNip && !isMe ? 6 : 0, ), child: child, ), Positioned( bottom: 2, right: 2, child: time, ), // time ], ), ), ), ), ); return Padding( padding: EdgeInsets.only( right: isNip && isMe ? 0 : 6, left: isNip && !isMe ? 0 : 6 ), child: clipPath ); } bool checkDate(date1, date2) { final dateToCheck1 = DateTime(DateTime.parse(date1).year, DateTime.parse(date1).month, DateTime.parse(date1).day); final dateToCheck2 = DateTime(DateTime.parse(date2).year, DateTime.parse(date2).month, DateTime.parse(date2).day); return dateToCheck1 == dateToCheck2 ? true : false; } getImage(ImageSource gallery) {} }