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.

282 lines
14 KiB

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