diff --git a/mobile/lib/models/conversations.dart b/mobile/lib/models/conversations.dart index ed06387..3494e48 100644 --- a/mobile/lib/models/conversations.dart +++ b/mobile/lib/models/conversations.dart @@ -1,22 +1,131 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:Envelope/models/conversation_users.dart'; -import 'package:Envelope/models/friends.dart'; -import 'package:Envelope/models/my_profile.dart'; + import 'package:pointycastle/export.dart'; import 'package:sqflite/sqflite.dart'; import 'package:uuid/uuid.dart'; -import '/utils/encryption/crypto_utils.dart'; + +import '/models/conversation_users.dart'; +import '/models/friends.dart'; +import '/models/my_profile.dart'; import '/utils/encryption/aes_helper.dart'; +import '/utils/encryption/crypto_utils.dart'; import '/utils/storage/database.dart'; import '/utils/strings.dart'; -enum ConversationStatus { - complete, - pending, - error, +Future createConversation(String title, List friends) async { + final db = await getDatabaseConnection(); + + MyProfile profile = await MyProfile.getProfile(); + + var uuid = const Uuid(); + final String conversationId = uuid.v4(); + + Uint8List symmetricKey = AesHelper.deriveKey(generateRandomString(32)); + + String associationKey = generateRandomString(32); + + Conversation conversation = Conversation( + id: conversationId, + userId: profile.id, + conversationDetailId: '', + symmetricKey: base64.encode(symmetricKey), + admin: true, + name: title, + status: ConversationStatus.pending, + ); + + await db.insert( + 'conversations', + conversation.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + + await db.insert( + 'conversation_users', + ConversationUser( + id: uuid.v4(), + userId: profile.id, + conversationId: conversationId, + username: profile.username, + associationKey: associationKey, + admin: true, + ).toMap(), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + + for (Friend friend in friends) { + await db.insert( + 'conversation_users', + ConversationUser( + id: uuid.v4(), + userId: friend.friendId, + conversationId: conversationId, + username: friend.username, + associationKey: associationKey, + admin: false, + ).toMap(), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + } + + return conversation; +} + +Conversation findConversationByDetailId(List conversations, String id) { + for (var conversation in conversations) { + if (conversation.conversationDetailId == id) { + return conversation; + } + } + // Or return `null`. + throw ArgumentError.value(id, "id", "No element with that id"); +} + +Future getConversationById(String id) async { + final db = await getDatabaseConnection(); + + final List> maps = await db.query( + 'conversations', + where: 'id = ?', + whereArgs: [id], + ); + + if (maps.length != 1) { + throw ArgumentError('Invalid user id'); + } + + return Conversation( + id: maps[0]['id'], + userId: maps[0]['user_id'], + conversationDetailId: maps[0]['conversation_detail_id'], + symmetricKey: maps[0]['symmetric_key'], + admin: maps[0]['admin'] == 1, + name: maps[0]['name'], + status: ConversationStatus.values[maps[0]['status']], + ); } +// A method that retrieves all the dogs from the dogs table. +Future> getConversations() async { + final db = await getDatabaseConnection(); + + final List> maps = await db.query('conversations'); + + return List.generate(maps.length, (i) { + return Conversation( + id: maps[i]['id'], + userId: maps[i]['user_id'], + conversationDetailId: maps[i]['conversation_detail_id'], + symmetricKey: maps[i]['symmetric_key'], + admin: maps[i]['admin'] == 1, + name: maps[i]['name'], + status: ConversationStatus.values[maps[i]['status']], + ); + }); +} + + class Conversation { String id; String userId; @@ -113,17 +222,6 @@ class Conversation { }; } - @override - String toString() { - return ''' - - - id: $id - userId: $userId - name: $name - admin: $admin'''; - } - Map toMap() { return { 'id': id, @@ -135,118 +233,22 @@ class Conversation { 'status': status.index, }; } -} - -Future createConversation(String title, List friends) async { - final db = await getDatabaseConnection(); - - MyProfile profile = await MyProfile.getProfile(); - - var uuid = const Uuid(); - final String conversationId = uuid.v4(); - - Uint8List symmetricKey = AesHelper.deriveKey(generateRandomString(32)); - - String associationKey = generateRandomString(32); - - Conversation conversation = Conversation( - id: conversationId, - userId: profile.id, - conversationDetailId: '', - symmetricKey: base64.encode(symmetricKey), - admin: true, - name: title, - status: ConversationStatus.pending, - ); - - await db.insert( - 'conversations', - conversation.toMap(), - conflictAlgorithm: ConflictAlgorithm.replace, - ); - await db.insert( - 'conversation_users', - ConversationUser( - id: uuid.v4(), - userId: profile.id, - conversationId: conversationId, - username: profile.username, - associationKey: associationKey, - admin: true, - ).toMap(), - conflictAlgorithm: ConflictAlgorithm.replace, - ); - - for (Friend friend in friends) { - await db.insert( - 'conversation_users', - ConversationUser( - id: uuid.v4(), - userId: friend.friendId, - conversationId: conversationId, - username: friend.username, - associationKey: associationKey, - admin: false, - ).toMap(), - conflictAlgorithm: ConflictAlgorithm.replace, - ); - } + @override + String toString() { + return ''' - return conversation; -} -Conversation findConversationByDetailId(List conversations, String id) { - for (var conversation in conversations) { - if (conversation.conversationDetailId == id) { - return conversation; - } + id: $id + userId: $userId + name: $name + admin: $admin'''; } - // Or return `null`. - throw ArgumentError.value(id, "id", "No element with that id"); -} - - -// A method that retrieves all the dogs from the dogs table. -Future> getConversations() async { - final db = await getDatabaseConnection(); - - final List> maps = await db.query('conversations'); - - return List.generate(maps.length, (i) { - return Conversation( - id: maps[i]['id'], - userId: maps[i]['user_id'], - conversationDetailId: maps[i]['conversation_detail_id'], - symmetricKey: maps[i]['symmetric_key'], - admin: maps[i]['admin'] == 1, - name: maps[i]['name'], - status: ConversationStatus.values[maps[i]['status']], - ); - }); } -Future getConversationById(String id) async { - final db = await getDatabaseConnection(); - - final List> maps = await db.query( - 'conversations', - where: 'id = ?', - whereArgs: [id], - ); - - if (maps.length != 1) { - throw ArgumentError('Invalid user id'); - } - - return Conversation( - id: maps[0]['id'], - userId: maps[0]['user_id'], - conversationDetailId: maps[0]['conversation_detail_id'], - symmetricKey: maps[0]['symmetric_key'], - admin: maps[0]['admin'] == 1, - name: maps[0]['name'], - status: ConversationStatus.values[maps[0]['status']], - ); +enum ConversationStatus { + complete, + pending, + error, } diff --git a/mobile/lib/utils/storage/messages.dart b/mobile/lib/utils/storage/messages.dart index bc524b3..4342e43 100644 --- a/mobile/lib/utils/storage/messages.dart +++ b/mobile/lib/utils/storage/messages.dart @@ -1,65 +1,17 @@ import 'dart:convert'; -import 'package:Envelope/models/my_profile.dart'; -import 'package:uuid/uuid.dart'; -import 'package:Envelope/models/conversation_users.dart'; -import 'package:http/http.dart' as http; + import 'package:flutter_dotenv/flutter_dotenv.dart'; -import 'package:sqflite/sqflite.dart'; +import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; -import '/utils/storage/session_cookie.dart'; -import '/utils/storage/database.dart'; +import 'package:sqflite/sqflite.dart'; +import 'package:uuid/uuid.dart'; + +import '/models/conversation_users.dart'; import '/models/conversations.dart'; import '/models/messages.dart'; - -Future updateMessageThread(Conversation conversation, {MyProfile? profile}) async { - profile ??= await MyProfile.getProfile(); - ConversationUser currentUser = await getConversationUser(conversation, profile.id); - - var resp = await http.get( - Uri.parse('${dotenv.env["SERVER_URL"]}api/v1/auth/messages/${currentUser.associationKey}'), - headers: { - 'cookie': await getSessionCookie(), - } - ); - - if (resp.statusCode != 200) { - throw Exception(resp.body); - } - - List messageThreadJson = jsonDecode(resp.body); - - final db = await getDatabaseConnection(); - - for (var i = 0; i < messageThreadJson.length; i++) { - Message message = Message.fromJson( - messageThreadJson[i] as Map, - profile.privateKey!, - ); - - ConversationUser messageUser = await getConversationUser(conversation, message.senderId); - message.senderUsername = messageUser.username; - - await db.insert( - 'messages', - message.toMap(), - conflictAlgorithm: ConflictAlgorithm.replace, - ); - } -} - -Future updateMessageThreads({List? conversations}) async { - // try { - MyProfile profile = await MyProfile.getProfile(); - - conversations ??= await getConversations(); - - for (var i = 0; i < conversations.length; i++) { - await updateMessageThread(conversations[i], profile: profile); - } - // } catch(SocketException) { - // return; - // } -} +import '/models/my_profile.dart'; +import '/utils/storage/database.dart'; +import '/utils/storage/session_cookie.dart'; Future sendMessage(Conversation conversation, String data) async { final preferences = await SharedPreferences.getInstance(); @@ -122,3 +74,53 @@ Future sendMessage(Conversation conversation, String data) async { ); }); } + +Future updateMessageThread(Conversation conversation, {MyProfile? profile}) async { + profile ??= await MyProfile.getProfile(); + ConversationUser currentUser = await getConversationUser(conversation, profile.id); + + var resp = await http.get( + Uri.parse('${dotenv.env["SERVER_URL"]}api/v1/auth/messages/${currentUser.associationKey}'), + headers: { + 'cookie': await getSessionCookie(), + } + ); + + if (resp.statusCode != 200) { + throw Exception(resp.body); + } + + List messageThreadJson = jsonDecode(resp.body); + + final db = await getDatabaseConnection(); + + for (var i = 0; i < messageThreadJson.length; i++) { + Message message = Message.fromJson( + messageThreadJson[i] as Map, + profile.privateKey!, + ); + + ConversationUser messageUser = await getConversationUser(conversation, message.senderId); + message.senderUsername = messageUser.username; + + await db.insert( + 'messages', + message.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + } +} + +Future updateMessageThreads({List? conversations}) async { + // try { + MyProfile profile = await MyProfile.getProfile(); + + conversations ??= await getConversations(); + + for (var i = 0; i < conversations.length; i++) { + await updateMessageThread(conversations[i], profile: profile); + } + // } catch(SocketException) { + // return; + // } +} diff --git a/mobile/lib/views/main/conversation_create_add_users.dart b/mobile/lib/views/main/conversation/create_add_users.dart similarity index 95% rename from mobile/lib/views/main/conversation_create_add_users.dart rename to mobile/lib/views/main/conversation/create_add_users.dart index 7880dc8..4e9ed49 100644 --- a/mobile/lib/views/main/conversation_create_add_users.dart +++ b/mobile/lib/views/main/conversation/create_add_users.dart @@ -1,9 +1,10 @@ -import 'package:Envelope/models/conversations.dart'; -import 'package:Envelope/utils/storage/conversations.dart'; -import 'package:Envelope/views/main/conversation_create_add_users_list.dart'; -import 'package:Envelope/views/main/conversation_detail.dart'; import 'package:flutter/material.dart'; + +import '/models/conversations.dart'; import '/models/friends.dart'; +import '/utils/storage/conversations.dart'; +import '/views/main/conversation/create_add_users_list.dart'; +import '/views/main/conversation/detail.dart'; class ConversationAddFriendsList extends StatefulWidget { final List friends; @@ -22,67 +23,6 @@ class _ConversationAddFriendsListState extends State List friends = []; List friendsSelected = []; - @override - void initState() { - super.initState(); - friends.addAll(widget.friends); - setState(() {}); - } - - void filterSearchResults(String query) { - List dummySearchList = []; - dummySearchList.addAll(widget.friends); - - if(query.isNotEmpty) { - List dummyListData = []; - for (Friend friend in dummySearchList) { - if (friend.username.toLowerCase().contains(query)) { - dummyListData.add(friend); - } - } - setState(() { - friends.clear(); - friends.addAll(dummyListData); - }); - return; - } - - setState(() { - friends.clear(); - friends.addAll(widget.friends); - }); - } - - Widget list() { - if (friends.isEmpty) { - return const Center( - child: Text('No Friends'), - ); - } - - return ListView.builder( - itemCount: friends.length, - shrinkWrap: true, - padding: const EdgeInsets.only(top: 16), - physics: const BouncingScrollPhysics(), - itemBuilder: (context, i) { - return ConversationAddFriendItem( - friend: friends[i], - isSelected: (bool value) { - setState(() { - widget.friends[i].selected = value; - if (value) { - friendsSelected.add(friends[i]); - return; - } - friendsSelected.remove(friends[i]); - }); - } - ); - }, - ); - } - @override Widget build(BuildContext context) { return Scaffold( @@ -171,4 +111,65 @@ class _ConversationAddFriendsListState extends State ), ); } + + void filterSearchResults(String query) { + List dummySearchList = []; + dummySearchList.addAll(widget.friends); + + if(query.isNotEmpty) { + List dummyListData = []; + for (Friend friend in dummySearchList) { + if (friend.username.toLowerCase().contains(query)) { + dummyListData.add(friend); + } + } + setState(() { + friends.clear(); + friends.addAll(dummyListData); + }); + return; + } + + setState(() { + friends.clear(); + friends.addAll(widget.friends); + }); + } + + @override + void initState() { + super.initState(); + friends.addAll(widget.friends); + setState(() {}); + } + + Widget list() { + if (friends.isEmpty) { + return const Center( + child: Text('No Friends'), + ); + } + + return ListView.builder( + itemCount: friends.length, + shrinkWrap: true, + padding: const EdgeInsets.only(top: 16), + physics: const BouncingScrollPhysics(), + itemBuilder: (context, i) { + return ConversationAddFriendItem( + friend: friends[i], + isSelected: (bool value) { + setState(() { + widget.friends[i].selected = value; + if (value) { + friendsSelected.add(friends[i]); + return; + } + friendsSelected.remove(friends[i]); + }); + } + ); + }, + ); + } } diff --git a/mobile/lib/views/main/conversation_create_add_users_list.dart b/mobile/lib/views/main/conversation/create_add_users_list.dart similarity index 100% rename from mobile/lib/views/main/conversation_create_add_users_list.dart rename to mobile/lib/views/main/conversation/create_add_users_list.dart diff --git a/mobile/lib/views/main/conversation_detail.dart b/mobile/lib/views/main/conversation/detail.dart similarity index 99% rename from mobile/lib/views/main/conversation_detail.dart rename to mobile/lib/views/main/conversation/detail.dart index 02bb964..511b538 100644 --- a/mobile/lib/views/main/conversation_detail.dart +++ b/mobile/lib/views/main/conversation/detail.dart @@ -1,9 +1,10 @@ -import 'package:Envelope/models/my_profile.dart'; -import 'package:Envelope/views/main/conversation_settings.dart'; import 'package:flutter/material.dart'; + import '/models/conversations.dart'; import '/models/messages.dart'; +import '/models/my_profile.dart'; import '/utils/storage/messages.dart'; +import '/views/main/conversation/settings.dart'; String convertToAgo(String input){ DateTime time = DateTime.parse(input); @@ -42,50 +43,6 @@ class _ConversationDetailState extends State { TextEditingController msgController = TextEditingController(); - @override - void initState() { - super.initState(); - fetchMessages(); - } - - Future fetchMessages() async { - profile = await MyProfile.getProfile(); - messages = await getMessagesForThread(widget.conversation); - setState(() {}); - } - - Widget usernameOrFailedToSend(int index) { - if (messages[index].senderUsername != profile.username) { - return Text( - messages[index].senderUsername, - style: TextStyle( - fontSize: 12, - color: Colors.grey[300], - ), - ); - } - - if (messages[index].failedToSend) { - return Row( - mainAxisAlignment: MainAxisAlignment.end, - children: const [ - Icon( - Icons.warning_rounded, - color: Colors.red, - size: 20, - ), - Text( - 'Failed to send', - style: TextStyle(color: Colors.red, fontSize: 12), - textAlign: TextAlign.right, - ), - ], - ); - } - - return const SizedBox.shrink(); - } - @override Widget build(BuildContext context) { return Scaffold( @@ -296,4 +253,48 @@ class _ConversationDetailState extends State { ), ); } + + Future fetchMessages() async { + profile = await MyProfile.getProfile(); + messages = await getMessagesForThread(widget.conversation); + setState(() {}); + } + + @override + void initState() { + super.initState(); + fetchMessages(); + } + + Widget usernameOrFailedToSend(int index) { + if (messages[index].senderUsername != profile.username) { + return Text( + messages[index].senderUsername, + style: TextStyle( + fontSize: 12, + color: Colors.grey[300], + ), + ); + } + + if (messages[index].failedToSend) { + return Row( + mainAxisAlignment: MainAxisAlignment.end, + children: const [ + Icon( + Icons.warning_rounded, + color: Colors.red, + size: 20, + ), + Text( + 'Failed to send', + style: TextStyle(color: Colors.red, fontSize: 12), + textAlign: TextAlign.right, + ), + ], + ); + } + + return const SizedBox.shrink(); + } } diff --git a/mobile/lib/views/main/conversation_edit_details.dart b/mobile/lib/views/main/conversation/edit_details.dart similarity index 98% rename from mobile/lib/views/main/conversation_edit_details.dart rename to mobile/lib/views/main/conversation/edit_details.dart index a882913..be48588 100644 --- a/mobile/lib/views/main/conversation_edit_details.dart +++ b/mobile/lib/views/main/conversation/edit_details.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; + import '/components/custom_circle_avatar.dart'; -import '/views/main/conversation_create_add_users.dart'; -import '/models/friends.dart'; import '/models/conversations.dart'; +import '/models/friends.dart'; +import '/views/main/conversation/create_add_users.dart'; class ConversationEditDetails extends StatefulWidget { final Conversation? conversation; diff --git a/mobile/lib/views/main/conversation_list.dart b/mobile/lib/views/main/conversation/list.dart similarity index 97% rename from mobile/lib/views/main/conversation_list.dart rename to mobile/lib/views/main/conversation/list.dart index 7de543c..8ec0931 100644 --- a/mobile/lib/views/main/conversation_list.dart +++ b/mobile/lib/views/main/conversation/list.dart @@ -1,8 +1,9 @@ import 'package:Envelope/models/friends.dart'; -import 'package:Envelope/views/main/conversation_edit_details.dart'; import 'package:flutter/material.dart'; + import '/models/conversations.dart'; -import '/views/main/conversation_list_item.dart'; +import '/views/main/conversation/edit_details.dart'; +import '/views/main/conversation/list_item.dart'; class ConversationList extends StatefulWidget { final List conversations; @@ -21,58 +22,6 @@ class _ConversationListState extends State { List conversations = []; List friends = []; - @override - void initState() { - super.initState(); - conversations.addAll(widget.conversations); - friends.addAll(widget.friends); - setState(() {}); - } - - void filterSearchResults(String query) { - List dummySearchList = []; - dummySearchList.addAll(widget.conversations); - - if(query.isNotEmpty) { - List dummyListData = []; - for (Conversation item in dummySearchList) { - if (item.name.toLowerCase().contains(query)) { - dummyListData.add(item); - } - } - setState(() { - conversations.clear(); - conversations.addAll(dummyListData); - }); - return; - } - - setState(() { - conversations.clear(); - conversations.addAll(widget.conversations); - }); - } - - Widget list() { - if (conversations.isEmpty) { - return const Center( - child: Text('No Conversations'), - ); - } - - return ListView.builder( - itemCount: conversations.length, - shrinkWrap: true, - padding: const EdgeInsets.only(top: 16), - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, i) { - return ConversationListItem( - conversation: conversations[i], - ); - }, - ); - } - @override Widget build(BuildContext context) { return Scaffold( @@ -135,6 +84,58 @@ class _ConversationListState extends State { ); } + void filterSearchResults(String query) { + List dummySearchList = []; + dummySearchList.addAll(widget.conversations); + + if(query.isNotEmpty) { + List dummyListData = []; + for (Conversation item in dummySearchList) { + if (item.name.toLowerCase().contains(query)) { + dummyListData.add(item); + } + } + setState(() { + conversations.clear(); + conversations.addAll(dummyListData); + }); + return; + } + + setState(() { + conversations.clear(); + conversations.addAll(widget.conversations); + }); + } + + @override + void initState() { + super.initState(); + conversations.addAll(widget.conversations); + friends.addAll(widget.friends); + setState(() {}); + } + + Widget list() { + if (conversations.isEmpty) { + return const Center( + child: Text('No Conversations'), + ); + } + + return ListView.builder( + itemCount: conversations.length, + shrinkWrap: true, + padding: const EdgeInsets.only(top: 16), + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, i) { + return ConversationListItem( + conversation: conversations[i], + ); + }, + ); + } + onGoBack(dynamic value) async { conversations = await getConversations(); friends = await getFriends(); diff --git a/mobile/lib/views/main/conversation_list_item.dart b/mobile/lib/views/main/conversation/list_item.dart similarity index 95% rename from mobile/lib/views/main/conversation_list_item.dart rename to mobile/lib/views/main/conversation/list_item.dart index b26c0fc..45015e9 100644 --- a/mobile/lib/views/main/conversation_list_item.dart +++ b/mobile/lib/views/main/conversation/list_item.dart @@ -1,7 +1,8 @@ -import 'package:Envelope/components/custom_circle_avatar.dart'; -import 'package:Envelope/models/conversations.dart'; import 'package:flutter/material.dart'; -import '/views/main/conversation_detail.dart'; + +import '/components/custom_circle_avatar.dart'; +import '/models/conversations.dart'; +import '/views/main/conversation/detail.dart'; class ConversationListItem extends StatefulWidget{ final Conversation conversation; @@ -18,14 +19,6 @@ class _ConversationListItemState extends State { late Conversation conversation; bool loaded = false; - @override - void initState() { - super.initState(); - conversation = widget.conversation; - loaded = true; - setState(() {}); - } - @override Widget build(BuildContext context) { return GestureDetector( @@ -76,6 +69,14 @@ class _ConversationListItemState extends State { ); } + @override + void initState() { + super.initState(); + conversation = widget.conversation; + loaded = true; + setState(() {}); + } + onGoBack(dynamic value) async { conversation = await getConversationById(widget.conversation.id); setState(() {}); diff --git a/mobile/lib/views/main/conversation_settings.dart b/mobile/lib/views/main/conversation/settings.dart similarity index 97% rename from mobile/lib/views/main/conversation_settings.dart rename to mobile/lib/views/main/conversation/settings.dart index caf917d..85dbc7e 100644 --- a/mobile/lib/views/main/conversation_settings.dart +++ b/mobile/lib/views/main/conversation/settings.dart @@ -1,9 +1,10 @@ -import 'package:Envelope/models/conversation_users.dart'; -import 'package:Envelope/models/my_profile.dart'; +import 'package:Envelope/components/custom_circle_avatar.dart'; import 'package:flutter/material.dart'; -import '/views/main/conversation_settings_user_list_item.dart'; + +import '/models/conversation_users.dart'; import '/models/conversations.dart'; -import 'package:Envelope/components/custom_circle_avatar.dart'; +import '/models/my_profile.dart'; +import '/views/main/conversation/settings_user_list_item.dart'; class ConversationSettings extends StatefulWidget { final Conversation conversation; @@ -23,16 +24,67 @@ class _ConversationSettingsState extends State { TextEditingController nameController = TextEditingController(); @override - void initState() { - nameController.text = widget.conversation.name; - super.initState(); - getUsers(); - } - - Future getUsers() async { - users = await getConversationUsers(widget.conversation); - profile = await MyProfile.getProfile(); - setState(() {}); + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + automaticallyImplyLeading: false, + flexibleSpace: SafeArea( + child: Container( + padding: const EdgeInsets.only(right: 16), + child: Row( + children: [ + IconButton( + onPressed: (){ + Navigator.pop(context); + }, + icon: const Icon(Icons.arrow_back), + ), + const SizedBox(width: 2,), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + widget.conversation.name + " Settings", + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600 + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + body: Padding( + padding: const EdgeInsets.all(15), + 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'), + usersList(), + const SizedBox(height: 25), + myAccess(), + ], + ), + ), + ); } Widget conversationName() { @@ -64,6 +116,43 @@ class _ConversationSettingsState extends State { ); } + 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) { return Align( alignment: Alignment.centerLeft, @@ -125,30 +214,6 @@ class _ConversationSettingsState extends State { ); } - 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 usersList() { return ListView.builder( itemCount: users.length, @@ -163,69 +228,5 @@ class _ConversationSettingsState extends State { } ); } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - elevation: 0, - automaticallyImplyLeading: false, - flexibleSpace: SafeArea( - child: Container( - padding: const EdgeInsets.only(right: 16), - child: Row( - children: [ - IconButton( - onPressed: (){ - Navigator.pop(context); - }, - icon: const Icon(Icons.arrow_back), - ), - const SizedBox(width: 2,), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - widget.conversation.name + " Settings", - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600 - ), - ), - ], - ), - ), - ], - ), - ), - ), - ), - body: Padding( - padding: const EdgeInsets.all(15), - 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'), - usersList(), - const SizedBox(height: 25), - myAccess(), - ], - ), - ), - ); - } } diff --git a/mobile/lib/views/main/conversation_settings_user_list_item.dart b/mobile/lib/views/main/conversation/settings_user_list_item.dart similarity index 95% rename from mobile/lib/views/main/conversation_settings_user_list_item.dart rename to mobile/lib/views/main/conversation/settings_user_list_item.dart index b261aad..7c676a0 100644 --- a/mobile/lib/views/main/conversation_settings_user_list_item.dart +++ b/mobile/lib/views/main/conversation/settings_user_list_item.dart @@ -1,7 +1,8 @@ -import 'package:Envelope/models/my_profile.dart'; import 'package:flutter/material.dart'; -import 'package:Envelope/models/conversation_users.dart'; -import 'package:Envelope/components/custom_circle_avatar.dart'; + +import '/components/custom_circle_avatar.dart'; +import '/models/conversation_users.dart'; +import '/models/my_profile.dart'; class ConversationSettingsUserListItem extends StatefulWidget{ final ConversationUser user; diff --git a/mobile/lib/views/main/friend_list.dart b/mobile/lib/views/main/friend/list.dart similarity index 99% rename from mobile/lib/views/main/friend_list.dart rename to mobile/lib/views/main/friend/list.dart index f1076ef..40b8fd3 100644 --- a/mobile/lib/views/main/friend_list.dart +++ b/mobile/lib/views/main/friend/list.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; + import '/models/friends.dart'; -import '/views/main/friend_list_item.dart'; +import '/views/main/friend/list_item.dart'; class FriendList extends StatefulWidget { final List friends; @@ -17,57 +18,6 @@ class _FriendListState extends State { List friends = []; List friendsDuplicate = []; - @override - void initState() { - super.initState(); - friends.addAll(widget.friends); - setState(() {}); - } - - void filterSearchResults(String query) { - List dummySearchList = []; - dummySearchList.addAll(widget.friends); - - if(query.isNotEmpty) { - List dummyListData = []; - dummySearchList.forEach((item) { - if(item.username.toLowerCase().contains(query)) { - dummyListData.add(item); - } - }); - setState(() { - friends.clear(); - friends.addAll(dummyListData); - }); - return; - } - - setState(() { - friends.clear(); - friends.addAll(widget.friends); - }); - } - - Widget list() { - if (friends.isEmpty) { - return const Center( - child: Text('No Friends'), - ); - } - - return ListView.builder( - itemCount: friends.length, - shrinkWrap: true, - padding: const EdgeInsets.only(top: 16), - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, i) { - return FriendListItem( - friend: friends[i], - ); - }, - ); - } - @override Widget build(BuildContext context) { return Scaffold( @@ -134,4 +84,55 @@ class _FriendListState extends State { ), ); } + + void filterSearchResults(String query) { + List dummySearchList = []; + dummySearchList.addAll(widget.friends); + + if(query.isNotEmpty) { + List dummyListData = []; + dummySearchList.forEach((item) { + if(item.username.toLowerCase().contains(query)) { + dummyListData.add(item); + } + }); + setState(() { + friends.clear(); + friends.addAll(dummyListData); + }); + return; + } + + setState(() { + friends.clear(); + friends.addAll(widget.friends); + }); + } + + @override + void initState() { + super.initState(); + friends.addAll(widget.friends); + setState(() {}); + } + + Widget list() { + if (friends.isEmpty) { + return const Center( + child: Text('No Friends'), + ); + } + + return ListView.builder( + itemCount: friends.length, + shrinkWrap: true, + padding: const EdgeInsets.only(top: 16), + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, i) { + return FriendListItem( + friend: friends[i], + ); + }, + ); + } } diff --git a/mobile/lib/views/main/friend_list_item.dart b/mobile/lib/views/main/friend/list_item.dart similarity index 100% rename from mobile/lib/views/main/friend_list_item.dart rename to mobile/lib/views/main/friend/list_item.dart diff --git a/mobile/lib/views/main/home.dart b/mobile/lib/views/main/home.dart index cb077d4..80f2be5 100644 --- a/mobile/lib/views/main/home.dart +++ b/mobile/lib/views/main/home.dart @@ -1,16 +1,17 @@ import 'package:flutter/material.dart'; -import 'package:http/http.dart' as http; import 'package:flutter_dotenv/flutter_dotenv.dart'; -import '/views/main/conversation_list.dart'; -import '/views/main/friend_list.dart'; -import '/views/main/profile.dart'; -import '/utils/storage/friends.dart'; -import '/utils/storage/conversations.dart'; -import '/utils/storage/messages.dart'; -import '/utils/storage/session_cookie.dart'; +import 'package:http/http.dart' as http; + import '/models/conversations.dart'; import '/models/friends.dart'; import '/models/my_profile.dart'; +import '/utils/storage/conversations.dart'; +import '/utils/storage/friends.dart'; +import '/utils/storage/messages.dart'; +import '/utils/storage/session_cookie.dart'; +import '/views/main/conversation/list.dart'; +import '/views/main/friend/list.dart'; +import '/views/main/profile/profile.dart'; class Home extends StatefulWidget { const Home({Key? key}) : super(key: key); @@ -41,35 +42,36 @@ class _HomeState extends State { ]; @override - void initState() { - updateData(); - super.initState(); - } - - void updateData() async { - if (!await checkLogin()) { - return; - } - - await updateFriends(); - await updateConversations(); - await updateMessageThreads(); - - conversations = await getConversations(); - friends = await getFriends(); - profile = await MyProfile.getProfile(); - - setState(() { - _widgetOptions = [ - ConversationList( - conversations: conversations, - friends: friends, + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async => false, + child: isLoading ? loading() : Scaffold( + body: _widgetOptions.elementAt(_selectedIndex), + bottomNavigationBar: isLoading ? const SizedBox.shrink() : BottomNavigationBar( + currentIndex: _selectedIndex, + onTap: _onItemTapped, + selectedItemColor: Theme.of(context).primaryColor, + unselectedItemColor: Theme.of(context).hintColor, + selectedLabelStyle: const TextStyle(fontWeight: FontWeight.w600), + unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.w600), + type: BottomNavigationBarType.fixed, + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.message), + label: "Chats", + ), + BottomNavigationBarItem( + icon: Icon(Icons.group_work), + label: "Friends", + ), + BottomNavigationBarItem( + icon: Icon(Icons.account_box), + label: "Profile", + ), + ], + ), ), - FriendList(friends: friends), - Profile(profile: profile), - ]; - isLoading = false; - }); + ); } Future checkLogin() async { @@ -112,10 +114,10 @@ class _HomeState extends State { return false; } - void _onItemTapped(int index) { - setState(() { - _selectedIndex = index; - }); + @override + void initState() { + updateData(); + super.initState(); } Widget loading() { @@ -140,36 +142,35 @@ class _HomeState extends State { ); } - @override - Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () async => false, - child: isLoading ? loading() : Scaffold( - body: _widgetOptions.elementAt(_selectedIndex), - bottomNavigationBar: isLoading ? const SizedBox.shrink() : BottomNavigationBar( - currentIndex: _selectedIndex, - onTap: _onItemTapped, - selectedItemColor: Theme.of(context).primaryColor, - unselectedItemColor: Theme.of(context).hintColor, - selectedLabelStyle: const TextStyle(fontWeight: FontWeight.w600), - unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.w600), - type: BottomNavigationBarType.fixed, - items: const [ - BottomNavigationBarItem( - icon: Icon(Icons.message), - label: "Chats", - ), - BottomNavigationBarItem( - icon: Icon(Icons.group_work), - label: "Friends", - ), - BottomNavigationBarItem( - icon: Icon(Icons.account_box), - label: "Profile", - ), - ], - ), + void updateData() async { + if (!await checkLogin()) { + return; + } + + await updateFriends(); + await updateConversations(); + await updateMessageThreads(); + + conversations = await getConversations(); + friends = await getFriends(); + profile = await MyProfile.getProfile(); + + setState(() { + _widgetOptions = [ + ConversationList( + conversations: conversations, + friends: friends, ), - ); + FriendList(friends: friends), + Profile(profile: profile), + ]; + isLoading = false; + }); + } + + void _onItemTapped(int index) { + setState(() { + _selectedIndex = index; + }); } } diff --git a/mobile/lib/views/main/profile.dart b/mobile/lib/views/main/profile/profile.dart similarity index 100% rename from mobile/lib/views/main/profile.dart rename to mobile/lib/views/main/profile/profile.dart