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.

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