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.

300 lines
15 KiB

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