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.

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