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.

323 lines
10 KiB

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