message_list.dart 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. import 'package:cloud_firestore/cloud_firestore.dart';
  2. import 'package:easy_localization/easy_localization.dart';
  3. import 'package:easy_refresh/easy_refresh.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter/services.dart';
  6. import 'package:provider/provider.dart';
  7. import 'package:selectable_autolink_text/selectable_autolink_text.dart';
  8. import 'package:telnow_mobile_new/src/api/api_auth_provider.dart';
  9. import 'package:telnow_mobile_new/src/layouts/functions/message.dart';
  10. import 'package:telnow_mobile_new/src/layouts/components/template.dart';
  11. import 'package:telnow_mobile_new/src/utils/U.dart';
  12. import 'package:telnow_mobile_new/src/utils/provider.dart';
  13. import 'package:translator/translator.dart';
  14. import 'package:url_launcher/url_launcher.dart';
  15. class WebMessageListPage extends StatefulWidget {
  16. Map<String, dynamic>? user;
  17. WebMessageListPage(this.user, {super.key});
  18. @override
  19. State<WebMessageListPage> createState() => _WebMessageListPageState();
  20. }
  21. class _WebMessageListPageState extends State<WebMessageListPage> {
  22. final MessageFunction messageFunc = MessageFunction();
  23. final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
  24. final translator = GoogleTranslator();
  25. String? idChat;
  26. String tenant = '';
  27. String opponent = '';
  28. ScrollController scrollController = ScrollController();
  29. List messageData = [];
  30. int page = 0;
  31. bool isAfterLoad = false;
  32. String username = '';
  33. bool isLoad = false;
  34. bool stopLoad = false;
  35. bool scrollBottom = false;
  36. bool isReverse = false;
  37. bool askBroadcast = false;
  38. @override
  39. void initState() {
  40. Provider.of<MessageModule>(context, listen: false).reset();
  41. if(widget.user == null){
  42. messageFunc.getUser(context);
  43. }
  44. else{
  45. Provider.of<MessageModule>(context, listen: false).setUser(widget.user!);
  46. messageFunc.getDataMessages(context);
  47. }
  48. // TODO: implement initState
  49. super.initState();
  50. }
  51. selectMessage(data, isMe) async{
  52. if(idChat != data['chatId']){
  53. setState(() {
  54. idChat = data['chatId'];
  55. username = Provider.of<MessageModule>(context, listen: false).user()['userId'];
  56. askBroadcast = isMe;
  57. opponent = isMe ? data['recipientName'] : data['senderName'];
  58. tenant = data['recipient'];
  59. messageData = [];
  60. page = 0;
  61. isAfterLoad = false;
  62. isLoad = false;
  63. stopLoad = false;
  64. scrollBottom = false;
  65. isReverse = false;
  66. });
  67. scrollController.addListener(() => scrollListener());
  68. if (!isMe) {
  69. await apiAuthProvider.patchData('/api/messages/' + data['id'].toString(), {'readStatus': 'READ'}, context);
  70. }
  71. getMessage();
  72. getCollectionData();
  73. }
  74. }
  75. getMessage() async {
  76. String url = 'myMessages/$idChat';
  77. String filter = '{"f":["uniqueId","LIKE","%-%"]}';
  78. if (askBroadcast) {
  79. url = 'myBroadcast';
  80. filter = '{"f":["1","EQ","1"]}';
  81. }
  82. if (!isLoad && !stopLoad) {
  83. setState(() => isLoad = true);
  84. var mymess = await apiAuthProvider.getData('/api/messages/search/$url', {'isPaged': 'true', 'page': page.toString(), 'size': '20', 'filter': filter, 'tenant': tenant}, context);
  85. if (mymess.containsKey('_embedded') && mounted) {
  86. List data = mymess['_embedded']['myMessages'];
  87. for (int i = 0; i < data.length; i++) {
  88. if (username == data[i]['from']['user']) {
  89. data[i]['selected'] = false;
  90. }
  91. else{
  92. if(U.autoTranslate()){
  93. data[i]['translate'] = '';
  94. }
  95. }
  96. setState(() => messageData.insert(0, data[i]));
  97. }
  98. setState(() {
  99. if (page == 0) {
  100. scrollBottom = true;
  101. }
  102. isLoad = false;
  103. page++;
  104. if (messageData.length >= mymess['page']['totalElements']) {
  105. stopLoad = true;
  106. }
  107. });
  108. if(U.autoTranslate()){
  109. translateMessage();
  110. }
  111. } else {
  112. setState(() {
  113. isLoad = false;
  114. stopLoad = true;
  115. });
  116. }
  117. }
  118. }
  119. getCollectionData() {
  120. FirebaseFirestore.instance.collection("tmMessages").doc('messages').collection(idChat!).snapshots().listen((querySnapshot) {
  121. setState(() {
  122. querySnapshot.docChanges.forEach((result) async {
  123. var data = result.doc.data();
  124. if (result.type == DocumentChangeType.added && isAfterLoad) {
  125. if ((username == data!['from']['user'] && messageData.where((element) => element['uniqueId'] == data['uniqueId']).length > 0) || (username != data['from']['user'] && data['readStatus'] == 'DELETED')) {
  126. int index = messageData.indexWhere((element) => element['uniqueId'] == data['uniqueId']);
  127. messageData[index]['read'] = data['read'];
  128. messageData[index]['readStatus'] = data['readStatus'];
  129. messageData[index]['imageUrl'] = data['imageUrl'];
  130. } else {
  131. if(U.autoTranslate()){
  132. var locale = context.locale.toString() == 'zh' ? 'zh-cn' : context.locale.toString();
  133. var translate = await translator.translate(data['msg']??'', to: locale);
  134. data['translate'] = translate.text;
  135. }
  136. messageData.add(data);
  137. }
  138. } else if (result.type == DocumentChangeType.modified && isAfterLoad) {
  139. // print("modified");
  140. if (messageData.where((element) => element['uniqueId'] == data!['uniqueId']).length > 0) {
  141. int index = messageData.indexWhere((element) => element['uniqueId'] == data!['uniqueId']);
  142. messageData[index]['read'] = data!['read'];
  143. messageData[index]['readStatus'] = data['readStatus'];
  144. }
  145. } else if (result.type == DocumentChangeType.removed && isAfterLoad) {
  146. // print("removed");
  147. }
  148. });
  149. isAfterLoad = true;
  150. });
  151. });
  152. }
  153. translateMessage(){
  154. var locale = context.locale.toString() == 'zh' ? 'zh-cn' : context.locale.toString();
  155. messageData.forEach((element) async{
  156. if (username != element['from']['user'] && element['translate'] == '') {
  157. var translate = await translator.translate(element['msg']??'', to: locale);
  158. setState(() {
  159. element['translate'] = translate.text;
  160. });
  161. }
  162. });
  163. }
  164. deleteCollection() {
  165. try{
  166. FirebaseFirestore.instance.collection("tmMessages").doc('messages').collection(idChat!).get().then((value) {
  167. for (DocumentSnapshot ds in value.docs) {
  168. ds.reference.delete();
  169. }
  170. });
  171. } catch(e){}
  172. }
  173. scrollToBottom() {
  174. scrollController.animateTo(scrollController.position.minScrollExtent, duration: Duration(milliseconds: 1), curve: Curves.decelerate);
  175. scrollBottom = false;
  176. }
  177. scrollListener() {
  178. if (scrollController.offset >= scrollController.position.maxScrollExtent) {
  179. // print('loadMessage');
  180. getMessage();
  181. }
  182. }
  183. @override
  184. Widget build(BuildContext context) {
  185. if (messageData.length > 0) {
  186. if (scrollController.position.maxScrollExtent > 0 && !isReverse) {
  187. setState(() => isReverse = true);
  188. }
  189. if (isAfterLoad && scrollBottom && isReverse) {
  190. scrollToBottom();
  191. }
  192. }
  193. return Scaffold(
  194. backgroundColor: backgroundColor,
  195. appBar: PreferredSize(preferredSize: Size.fromHeight(0), child: AppBar(elevation: 0, backgroundColor: primaryColor)),
  196. body: Column(
  197. children: [
  198. Container(
  199. padding: EdgeInsets.symmetric(vertical: 25, horizontal: 100),
  200. child: Row(
  201. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  202. children: [
  203. Text('message'.tr(), style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
  204. GestureDetector(
  205. child: Text('buttonBack'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)),
  206. onTap: (){
  207. deleteCollection();
  208. navigateBack(context);
  209. },
  210. )
  211. ],
  212. ),
  213. ),
  214. divider(),
  215. Expanded(
  216. child: Container(
  217. width: double.infinity, height: double.infinity,
  218. padding: EdgeInsets.symmetric(vertical: 25, horizontal: 100),
  219. child: Row(
  220. children: [
  221. Expanded(
  222. child: Stack(
  223. children: [
  224. Container(
  225. width: double.infinity, height: double.infinity,
  226. child: EasyRefresh(
  227. header: MaterialHeader(clamping: true, color: primaryColor),
  228. onRefresh: () => messageFunc.onRefresh(context),
  229. child: Provider.of<MessageModule>(context).data().isEmpty && !Provider.of<MessageModule>(context).firstLoad()? loadingTemplate(() {},) : Provider.of<MessageModule>(context).data().isEmpty ? Center(
  230. child: Text('noMessageText').tr(),
  231. ) : SingleChildScrollView(
  232. padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
  233. child: Column(
  234. mainAxisSize: MainAxisSize.max,
  235. children: List.generate(Provider.of<MessageModule>(context).data().length, (i) {
  236. bool _isMe = Provider.of<MessageModule>(context, listen: false).data()[i]['userId'] == Provider.of<MessageModule>(context, listen: false).user()['userId'] ? true : false;
  237. return GestureDetector(
  238. child: Container(
  239. padding: EdgeInsets.all(10),
  240. child: Row(
  241. children: [
  242. Provider.of<MessageModule>(context).data()[i]['senderAvatar'] != null && Provider.of<MessageModule>(context).data()[i]['senderAvatar'] != '' ? CircleAvatar(
  243. backgroundImage: NetworkImage(Provider.of<MessageModule>(context).data()[i]['senderAvatar']), radius: 24,
  244. ) : Container(
  245. height: 48, width: 48,
  246. child: Center(child: Text(_isMe ? Provider.of<MessageModule>(context).data()[i]['recipientName'] == "all_informants" ? "allInformants".tr()[0] : Provider.of<MessageModule>(context).data()[i]['recipientName'][0] : Provider.of<MessageModule>(context).data()[i]['senderName'][0], style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18))),
  247. decoration: BoxDecoration(shape: BoxShape.circle, color: Color(U.getColor(_isMe ? Provider.of<MessageModule>(context).data()[i]['recipient'] : Provider.of<MessageModule>(context).data()[i]['recipientId']))),
  248. ),
  249. SizedBox(width: 20),
  250. Expanded(
  251. child: Column(
  252. crossAxisAlignment: CrossAxisAlignment.start,
  253. children: [
  254. Text(_isMe ? Provider.of<MessageModule>(context).data()[i]['recipientName'] == "all_informants" ? "allInformants".tr() : Provider.of<MessageModule>(context).data()[i]['recipientName'] : Provider.of<MessageModule>(context).data()[i]['senderName']),
  255. SizedBox(height: 5),
  256. Row(
  257. children: [
  258. _isMe ? Padding(
  259. padding: EdgeInsets.only(right: 5),
  260. child: Icon(Icons.done_all, size: 18, color: Colors.blueGrey),
  261. ) : Container(),
  262. Provider.of<MessageModule>(context).data()[i]['isImage'] ? Align(
  263. alignment: Alignment.centerLeft,
  264. child: Row(
  265. children: [
  266. Icon(Provider.of<MessageModule>(context).data()[i]['fileType'] == 'pdf' ? Icons.picture_as_pdf : Icons.image, color: Colors.black45, size: 16),
  267. SizedBox(width: 6),
  268. Provider.of<MessageModule>(context).data()[i]['lastText'] == '' ? Provider.of<MessageModule>(context).data()[i]['fileType'] == 'pdf' ? Text('pdfFile'.tr()) : Text('photo'.tr()) : Container()
  269. ],
  270. ),
  271. ) : Container(),
  272. Expanded(
  273. child: Text(Provider.of<MessageModule>(context).data()[i]['lastText'], style: TextStyle(fontSize: 13), maxLines: 1, overflow: TextOverflow.ellipsis),
  274. )
  275. ],
  276. )
  277. ],
  278. ),
  279. ),
  280. SizedBox(width: 20),
  281. Column(
  282. mainAxisAlignment: MainAxisAlignment.center,
  283. crossAxisAlignment: CrossAxisAlignment.end,
  284. children: [
  285. Text(messageFunc.timeSet(Provider.of<MessageModule>(context).data()[i]['lastDateTimeSend']), style: TextStyle(fontSize: 11, color: Provider.of<MessageModule>(context).data()[i]['lastReadStatus'] == 'UNREAD' && !_isMe ? Colors.green : Colors.black45)),
  286. SizedBox(height: 4),
  287. Icon(Icons.circle, color: Colors.green.withValues(alpha: Provider.of<MessageModule>(context).data()[i]['lastReadStatus'] == 'UNREAD' && !_isMe ? 1 : 0))
  288. ],
  289. )
  290. ],
  291. ),
  292. decoration: BoxDecoration(color: idChat != null && idChat == Provider.of<MessageModule>(context, listen: false).data()[i]['chatId'] ? Color(0xff26DA17).withValues(alpha: 0.2) : Colors.white, borderRadius: BorderRadius.all(Radius.circular(12))),
  293. ),
  294. onTap: ()=>selectMessage(Provider.of<MessageModule>(context, listen: false).data()[i], _isMe),
  295. );
  296. }),
  297. ),
  298. ),
  299. ),
  300. decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
  301. ),
  302. Provider.of<MessageModule>(context).user().isNotEmpty && Provider.of<MessageModule>(context).user()['canSendMessage'] ? Align(
  303. alignment: Alignment.bottomRight,
  304. child: GestureDetector(
  305. child: Container(
  306. margin: EdgeInsets.only(right: 20, bottom: 20),
  307. width: 50, height: 50, alignment: Alignment.center,
  308. child: U.iconsax('message', color: Colors.white),
  309. decoration: BoxDecoration(
  310. color: primaryColor,
  311. borderRadius: BorderRadius.all(Radius.circular(50))
  312. ),
  313. ),
  314. onTap: ()=>messageFunc.createMessage(context),
  315. ),
  316. ) : Container()
  317. ],
  318. ),
  319. ),
  320. SizedBox(width: 30),
  321. Expanded(
  322. child: Container(
  323. child: idChat != null ? Column(
  324. children: [
  325. Container(
  326. width: double.infinity, padding: EdgeInsets.all(20),
  327. child: Text(opponent != 'all_informants' ? opponent : "allInformants".tr(), style: TextStyle(color: textColor, fontSize: 16)),
  328. ),
  329. divider(),
  330. Expanded(
  331. child: messageData.isEmpty && !isAfterLoad ? loadingTemplate(() {},) : SingleChildScrollView(
  332. padding: const EdgeInsets.fromLTRB(10, 10, 10, 7),
  333. controller: scrollController,
  334. reverse: isReverse,
  335. child: Column(
  336. children: List.generate(messageData.length, (i) {
  337. bool hideDate = i == 0 ? false : checkDate(messageData[i]['datetime'], messageData[i - 1]['datetime']);
  338. bool isNip = i == 0 ? true : !hideDate ? true : messageData[i]['from']['user'] == messageData[i - 1]['from']['user'] ? false : true;
  339. return Column(
  340. children: [
  341. !hideDate ? Center(
  342. child: Padding(
  343. padding: const EdgeInsets.all(10),
  344. child: bubble_chat(Text(convertDate(messageData[i]['datetime'], context.locale.toString()), textAlign: TextAlign.center, style: TextStyle(fontSize: 12)), null, false, false),
  345. ),
  346. ) : Container(),
  347. Builder(builder: (context) {
  348. var isMe = messageData[i]['from']['user'] == Provider.of<MessageModule>(context, listen: false).user()['userId'];
  349. var isFile = messageData[i]['imageUrl'] != null && messageData[i]['imageUrl'] != '' && messageData[i]['readStatus'] != 'DELETED';
  350. var isPdf = isFile && messageData[i]['imageUrl'].split('.').last == 'pdf';
  351. var isImg = isFile && !isPdf;
  352. var isTranslate = U.autoTranslate() && !isMe && messageData[i]['msg'] != messageData[i]['translate'];
  353. var wdg = messageData[i]['readStatus'] == 'DELETED' ? Text('deletedMessage'.tr(), style: TextStyle(fontSize: 14, color: Colors.black54, fontStyle: FontStyle.italic)) : Column(
  354. crossAxisAlignment: CrossAxisAlignment.start,
  355. children: [
  356. isPdf ? GestureDetector(
  357. child: Container(
  358. child: Builder(builder: (context) {
  359. var pdfContainer = Container(
  360. padding: EdgeInsets.all(6),
  361. child: Row(
  362. mainAxisSize: MainAxisSize.min,
  363. children: [
  364. Icon(Icons.picture_as_pdf),
  365. SizedBox(width: 6),
  366. Flexible(child: Text(getImageName(messageData[i]['imageUrl'])))
  367. ],
  368. ),
  369. decoration: BoxDecoration(color: Colors.black12.withValues(alpha: 0.1), borderRadius: BorderRadius.all(Radius.circular(4))),
  370. );
  371. return pdfContainer;
  372. }),
  373. ),
  374. onTap: () async {
  375. await launchUrl( messageData[i]['imageUrl']);
  376. },
  377. ) : isImg ? GestureDetector(
  378. child: Container(
  379. margin: const EdgeInsets.only(bottom: 5),
  380. child: Builder(builder: (context) {
  381. return Image.network(messageData[i]['imageUrl'], fit: BoxFit.cover, width: 200, height: 200);
  382. })
  383. ),
  384. onTap: () => navigateTo(context, PhotoPreview(opponent, messageData[i]['imageUrl'], true)),
  385. ) : SizedBox(height: 1),
  386. messageData[i]['msg'] != null && messageData[i]['msg'] != '' ? Column(
  387. crossAxisAlignment: !isMe?CrossAxisAlignment.start:CrossAxisAlignment.end,
  388. children: [
  389. SizedBox(height: 4),
  390. SelectableAutoLinkText(
  391. messageData[i]['msg'],
  392. style: TextStyle(fontSize: 14, color: Colors.black),
  393. linkStyle: TextStyle(color: Colors.blueAccent),
  394. highlightedLinkStyle: TextStyle(color: Colors.blueAccent, backgroundColor: Colors.blueAccent.withAlpha(0x33)),
  395. onTap: (link) async{
  396. if (await canLaunchUrl(Uri.parse(link))) {
  397. await launchUrl(Uri.parse(link));
  398. }
  399. },
  400. onLongPress: (link) {
  401. Clipboard.setData(new ClipboardData(text: link)).then((value){
  402. showSuccess('link_copied'.tr(), context);
  403. });
  404. },
  405. enableInteractiveSelection: false,
  406. ),
  407. isTranslate ? Container(
  408. margin: EdgeInsets.only(top: 2), decoration: BoxDecoration(border: Border(top: BorderSide(color: Colors.black.withValues(alpha: 0.2)))),
  409. child: Text(messageData[i]['translate']!=''?'(${messageData[i]['translate']})':'...', style: TextStyle(fontSize: 14, color: Colors.black.withValues(alpha: 0.65), fontStyle: FontStyle.italic)),
  410. ) : Container(),
  411. ],
  412. ) : Container()
  413. ],
  414. );
  415. var timeBubble = Positioned(
  416. bottom: 0, right: isNip ? 6 : 0,
  417. child: Text(DateFormat('HH:mm').format(DateTime.parse(messageData[i]['datetime'])), style: TextStyle(fontSize: 10, color: Colors.black45)),
  418. );
  419. var expander = Expanded(
  420. flex: 8,
  421. child: Column(
  422. children: [
  423. bubble_chat(wdg, timeBubble, isNip, isMe),
  424. SizedBox(height: 5),
  425. ],
  426. crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
  427. )
  428. );
  429. return isMe ? Row(
  430. children: [
  431. Expanded(flex: 2, child: Container()),
  432. expander,
  433. ],
  434. ) : Row(
  435. children: [
  436. expander,
  437. Expanded(flex: 2, child: Container()),
  438. ],
  439. );
  440. }),
  441. ],
  442. );
  443. }),
  444. ),
  445. ),
  446. )
  447. ],
  448. ) : Container(),
  449. decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
  450. ),
  451. )
  452. ],
  453. ),
  454. ),
  455. )
  456. ],
  457. ),
  458. );
  459. }
  460. getImageName(imageUrl) {
  461. var imgSplit = imageUrl.toString().split('/');
  462. return imgSplit[imgSplit.length - 1];
  463. }
  464. Widget bubble_chat(Widget child, Widget? time, bool isNip, bool isMe) {
  465. bool isDate = false;
  466. if (time == null) {
  467. isDate = true;
  468. time = Container();
  469. }
  470. Color color = isMe ? primaryColor.withValues(alpha: 0.3) : isDate ? Color(0xffD5F5FF) : Color(0xffECECEC);
  471. var clipPath = ClipPath(
  472. child: ConstrainedBox(
  473. constraints: BoxConstraints(minWidth: 50),
  474. child: Container(
  475. decoration: BoxDecoration(
  476. color: color,
  477. ),
  478. child: Padding(padding: EdgeInsets.all(8),
  479. child: isDate ? child : Stack(
  480. children: [
  481. Padding(
  482. padding: EdgeInsets.only(
  483. bottom: 15,
  484. right: isNip && isMe ? 6 : 0,
  485. left: isNip && !isMe ? 6 : 0,
  486. ),
  487. child: child,
  488. ),
  489. time
  490. ],
  491. ),
  492. ),
  493. ),
  494. ),
  495. clipper: isMe ? MyClipper(isNip) : YourClipper(isNip),
  496. );
  497. return Padding(
  498. padding: EdgeInsets.only(
  499. right: isNip && isMe ? 0 : 6,
  500. left: isNip && !isMe ? 0 : 6
  501. ),
  502. child: clipPath
  503. );
  504. }
  505. bool checkDate(date1, date2) {
  506. final dateToCheck1 = DateTime(DateTime.parse(date1).year, DateTime.parse(date1).month, DateTime.parse(date1).day);
  507. final dateToCheck2 = DateTime(DateTime.parse(date2).year, DateTime.parse(date2).month, DateTime.parse(date2).day);
  508. return dateToCheck1 == dateToCheck2 ? true : false;
  509. }
  510. String convertDate(date, locale) {
  511. final dateToCheck = DateTime.parse(date);
  512. final now = DateTime.now();
  513. final today = DateTime(now.year, now.month, now.day);
  514. final yesterday = DateTime(now.year, now.month, now.day - 1);
  515. final aDate = DateTime(dateToCheck.year, dateToCheck.month, dateToCheck.day);
  516. if (aDate == today) {
  517. return 'today'.tr();
  518. } else if (aDate == yesterday) {
  519. return 'yesterday'.tr();
  520. } else {
  521. return DateFormat('dd MMMM yyyy', locale).format(DateTime.parse(date));
  522. }
  523. }
  524. }