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.

327 lines
9.6 KiB

  1. import 'package:Envelope/models/friends.dart';
  2. import 'package:Envelope/utils/encryption/crypto_utils.dart';
  3. import 'package:Envelope/views/main/conversation/create_add_users.dart';
  4. import 'package:flutter/material.dart';
  5. import '/models/conversation_users.dart';
  6. import '/models/conversations.dart';
  7. import '/models/my_profile.dart';
  8. import '/views/main/conversation/settings_user_list_item.dart';
  9. import '/views/main/conversation/edit_details.dart';
  10. import '/components/custom_circle_avatar.dart';
  11. import '/utils/storage/database.dart';
  12. import '/utils/storage/conversations.dart';
  13. class ConversationSettings extends StatefulWidget {
  14. final Conversation conversation;
  15. const ConversationSettings({
  16. Key? key,
  17. required this.conversation,
  18. }) : super(key: key);
  19. @override
  20. State<ConversationSettings> createState() => _ConversationSettingsState();
  21. }
  22. class _ConversationSettingsState extends State<ConversationSettings> {
  23. List<ConversationUser> users = [];
  24. MyProfile? profile;
  25. TextEditingController nameController = TextEditingController();
  26. @override
  27. Widget build(BuildContext context) {
  28. return Scaffold(
  29. appBar: AppBar(
  30. elevation: 0,
  31. automaticallyImplyLeading: false,
  32. flexibleSpace: SafeArea(
  33. child: Container(
  34. padding: const EdgeInsets.only(right: 16),
  35. child: Row(
  36. children: <Widget>[
  37. IconButton(
  38. onPressed: (){
  39. Navigator.pop(context);
  40. },
  41. icon: const Icon(Icons.arrow_back),
  42. ),
  43. const SizedBox(width: 2,),
  44. Expanded(
  45. child: Column(
  46. crossAxisAlignment: CrossAxisAlignment.start,
  47. mainAxisAlignment: MainAxisAlignment.center,
  48. children: <Widget>[
  49. Text(
  50. widget.conversation.name + " Settings",
  51. style: const TextStyle(
  52. fontSize: 16,
  53. fontWeight: FontWeight.w600
  54. ),
  55. ),
  56. ],
  57. ),
  58. ),
  59. ],
  60. ),
  61. ),
  62. ),
  63. ),
  64. body: Padding(
  65. padding: const EdgeInsets.all(15),
  66. child: SingleChildScrollView(
  67. child: Column(
  68. children: <Widget> [
  69. const SizedBox(height: 30),
  70. conversationName(),
  71. const SizedBox(height: 25),
  72. widget.conversation.admin ?
  73. sectionTitle('Settings') :
  74. const SizedBox.shrink(),
  75. widget.conversation.admin ?
  76. settings() :
  77. const SizedBox.shrink(),
  78. widget.conversation.admin ?
  79. const SizedBox(height: 25) :
  80. const SizedBox.shrink(),
  81. sectionTitle('Members', showUsersAdd: true),
  82. usersList(),
  83. const SizedBox(height: 25),
  84. myAccess(),
  85. ],
  86. ),
  87. ),
  88. ),
  89. );
  90. }
  91. Widget conversationName() {
  92. return Row(
  93. children: <Widget> [
  94. const CustomCircleAvatar(
  95. icon: Icon(Icons.people, size: 40),
  96. imagePath: null, // TODO: Add image here
  97. radius: 30,
  98. ),
  99. const SizedBox(width: 10),
  100. Text(
  101. widget.conversation.name,
  102. style: const TextStyle(
  103. fontSize: 25,
  104. fontWeight: FontWeight.w500,
  105. ),
  106. ),
  107. widget.conversation.admin ? IconButton(
  108. iconSize: 20,
  109. icon: const Icon(Icons.edit),
  110. padding: const EdgeInsets.all(5.0),
  111. splashRadius: 25,
  112. onPressed: () {
  113. Navigator.of(context).push(
  114. MaterialPageRoute(builder: (context) => ConversationEditDetails(
  115. saveCallback: (String conversationName) async {
  116. widget.conversation.name = conversationName;
  117. final db = await getDatabaseConnection();
  118. db.update(
  119. 'conversations',
  120. widget.conversation.toMap(),
  121. where: 'id = ?',
  122. whereArgs: [widget.conversation.id],
  123. );
  124. await updateConversation(widget.conversation, includeUsers: true);
  125. setState(() {});
  126. Navigator.pop(context);
  127. },
  128. conversation: widget.conversation,
  129. )),
  130. ).then(onGoBack);
  131. },
  132. ) : const SizedBox.shrink(),
  133. ],
  134. );
  135. }
  136. Future<void> getUsers() async {
  137. users = await getConversationUsers(widget.conversation);
  138. profile = await MyProfile.getProfile();
  139. setState(() {});
  140. }
  141. @override
  142. void initState() {
  143. nameController.text = widget.conversation.name;
  144. super.initState();
  145. getUsers();
  146. }
  147. Widget myAccess() {
  148. return Align(
  149. alignment: Alignment.centerLeft,
  150. child: Column(
  151. crossAxisAlignment: CrossAxisAlignment.stretch,
  152. children: [
  153. TextButton.icon(
  154. label: const Text(
  155. 'Leave Conversation',
  156. style: TextStyle(fontSize: 16)
  157. ),
  158. icon: const Icon(Icons.exit_to_app),
  159. style: const ButtonStyle(
  160. alignment: Alignment.centerLeft,
  161. ),
  162. onPressed: () {
  163. print('Leave Group');
  164. }
  165. ),
  166. ],
  167. ),
  168. );
  169. }
  170. Widget sectionTitle(String title, { bool showUsersAdd = false}) {
  171. return Align(
  172. alignment: Alignment.centerLeft,
  173. child: Padding(
  174. padding: const EdgeInsets.only(right: 6),
  175. child: Row(
  176. children: [
  177. Expanded(
  178. child: Container(
  179. padding: const EdgeInsets.only(left: 12),
  180. child: Text(
  181. title,
  182. style: const TextStyle(fontSize: 20),
  183. ),
  184. ),
  185. ),
  186. !showUsersAdd ?
  187. const SizedBox.shrink() :
  188. IconButton(
  189. icon: const Icon(Icons.add),
  190. padding: const EdgeInsets.all(0),
  191. onPressed: () async {
  192. List<Friend> friends = await unselectedFriends();
  193. Navigator.of(context).push(
  194. MaterialPageRoute(builder: (context) => ConversationAddFriendsList(
  195. friends: friends,
  196. saveCallback: (List<Friend> selectedFriends) async {
  197. addUsersToConversation(
  198. widget.conversation,
  199. selectedFriends,
  200. );
  201. await updateConversation(widget.conversation, includeUsers: true);
  202. await getUsers();
  203. Navigator.pop(context);
  204. },
  205. ))
  206. );
  207. },
  208. ),
  209. ],
  210. )
  211. )
  212. );
  213. }
  214. Widget settings() {
  215. return Align(
  216. alignment: Alignment.centerLeft,
  217. child: Column(
  218. crossAxisAlignment: CrossAxisAlignment.stretch,
  219. children: [
  220. const SizedBox(height: 5),
  221. TextButton.icon(
  222. label: const Text(
  223. 'Disappearing Messages',
  224. style: TextStyle(fontSize: 16)
  225. ),
  226. icon: const Icon(Icons.timer),
  227. style: ButtonStyle(
  228. alignment: Alignment.centerLeft,
  229. foregroundColor: MaterialStateProperty.resolveWith<Color>(
  230. (Set<MaterialState> states) {
  231. return Theme.of(context).colorScheme.onBackground;
  232. },
  233. )
  234. ),
  235. onPressed: () {
  236. print('Disappearing Messages');
  237. }
  238. ),
  239. TextButton.icon(
  240. label: const Text(
  241. 'Permissions',
  242. style: TextStyle(fontSize: 16)
  243. ),
  244. icon: const Icon(Icons.lock),
  245. style: ButtonStyle(
  246. alignment: Alignment.centerLeft,
  247. foregroundColor: MaterialStateProperty.resolveWith<Color>(
  248. (Set<MaterialState> states) {
  249. return Theme.of(context).colorScheme.onBackground;
  250. },
  251. )
  252. ),
  253. onPressed: () {
  254. print('Permissions');
  255. }
  256. ),
  257. ],
  258. ),
  259. );
  260. }
  261. Widget usersList() {
  262. return ListView.builder(
  263. itemCount: users.length,
  264. shrinkWrap: true,
  265. padding: const EdgeInsets.only(top: 5, bottom: 0),
  266. physics: const NeverScrollableScrollPhysics(),
  267. itemBuilder: (context, i) {
  268. return ConversationSettingsUserListItem(
  269. user: users[i],
  270. isAdmin: widget.conversation.admin,
  271. profile: profile!, // TODO: Fix this
  272. );
  273. }
  274. );
  275. }
  276. Future<List<Friend>> unselectedFriends() async {
  277. final db = await getDatabaseConnection();
  278. List<String> notInArgs = [];
  279. for (var user in users) {
  280. notInArgs.add(user.userId);
  281. }
  282. final List<Map<String, dynamic>> maps = await db.query(
  283. 'friends',
  284. where: 'friend_id not in (${List.filled(notInArgs.length, '?').join(',')})',
  285. whereArgs: notInArgs,
  286. orderBy: 'username',
  287. );
  288. return List.generate(maps.length, (i) {
  289. return Friend(
  290. id: maps[i]['id'],
  291. userId: maps[i]['user_id'],
  292. friendId: maps[i]['friend_id'],
  293. friendSymmetricKey: maps[i]['symmetric_key'],
  294. publicKey: CryptoUtils.rsaPublicKeyFromPem(maps[i]['asymmetric_public_key']),
  295. acceptedAt: maps[i]['accepted_at'],
  296. username: maps[i]['username'],
  297. );
  298. });
  299. }
  300. onGoBack(dynamic value) async {
  301. nameController.text = widget.conversation.name;
  302. getUsers();
  303. setState(() {});
  304. }
  305. }