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.

270 lines
8.8 KiB

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