From 999923bcf9b8c0d8135c3be50e7f7e9170f63678 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 31 Jul 2022 18:54:49 +0100 Subject: [PATCH] core: allow creating groups with the same display name; mobile: update group status when group deleted by another member or user removed (#859) --- .../java/chat/simplex/app/model/SimpleXAPI.kt | 4 ++++ .../app/views/chatlist/ChatPreviewView.kt | 6 +++--- apps/ios/Shared/Model/SimpleXAPI.swift | 4 ++++ .../xcschemes/SimpleX (iOS).xcscheme | 1 - src/Simplex/Chat/Store.hs | 19 +++++++------------ src/Simplex/Chat/View.hs | 9 ++++++++- tests/ChatTests.hs | 12 ++++++++++++ 7 files changed, 38 insertions(+), 17 deletions(-) diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt index 64c8ab8bb1..519451a65b 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt @@ -692,6 +692,10 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager } is CR.UserJoinedGroup -> chatModel.updateGroup(r.groupInfo) + is CR.GroupDeleted -> + chatModel.updateGroup(r.groupInfo) + is CR.DeletedMemberUser -> + chatModel.updateGroup(r.groupInfo) is CR.RcvFileStart -> chatItemSimpleUpdate(r.chatItem) is CR.RcvFileComplete -> diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt index cc0d81e366..ec5a2d94b2 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt @@ -7,7 +7,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.* import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.RemoveCircle +import androidx.compose.material.icons.filled.Cancel import androidx.compose.material.icons.outlined.ErrorOutline import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -33,10 +33,10 @@ fun ChatPreviewView(chat: Chat, stopped: Boolean) { @Composable fun groupInactiveIcon() { Icon( - Icons.Filled.RemoveCircle, + Icons.Filled.Cancel, stringResource(R.string.icon_descr_group_inactive), Modifier.size(18.dp).background(MaterialTheme.colors.background, CircleShape), - tint = Color.Red + tint = HighOrLowlight ) } diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 255f37b5c4..c016084119 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -818,6 +818,10 @@ func processReceivedMsg(_ res: ChatResponse) async { // NtfManager.shared.notifyContactRequest(contactRequest) // TODO notifyGroupInvitation? case let .userJoinedGroup(groupInfo): m.updateGroup(groupInfo) + case let .groupDeleted(groupInfo, _): + m.updateGroup(groupInfo) + case let .deletedMemberUser(groupInfo, _): + m.updateGroup(groupInfo) case let .rcvFileStart(aChatItem): chatItemSimpleUpdate(aChatItem) case let .rcvFileComplete(aChatItem): diff --git a/apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX (iOS).xcscheme b/apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX (iOS).xcscheme index 8b60dc4fae..6a1d4192e6 100644 --- a/apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX (iOS).xcscheme +++ b/apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX (iOS).xcscheme @@ -44,7 +44,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - enableAddressSanitizer = "YES" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 4fb51dc73a..31cf52c72b 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -1278,28 +1278,23 @@ updateConnectionStatus db Connection {connId} connStatus = do -- | creates completely new group with a single member - the current user createNewGroup :: DB.Connection -> TVar ChaChaDRG -> User -> GroupProfile -> ExceptT StoreError IO GroupInfo -createNewGroup db gVar user groupProfile = - checkConstraint SEDuplicateName . liftIO $ do - let GroupProfile {displayName, fullName, image} = groupProfile - uId = userId user - currentTs <- getCurrentTime - DB.execute - db - "INSERT INTO display_names (local_display_name, ldn_base, user_id, created_at, updated_at) VALUES (?,?,?,?,?)" - (displayName, displayName, uId, currentTs, currentTs) +createNewGroup db gVar user@User {userId} groupProfile = ExceptT $ do + let GroupProfile {displayName, fullName, image} = groupProfile + currentTs <- getCurrentTime + withLocalDisplayName db userId displayName $ \ldn -> do DB.execute db "INSERT INTO group_profiles (display_name, full_name, image, user_id, created_at, updated_at) VALUES (?,?,?,?,?,?)" - (displayName, fullName, image, uId, currentTs, currentTs) + (displayName, fullName, image, userId, currentTs, currentTs) profileId <- insertedRowId db DB.execute db "INSERT INTO groups (local_display_name, user_id, group_profile_id, created_at, updated_at) VALUES (?,?,?,?,?)" - (displayName, uId, profileId, currentTs, currentTs) + (ldn, userId, profileId, currentTs, currentTs) groupId <- insertedRowId db memberId <- encodedRandomBytes gVar 12 membership <- createContactMember_ db user groupId user (MemberIdRole (MemberId memberId) GROwner) GCUserMember GSMemCreator IBUser currentTs - pure GroupInfo {groupId, localDisplayName = displayName, groupProfile, membership, createdAt = currentTs, updatedAt = currentTs} + pure GroupInfo {groupId, localDisplayName = ldn, groupProfile, membership, createdAt = currentTs, updatedAt = currentTs} -- | creates a new group record for the group the current user was invited to, or returns an existing one createGroupInvitation :: DB.Connection -> User -> Contact -> GroupInvitation -> ExceptT StoreError IO GroupInfo diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index d6e62a39a9..5bdd11506b 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -450,7 +450,14 @@ viewGroupsList gs = map groupSS $ sortOn ldn_ gs groupSS GroupInfo {localDisplayName = ldn, groupProfile = GroupProfile {fullName}, membership} = case memberStatus membership of GSMemInvited -> groupInvitation' ldn fullName - _ -> ttyGroup ldn <> optFullName ldn fullName + s -> ttyGroup ldn <> optFullName ldn fullName <> viewMemberStatus s + where + viewMemberStatus = \case + GSMemRemoved -> delete "you are removed" + GSMemLeft -> delete "you left" + GSMemGroupDeleted -> delete "group deleted" + _ -> "" + delete reason = " (" <> reason <> ", delete local copy: " <> highlight ("/d #" <> ldn) <> ")" groupInvitation' :: GroupName -> Text -> StyledString groupInvitation' displayName fullName = diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index 952f399fac..e79bdefc0b 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -49,6 +49,7 @@ chatTests = do it "add contacts, create group and send/receive messages, check messages" testGroupCheckMessages it "create and join group with 4 members" testGroup2 it "create and delete group" testGroupDelete + it "create group with the same displayName" testGroupSameName it "invitee delete group when in status invited" testGroupDeleteWhenInvited it "re-add member in status invited" testGroupReAddInvited it "remove contact from group and add again" testGroupRemoveAdd @@ -692,6 +693,17 @@ testGroupDelete = cath ##> "/d #team" cath <## "#team: you deleted the group" +testGroupSameName :: IO () +testGroupSameName = + testChat2 aliceProfile bobProfile $ + \alice _ -> do + alice ##> "/g team" + alice <## "group #team is created" + alice <## "use /a team to add members" + alice ##> "/g team" + alice <## "group #team_1 (team) is created" + alice <## "use /a team_1 to add members" + testGroupDeleteWhenInvited :: IO () testGroupDeleteWhenInvited = testChat2 aliceProfile bobProfile $