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.

304 lines
9.0 KiB

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