From 2a5f2438251476fca9407e8c40d95b3d3bb163ae Mon Sep 17 00:00:00 2001 From: Tovi Jaeschke-Rogers Date: Sat, 13 Aug 2022 11:26:33 +0930 Subject: [PATCH] Fix state management on home page, and fix user conversations --- Backend/Api/Messages/CreateConversation.go | 8 ++- Backend/Api/Routes.go | 3 +- mobile/lib/models/conversations.dart | 57 +++++++++---------- mobile/lib/utils/storage/conversations.dart | 37 +++++++++--- .../unauthenticated_landing.dart | 4 +- .../lib/views/main/conversation/detail.dart | 4 +- mobile/lib/views/main/conversation/list.dart | 2 +- .../views/main/conversation/list_item.dart | 6 +- .../lib/views/main/conversation/settings.dart | 6 +- .../conversation/settings_user_list_item.dart | 2 +- mobile/lib/views/main/friend/list_item.dart | 7 ++- mobile/lib/views/main/home.dart | 32 +++++++++-- 12 files changed, 105 insertions(+), 63 deletions(-) diff --git a/Backend/Api/Messages/CreateConversation.go b/Backend/Api/Messages/CreateConversation.go index 5241995..41de38c 100644 --- a/Backend/Api/Messages/CreateConversation.go +++ b/Backend/Api/Messages/CreateConversation.go @@ -10,13 +10,16 @@ import ( "git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" ) +// RawCreateConversationData for holding POST payload type RawCreateConversationData struct { ID string `json:"id"` Name string `json:"name"` + TwoUser string `json:"two_user"` Users []Models.ConversationDetailUser `json:"users"` UserConversations []Models.UserConversation `json:"user_conversations"` } +// CreateConversation creates ConversationDetail, ConversationDetailUsers and UserConversations func CreateConversation(w http.ResponseWriter, r *http.Request) { var ( rawConversationData RawCreateConversationData @@ -34,8 +37,9 @@ func CreateConversation(w http.ResponseWriter, r *http.Request) { Base: Models.Base{ ID: uuid.FromStringOrNil(rawConversationData.ID), }, - Name: rawConversationData.Name, - Users: rawConversationData.Users, + Name: rawConversationData.Name, + TwoUser: rawConversationData.TwoUser, + Users: rawConversationData.Users, } err = Database.CreateConversationDetail(&messageThread) diff --git a/Backend/Api/Routes.go b/Backend/Api/Routes.go index 0143f90..c9d76ee 100644 --- a/Backend/Api/Routes.go +++ b/Backend/Api/Routes.go @@ -70,8 +70,7 @@ func InitAPIEndpoints(router *mux.Router) { authAPI.HandleFunc("/conversations", Messages.EncryptedConversationList).Methods("GET") authAPI.HandleFunc("/conversation_details", Messages.EncryptedConversationDetailsList).Methods("GET") - - authAPI.HandleFunc("/conversations", Messages.CreateConversation).Methods("POST") + authAPI.HandleFunc("/conversations", Messages.reateConversation).Methods("POST") authAPI.HandleFunc("/conversations", Messages.UpdateConversation).Methods("PUT") authAPI.HandleFunc("/message", Messages.CreateMessage).Methods("POST") diff --git a/mobile/lib/models/conversations.dart b/mobile/lib/models/conversations.dart index 69bc423..e7d760d 100644 --- a/mobile/lib/models/conversations.dart +++ b/mobile/lib/models/conversations.dart @@ -34,7 +34,7 @@ Future createConversation(String title, List friends, bool status: ConversationStatus.pending, isRead: true, ); - + await db.insert( 'conversations', conversation.toMap(), @@ -65,12 +65,32 @@ Future createConversation(String title, List friends, bool username: friend.username, associationKey: uuid.v4(), publicKey: friend.publicKey, - admin: false, + admin: twoUser ? true : false, ).toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); } + if (twoUser) { + List> maps = await db.query( + 'conversation_users', + where: 'conversation_id = ? AND user_id != ?', + whereArgs: [ conversation.id, profile.id ], + ); + + if (maps.length != 1) { + throw ArgumentError('Invalid user id'); + } + + conversation.name = maps[0]['username']; + + await db.insert( + 'conversations', + conversation.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + } + return conversation; } @@ -138,7 +158,10 @@ Future getConversationById(String id) async { Future> getConversations() async { final db = await getDatabaseConnection(); - final List> maps = await db.query('conversations'); + final List> maps = await db.query( + 'conversations', + orderBy: 'name', + ); return List.generate(maps.length, (i) { return Conversation( @@ -159,9 +182,6 @@ Future getTwoUserConversation(String userId) async { MyProfile profile = await MyProfile.getProfile(); - print(userId); - print(profile.id); - final List> maps = await db.rawQuery( ''' SELECT conversations.* FROM conversations @@ -281,6 +301,7 @@ class Conversation { 'id': id, 'name': AesHelper.aesEncrypt(symKey, Uint8List.fromList(name.codeUnits)), 'users': await getEncryptedConversationUsers(this, symKey), + 'two_user': AesHelper.aesEncrypt(symKey, Uint8List.fromList((twoUser ? 'true' : 'false').codeUnits)), 'user_conversations': userConversations, }; } @@ -320,7 +341,7 @@ class Conversation { ORDER BY created_at DESC LIMIT 1; ''', - [id], + [ id ], ); if (maps.isEmpty) { @@ -339,28 +360,6 @@ class Conversation { failedToSend: maps[0]['failed_to_send'] == 1, ); } - - Future getName() async { - if (!twoUser) { - return name; - } - - MyProfile profile = await MyProfile.getProfile(); - - final db = await getDatabaseConnection(); - - List> maps = await db.query( - 'conversation_users', - where: 'conversation_id = ? AND user_id != ?', - whereArgs: [ id, profile.id ], - ); - - if (maps.length != 1) { - throw ArgumentError('Invalid user id'); - } - - return maps[0]['username']; - } } diff --git a/mobile/lib/utils/storage/conversations.dart b/mobile/lib/utils/storage/conversations.dart index f4722a3..9ed72e5 100644 --- a/mobile/lib/utils/storage/conversations.dart +++ b/mobile/lib/utils/storage/conversations.dart @@ -1,5 +1,7 @@ import 'dart:convert'; +import 'package:Envelope/components/flash_message.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:http/http.dart' as http; import 'package:pointycastle/export.dart'; @@ -87,16 +89,34 @@ Future updateConversations() async { var conversationDetailJson = conversationsDetailsJson[i] as Map; var conversation = findConversationByDetailId(conversations, conversationDetailJson['id']); - conversation.name = AesHelper.aesDecrypt( - base64.decode(conversation.symmetricKey), - base64.decode(conversationDetailJson['name']), - ); - conversation.twoUser = AesHelper.aesDecrypt( base64.decode(conversation.symmetricKey), base64.decode(conversationDetailJson['two_user']), ) == 'true'; + if (conversation.twoUser) { + MyProfile profile = await MyProfile.getProfile(); + + final db = await getDatabaseConnection(); + + List> maps = await db.query( + 'conversation_users', + where: 'conversation_id = ? AND user_id != ?', + whereArgs: [ conversation.id, profile.id ], + ); + + if (maps.length != 1) { + throw ArgumentError('Invalid user id'); + } + + conversation.name = maps[0]['username']; + } else { + conversation.name = AesHelper.aesDecrypt( + base64.decode(conversation.symmetricKey), + base64.decode(conversationDetailJson['name']), + ); + } + await db.insert( 'conversations', conversation.toMap(), @@ -124,7 +144,7 @@ Future updateConversations() async { } -Future uploadConversation(Conversation conversation) async { +Future uploadConversation(Conversation conversation, BuildContext context) async { String sessionCookie = await getSessionCookie(); Map conversationJson = await conversation.payloadJson(); @@ -138,7 +158,8 @@ Future uploadConversation(Conversation conversation) async { body: jsonEncode(conversationJson), ); - // TODO: Handle errors here - print(x.statusCode); + if (x.statusCode != 200) { + showMessage('Failed to create conversation', context); + } } diff --git a/mobile/lib/views/authentication/unauthenticated_landing.dart b/mobile/lib/views/authentication/unauthenticated_landing.dart index 85748ef..6bcd086 100644 --- a/mobile/lib/views/authentication/unauthenticated_landing.dart +++ b/mobile/lib/views/authentication/unauthenticated_landing.dart @@ -18,9 +18,9 @@ class _UnauthenticatedLandingWidgetState extends State { - String conversationName = ''; List messages = []; MyProfile profile = MyProfile(id: '', username: ''); @@ -32,7 +31,7 @@ class _ConversationDetailState extends State { return Scaffold( appBar: CustomTitleBar( title: Text( - conversationName, + widget.conversation.name, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, @@ -138,7 +137,6 @@ class _ConversationDetailState extends State { } Future fetchMessages() async { - conversationName = await widget.conversation.getName(); profile = await MyProfile.getProfile(); messages = await getMessagesForThread(widget.conversation); setState(() {}); diff --git a/mobile/lib/views/main/conversation/list.dart b/mobile/lib/views/main/conversation/list.dart index a19887b..cabf6f0 100644 --- a/mobile/lib/views/main/conversation/list.dart +++ b/mobile/lib/views/main/conversation/list.dart @@ -77,7 +77,7 @@ class _ConversationListState extends State { false, ); - uploadConversation(conversation); + uploadConversation(conversation, context); Navigator.of(context).popUntil((route) => route.isFirst); Navigator.push(context, MaterialPageRoute(builder: (context){ diff --git a/mobile/lib/views/main/conversation/list_item.dart b/mobile/lib/views/main/conversation/list_item.dart index 5523204..816b996 100644 --- a/mobile/lib/views/main/conversation/list_item.dart +++ b/mobile/lib/views/main/conversation/list_item.dart @@ -19,7 +19,6 @@ class ConversationListItem extends StatefulWidget{ class _ConversationListItemState extends State { late Conversation conversation; - late String conversationName; late Message? recentMessage; bool loaded = false; @@ -42,7 +41,7 @@ class _ConversationListItemState extends State { child: Row( children: [ CustomCircleAvatar( - initials: conversationName[0].toUpperCase(), + initials: widget.conversation.name[0].toUpperCase(), imagePath: null, ), const SizedBox(width: 16), @@ -55,7 +54,7 @@ class _ConversationListItemState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - conversationName, + widget.conversation.name, style: const TextStyle(fontSize: 16) ), recentMessage != null ? @@ -108,7 +107,6 @@ class _ConversationListItemState extends State { Future getConversationData() async { conversation = widget.conversation; - conversationName = await widget.conversation.getName(); recentMessage = await conversation.getRecentMessage(); loaded = true; setState(() {}); diff --git a/mobile/lib/views/main/conversation/settings.dart b/mobile/lib/views/main/conversation/settings.dart index bc291f0..35939e1 100644 --- a/mobile/lib/views/main/conversation/settings.dart +++ b/mobile/lib/views/main/conversation/settings.dart @@ -35,7 +35,7 @@ class _ConversationSettingsState extends State { return Scaffold( appBar: CustomTitleBar( title: Text( - widget.conversation.name + " Settings", + widget.conversation.name + ' Settings', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, @@ -61,7 +61,7 @@ class _ConversationSettingsState extends State { widget.conversation.admin ? const SizedBox(height: 25) : const SizedBox.shrink(), - sectionTitle('Members', showUsersAdd: true), + sectionTitle('Members', showUsersAdd: widget.conversation.admin && !widget.conversation.twoUser), usersList(), const SizedBox(height: 25), myAccess(), @@ -88,7 +88,7 @@ class _ConversationSettingsState extends State { fontWeight: FontWeight.w500, ), ), - widget.conversation.admin ? IconButton( + widget.conversation.admin && !widget.conversation.twoUser ? IconButton( iconSize: 20, icon: const Icon(Icons.edit), padding: const EdgeInsets.all(5.0), diff --git a/mobile/lib/views/main/conversation/settings_user_list_item.dart b/mobile/lib/views/main/conversation/settings_user_list_item.dart index 9657bab..a527e23 100644 --- a/mobile/lib/views/main/conversation/settings_user_list_item.dart +++ b/mobile/lib/views/main/conversation/settings_user_list_item.dart @@ -42,7 +42,7 @@ class _ConversationSettingsUserListItemState extends State { Widget build(BuildContext context) { return GestureDetector( behavior: HitTestBehavior.opaque, - onTap: findOrCreateConversation, + onTap: () { findOrCreateConversation(context); }, child: Container( padding: const EdgeInsets.only(left: 16,right: 16,top: 0,bottom: 20), child: Row( @@ -58,7 +59,7 @@ class _FriendListItemState extends State { ); } - Future findOrCreateConversation() async { + Future findOrCreateConversation(BuildContext context) async { Conversation? conversation = await getTwoUserConversation(widget.friend.friendId); conversation ??= await createConversation( @@ -67,6 +68,8 @@ class _FriendListItemState extends State { true, ); + uploadConversation(conversation, context); + Navigator.push(context, MaterialPageRoute(builder: (context){ return ConversationDetail( conversation: conversation!, diff --git a/mobile/lib/views/main/home.dart b/mobile/lib/views/main/home.dart index e740db0..f6bfb92 100644 --- a/mobile/lib/views/main/home.dart +++ b/mobile/lib/views/main/home.dart @@ -59,15 +59,15 @@ class _HomeState extends State { items: const [ BottomNavigationBarItem( icon: Icon(Icons.message), - label: "Chats", + label: 'Chats', ), BottomNavigationBarItem( icon: Icon(Icons.group_work), - label: "Friends", + label: 'Friends', ), BottomNavigationBarItem( icon: Icon(Icons.account_box), - label: "Profile", + label: 'Profile', ), ], ), @@ -166,7 +166,7 @@ class _HomeState extends State { FriendList( friends: friends, friendRequests: friendRequests, - callback: initFriends, + callback: reinitDatabaseRecords, ), Profile(profile: profile), ]; @@ -174,12 +174,32 @@ class _HomeState extends State { }); } - Future initFriends() async { + Future reinitDatabaseRecords() async { + + conversations = await getConversations(); friends = await getFriends(accepted: true); friendRequests = await getFriends(accepted: false); + profile = await MyProfile.getProfile(); + + setState(() { + _widgetOptions = [ + ConversationList( + conversations: conversations, + friends: friends, + ), + FriendList( + friends: friends, + friendRequests: friendRequests, + callback: reinitDatabaseRecords, + ), + Profile(profile: profile), + ]; + isLoading = false; + }); } - void _onItemTapped(int index) { + void _onItemTapped(int index) async { + await reinitDatabaseRecords(); setState(() { _selectedIndex = index; });