Encrypted messaging app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

299 lines
15 KiB

  1. import 'package:Envelope/models/my_profile.dart';
  2. import 'package:Envelope/views/main/conversation_settings.dart';
  3. import 'package:flutter/material.dart';
  4. import '/models/conversations.dart';
  5. import '/models/messages.dart';
  6. import '/utils/storage/messages.dart';
  7. String convertToAgo(String input){
  8. DateTime time = DateTime.parse(input);
  9. Duration diff = DateTime.now().difference(time);
  10. if(diff.inDays >= 1){
  11. return '${diff.inDays} day${diff.inDays == 1 ? "" : "s"} ago';
  12. }
  13. if(diff.inHours >= 1){
  14. return '${diff.inHours} hour${diff.inHours == 1 ? "" : "s"} ago';
  15. }
  16. if(diff.inMinutes >= 1){
  17. return '${diff.inMinutes} minute${diff.inMinutes == 1 ? "" : "s"} ago';
  18. }
  19. if (diff.inSeconds >= 1){
  20. return '${diff.inSeconds} second${diff.inSeconds == 1 ? "" : "s"} ago';
  21. }
  22. return 'just now';
  23. }
  24. class ConversationDetail extends StatefulWidget{
  25. final Conversation conversation;
  26. const ConversationDetail({
  27. Key? key,
  28. required this.conversation,
  29. }) : super(key: key);
  30. @override
  31. _ConversationDetailState createState() => _ConversationDetailState();
  32. }
  33. class _ConversationDetailState extends State<ConversationDetail> {
  34. List<Message> messages = [];
  35. MyProfile profile = MyProfile(id: '', username: '');
  36. TextEditingController msgController = TextEditingController();
  37. @override
  38. void initState() {
  39. super.initState();
  40. fetchMessages();
  41. }
  42. Future<void> fetchMessages() async {
  43. profile = await MyProfile.getProfile();
  44. messages = await getMessagesForThread(widget.conversation);
  45. setState(() {});
  46. }
  47. Widget usernameOrFailedToSend(int index) {
  48. if (messages[index].senderUsername != profile.username) {
  49. return Text(
  50. messages[index].senderUsername,
  51. style: TextStyle(
  52. fontSize: 12,
  53. color: Colors.grey[300],
  54. ),
  55. );
  56. }
  57. if (messages[index].failedToSend) {
  58. return Row(
  59. mainAxisAlignment: MainAxisAlignment.end,
  60. children: const <Widget>[
  61. Icon(
  62. Icons.warning_rounded,
  63. color: Colors.red,
  64. size: 20,
  65. ),
  66. Text(
  67. 'Failed to send',
  68. style: TextStyle(color: Colors.red, fontSize: 12),
  69. textAlign: TextAlign.right,
  70. ),
  71. ],
  72. );
  73. }
  74. return const SizedBox.shrink();
  75. }
  76. @override
  77. Widget build(BuildContext context) {
  78. return Scaffold(
  79. appBar: AppBar(
  80. elevation: 0,
  81. automaticallyImplyLeading: false,
  82. flexibleSpace: SafeArea(
  83. child: Container(
  84. padding: const EdgeInsets.only(right: 16),
  85. child: Row(
  86. children: <Widget>[
  87. IconButton(
  88. onPressed: (){
  89. Navigator.pop(context);
  90. },
  91. icon: Icon(
  92. Icons.arrow_back,
  93. color: Theme.of(context).appBarTheme.iconTheme?.color,
  94. ),
  95. ),
  96. const SizedBox(width: 2,),
  97. Expanded(
  98. child: Column(
  99. crossAxisAlignment: CrossAxisAlignment.start,
  100. mainAxisAlignment: MainAxisAlignment.center,
  101. children: <Widget>[
  102. Text(
  103. widget.conversation.name,
  104. style: TextStyle(
  105. fontSize: 16,
  106. fontWeight: FontWeight.w600,
  107. color: Theme.of(context).appBarTheme.toolbarTextStyle?.color
  108. ),
  109. ),
  110. ],
  111. ),
  112. ),
  113. IconButton(
  114. onPressed: (){
  115. Navigator.of(context).push(
  116. MaterialPageRoute(builder: (context) => ConversationSettings(conversation: widget.conversation)),
  117. );
  118. },
  119. icon: Icon(
  120. Icons.settings,
  121. color: Theme.of(context).appBarTheme.iconTheme?.color,
  122. ),
  123. ),
  124. ],
  125. ),
  126. ),
  127. ),
  128. ),
  129. body: Stack(
  130. children: <Widget>[
  131. ListView.builder(
  132. itemCount: messages.length,
  133. shrinkWrap: true,
  134. padding: const EdgeInsets.only(top: 10,bottom: 90),
  135. reverse: true,
  136. itemBuilder: (context, index) {
  137. return Container(
  138. padding: const EdgeInsets.only(left: 14,right: 14,top: 0,bottom: 0),
  139. child: Align(
  140. alignment: (
  141. messages[index].senderUsername == profile.username ?
  142. Alignment.topRight :
  143. Alignment.topLeft
  144. ),
  145. child: Column(
  146. crossAxisAlignment: messages[index].senderUsername == profile.username ?
  147. CrossAxisAlignment.end :
  148. CrossAxisAlignment.start,
  149. children: <Widget>[
  150. Container(
  151. decoration: BoxDecoration(
  152. borderRadius: BorderRadius.circular(20),
  153. color: (
  154. messages[index].senderUsername == profile.username ?
  155. Theme.of(context).colorScheme.primary :
  156. Theme.of(context).colorScheme.tertiary
  157. ),
  158. ),
  159. padding: const EdgeInsets.all(12),
  160. child: Text(
  161. messages[index].data,
  162. style: TextStyle(
  163. fontSize: 15,
  164. color: messages[index].senderUsername == profile.username ?
  165. Theme.of(context).colorScheme.onPrimary :
  166. Theme.of(context).colorScheme.onTertiary,
  167. )
  168. ),
  169. ),
  170. const SizedBox(height: 1.5),
  171. Row(
  172. mainAxisAlignment: messages[index].senderUsername == profile.username ?
  173. MainAxisAlignment.end :
  174. MainAxisAlignment.start,
  175. children: <Widget>[
  176. const SizedBox(width: 10),
  177. usernameOrFailedToSend(index),
  178. ],
  179. ),
  180. const SizedBox(height: 1.5),
  181. Row(
  182. mainAxisAlignment: messages[index].senderUsername == profile.username ?
  183. MainAxisAlignment.end :
  184. MainAxisAlignment.start,
  185. children: <Widget>[
  186. const SizedBox(width: 10),
  187. Text(
  188. convertToAgo(messages[index].createdAt),
  189. textAlign: messages[index].senderUsername == profile.username ?
  190. TextAlign.left :
  191. TextAlign.right,
  192. style: TextStyle(
  193. fontSize: 12,
  194. color: Colors.grey[500],
  195. ),
  196. ),
  197. ],
  198. ),
  199. index != 0 ?
  200. const SizedBox(height: 20) :
  201. const SizedBox.shrink(),
  202. ],
  203. )
  204. ),
  205. );
  206. },
  207. ),
  208. Align(
  209. alignment: Alignment.bottomLeft,
  210. child: ConstrainedBox(
  211. constraints: const BoxConstraints(
  212. maxHeight: 200.0,
  213. ),
  214. child: Container(
  215. padding: const EdgeInsets.only(left: 10,bottom: 10,top: 10),
  216. // height: 60,
  217. width: double.infinity,
  218. color: Theme.of(context).backgroundColor,
  219. child: Row(
  220. children: <Widget>[
  221. GestureDetector(
  222. onTap: (){
  223. },
  224. child: Container(
  225. height: 30,
  226. width: 30,
  227. decoration: BoxDecoration(
  228. color: Theme.of(context).primaryColor,
  229. borderRadius: BorderRadius.circular(30),
  230. ),
  231. child: Icon(
  232. Icons.add,
  233. color: Theme.of(context).colorScheme.onPrimary,
  234. size: 20
  235. ),
  236. ),
  237. ),
  238. const SizedBox(width: 15,),
  239. Expanded(
  240. child: TextField(
  241. decoration: InputDecoration(
  242. hintText: "Write message...",
  243. hintStyle: TextStyle(
  244. color: Theme.of(context).hintColor,
  245. ),
  246. border: InputBorder.none,
  247. ),
  248. maxLines: null,
  249. controller: msgController,
  250. ),
  251. ),
  252. const SizedBox(width: 15),
  253. Container(
  254. width: 45,
  255. height: 45,
  256. child: FittedBox(
  257. child: FloatingActionButton(
  258. onPressed: () async {
  259. if (msgController.text == '') {
  260. return;
  261. }
  262. await sendMessage(widget.conversation, msgController.text);
  263. messages = await getMessagesForThread(widget.conversation);
  264. setState(() {});
  265. msgController.text = '';
  266. },
  267. child: Icon(
  268. Icons.send,
  269. color: Theme.of(context).colorScheme.onPrimary,
  270. size: 22
  271. ),
  272. backgroundColor: Theme.of(context).primaryColor,
  273. ),
  274. ),
  275. ),
  276. const SizedBox(width: 10),
  277. ],
  278. ),
  279. ),
  280. ),
  281. ),
  282. ],
  283. ),
  284. );
  285. }
  286. }