import 'dart:io'; import 'package:Capsule/components/custom_title_bar.dart'; import 'package:Capsule/components/flash_message.dart'; import 'package:Capsule/exceptions/update_data_exception.dart'; import 'package:Capsule/models/friends.dart'; import 'package:Capsule/utils/encryption/crypto_utils.dart'; import 'package:Capsule/utils/storage/write_file.dart'; import 'package:Capsule/views/main/conversation/create_add_users.dart'; import 'package:flutter/material.dart'; import '/models/conversation_users.dart'; import '/models/conversations.dart'; import '/models/my_profile.dart'; import '/views/main/conversation/settings_user_list_item.dart'; import '/views/main/conversation/edit_details.dart'; import '/components/custom_circle_avatar.dart'; import '/utils/storage/database.dart'; import '/utils/storage/conversations.dart'; class ConversationSettings extends StatefulWidget { final Conversation conversation; const ConversationSettings({ Key? key, required this.conversation, }) : super(key: key); @override State createState() => _ConversationSettingsState(); } class _ConversationSettingsState extends State { List users = []; MyProfile? profile; TextEditingController nameController = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( appBar: CustomTitleBar( title: Text( widget.conversation.name + ' Settings', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Theme.of(context).appBarTheme.toolbarTextStyle?.color ), ), showBack: true, ), body: Padding( padding: const EdgeInsets.all(15), child: SingleChildScrollView( child: Column( children: [ const SizedBox(height: 30), conversationName(), const SizedBox(height: 25), widget.conversation.admin ? sectionTitle('Settings') : const SizedBox.shrink(), widget.conversation.admin ? settings() : const SizedBox.shrink(), widget.conversation.admin ? const SizedBox(height: 25) : const SizedBox.shrink(), sectionTitle('Members', showUsersAdd: widget.conversation.admin && !widget.conversation.twoUser), usersList(), const SizedBox(height: 25), myAccess(), ], ), ), ), ); } Widget conversationName() { return Row( children: [ CustomCircleAvatar( icon: const Icon(Icons.people, size: 40), radius: 30, image: widget.conversation.icon, ), const SizedBox(width: 10), Text( widget.conversation.name, style: const TextStyle( fontSize: 25, fontWeight: FontWeight.w500, ), ), widget.conversation.admin && !widget.conversation.twoUser ? IconButton( iconSize: 20, icon: const Icon(Icons.edit), padding: const EdgeInsets.all(5.0), splashRadius: 25, onPressed: () { Navigator.of(context).push( MaterialPageRoute(builder: (context) => ConversationEditDetails( // TODO: Move saveCallback to somewhere else saveCallback: (String conversationName, File? file) async { bool updatedImage = false; File? writtenFile; if (file != null) { updatedImage = file.hashCode != widget.conversation.icon.hashCode; writtenFile = await writeImage( widget.conversation.id, file.readAsBytesSync(), ); } widget.conversation.name = conversationName; widget.conversation.icon = writtenFile; final db = await getDatabaseConnection(); db.update( 'conversations', widget.conversation.toMap(), where: 'id = ?', whereArgs: [widget.conversation.id], ); await updateConversation(widget.conversation, updatedImage: updatedImage) .catchError((error) { String message = error.toString(); if (error.runtimeType != UpdateDataException) { message = 'An error occured, please try again later'; } showMessage(message, context); }); setState(() {}); Navigator.pop(context); }, conversation: widget.conversation, )), ).then(onGoBack); }, ) : const SizedBox.shrink(), ], ); } Future getUsers() async { users = await getConversationUsers(widget.conversation); profile = await MyProfile.getProfile(); setState(() {}); } @override void initState() { nameController.text = widget.conversation.name; super.initState(); getUsers(); } Widget myAccess() { return Align( alignment: Alignment.centerLeft, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ TextButton.icon( label: const Text( 'Leave Conversation', style: TextStyle(fontSize: 16) ), icon: const Icon(Icons.exit_to_app), style: const ButtonStyle( alignment: Alignment.centerLeft, ), onPressed: () { print('Leave Group'); } ), ], ), ); } Widget sectionTitle(String title, { bool showUsersAdd = false}) { return Align( alignment: Alignment.centerLeft, child: Padding( padding: const EdgeInsets.only(right: 6), child: Row( children: [ Expanded( child: Container( padding: const EdgeInsets.only(left: 12), child: Text( title, style: const TextStyle(fontSize: 20), ), ), ), !showUsersAdd ? const SizedBox.shrink() : IconButton( icon: const Icon(Icons.add), padding: const EdgeInsets.all(0), onPressed: () async { List friends = await unselectedFriends(); Navigator.of(context).push( MaterialPageRoute(builder: (context) => ConversationAddFriendsList( friends: friends, saveCallback: (List selectedFriends) async { addUsersToConversation( widget.conversation, selectedFriends, ); await updateConversation(widget.conversation, includeUsers: true); await getUsers(); Navigator.pop(context); }, )) ); }, ), ], ) ) ); } Widget settings() { return Align( alignment: Alignment.centerLeft, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 5), TextButton.icon( label: const Text( 'Disappearing Messages', style: TextStyle(fontSize: 16) ), icon: const Icon(Icons.timer), style: ButtonStyle( alignment: Alignment.centerLeft, foregroundColor: MaterialStateProperty.resolveWith( (Set states) { return Theme.of(context).colorScheme.onBackground; }, ) ), onPressed: () { print('Disappearing Messages'); } ), TextButton.icon( label: const Text( 'Permissions', style: TextStyle(fontSize: 16) ), icon: const Icon(Icons.lock), style: ButtonStyle( alignment: Alignment.centerLeft, foregroundColor: MaterialStateProperty.resolveWith( (Set states) { return Theme.of(context).colorScheme.onBackground; }, ) ), onPressed: () { print('Permissions'); } ), ], ), ); } Widget usersList() { return ListView.builder( itemCount: users.length, shrinkWrap: true, padding: const EdgeInsets.only(top: 5, bottom: 0), physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, i) { return ConversationSettingsUserListItem( user: users[i], isAdmin: widget.conversation.admin, profile: profile!, // TODO: Fix this ); } ); } Future> unselectedFriends() async { final db = await getDatabaseConnection(); List notInArgs = []; for (var user in users) { notInArgs.add(user.userId); } final List> maps = await db.query( 'friends', where: 'friend_id not in (${List.filled(notInArgs.length, '?').join(',')})', whereArgs: notInArgs, orderBy: 'username', ); return List.generate(maps.length, (i) { return Friend( id: maps[i]['id'], userId: maps[i]['user_id'], friendId: maps[i]['friend_id'], friendSymmetricKey: maps[i]['symmetric_key'], publicKey: CryptoUtils.rsaPublicKeyFromPem(maps[i]['asymmetric_public_key']), acceptedAt: maps[i]['accepted_at'], username: maps[i]['username'], ); }); } onGoBack(dynamic value) async { nameController.text = widget.conversation.name; getUsers(); setState(() {}); } }