mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-24 12:43:53 +00:00
invitation to public group in group
This commit is contained in:
1 parent
d070a75f11
commit
1df5b548a1
5 files changed
+61
-28
No files matched your search
@@ -519,7 +519,7 @@ data ChatCommand
|
||||
| APIGetGroupRelays {groupId :: GroupId}
|
||||
| NewPublicGroup IncognitoEnabled (NonEmpty Int64) GroupProfile
|
||||
| AddMember GroupName ContactName GroupMemberRole
|
||||
| SharePublicGroup GroupName ContactName
|
||||
| SharePublicGroup GroupName ChatName
|
||||
| JoinGroup {groupName :: GroupName, enableNtfs :: MsgFilter}
|
||||
| AcceptMember GroupName ContactName GroupMemberRole
|
||||
| MemberRole GroupName ContactName GroupMemberRole
|
||||
@@ -880,6 +880,7 @@ data ChatEvent
|
||||
| CEvtHostConnected {protocol :: AProtocolType, transportHost :: TransportHost}
|
||||
| CEvtHostDisconnected {protocol :: AProtocolType, transportHost :: TransportHost}
|
||||
| CEvtReceivedGroupInvitation {user :: User, groupInfo :: GroupInfo, contact :: Contact, fromMemberRole :: GroupMemberRole, memberRole :: GroupMemberRole}
|
||||
| CEvtReceivedPublicGroupInvitation {user :: User, sharedGroupInfo :: GroupInfo, contact_ :: Maybe Contact, fromGroupInfo_ :: Maybe GroupInfo, fromMember_ :: Maybe GroupMember}
|
||||
| CEvtUserJoinedGroup {user :: User, groupInfo :: GroupInfo, hostMember :: GroupMember}
|
||||
| CEvtGroupLinkDataUpdated {user :: User, groupInfo :: GroupInfo, groupLink :: GroupLink, groupRelays :: [GroupRelay], relaysChanged :: Bool}
|
||||
| CEvtGroupRelayUpdated {user :: User, groupInfo :: GroupInfo, member :: GroupMember, groupRelay :: GroupRelay}
|
||||
|
||||
@@ -2472,11 +2472,16 @@ processChatCommand vr nm = \case
|
||||
pure (gInfo, relays)
|
||||
pure $ CRGroupRelays user gInfo relays
|
||||
APISharePublicGroup groupId toChatRef -> withUser $ \user -> do
|
||||
gInfo@GroupInfo {groupProfile, membership, groupSummary, localDisplayName = gName} <- withFastStore $ \db -> getGroupInfo db vr user groupId
|
||||
gInfo@GroupInfo {groupProfile, membership = GroupMember {memberId, groupMemberId}, groupSummary, localDisplayName} <- withFastStore $ \db -> getGroupInfo db vr user groupId
|
||||
unless (useRelays' gInfo) $ throwCmdError "not a public group"
|
||||
let groupSize = fromIntegral $ currentMembers groupSummary
|
||||
GroupMember {memberId = mId, groupMemberId = mGroupMemberId} = membership
|
||||
ciGroupInv oss = CIGroupInvitation {groupId, groupMemberId = mGroupMemberId, localDisplayName = gName, groupProfile, status = CIGISPending, ownerSigStatus = oss}
|
||||
sendInv :: forall c. ChatTypeI c => Maybe GroupOwnerSig -> SndMessage -> ChatDirection c 'MDSnd -> ChatInfo c -> CM ChatResponse
|
||||
sendInv groupOwner msg cd cInfo = do
|
||||
let oss = if isJust groupOwner then Just OSSVerified else Nothing
|
||||
ciContent = CISndGroupInvitation (CIGroupInvitation {groupId, groupMemberId, localDisplayName, groupProfile, status = CIGISPending, ownerSigStatus = oss}) GRObserver
|
||||
ci <- saveSndChatItem' user cd msg ciContent Nothing Nothing Nothing Nothing False
|
||||
toView $ CEvtNewChatItems user [AChatItem chatTypeI SMDSnd cInfo ci]
|
||||
pure $ CRSentPublicGroupInvitation user gInfo
|
||||
case toChatRef of
|
||||
ChatRef CTDirect contactId _ -> do
|
||||
ct@Contact {activeConn = Just ctConn} <- withFastStore $ \db -> getContact db vr user contactId
|
||||
@@ -2484,13 +2489,10 @@ processChatCommand vr nm = \case
|
||||
adHash <- withAgent $ \a -> getConnectionRatchetAdHash a (aConnId ctConn)
|
||||
let invBody = encodeUtf8 $ encodeJSON groupProfile
|
||||
sig = C.sign' memberPrivKey $ smpEncode CBDirect <> adHash <> invBody
|
||||
pure GroupOwnerSig {memberId = mId, binding = B64UrlByteString adHash, ownerSig = B64UrlByteString $ C.signatureBytes sig}
|
||||
pure GroupOwnerSig {memberId, binding = B64UrlByteString adHash, ownerSig = B64UrlByteString $ C.signatureBytes sig}
|
||||
let inv = PublicGroupInvitation {groupProfile, groupOwner, groupSize}
|
||||
oss = if isJust groupOwner then Just OSSVerified else Nothing
|
||||
(msg, _) <- sendDirectContactMessage user ct $ XGrpInvPub inv
|
||||
ci <- saveSndChatItem' user (CDDirectSnd ct) msg (CISndGroupInvitation (ciGroupInv oss) GRObserver) Nothing Nothing Nothing Nothing False
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct) ci]
|
||||
pure $ CRSentPublicGroupInvitation user gInfo
|
||||
sendInv groupOwner msg (CDDirectSnd ct) (DirectChat ct)
|
||||
ChatRef CTGroup toGroupId scope -> do
|
||||
(toGInfo, members) <- withFastStore $ \db -> do
|
||||
g <- getGroupInfo db vr user toGroupId
|
||||
@@ -2498,9 +2500,7 @@ processChatCommand vr nm = \case
|
||||
pure (g, ms)
|
||||
let inv = PublicGroupInvitation {groupProfile, groupOwner = Nothing, groupSize}
|
||||
msg <- sendGroupMessage user toGInfo scope members $ XGrpInvPub inv
|
||||
ci <- saveSndChatItem' user (CDGroupSnd toGInfo Nothing) msg (CISndGroupInvitation (ciGroupInv Nothing) GRObserver) Nothing Nothing Nothing Nothing False
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTGroup SMDSnd (GroupChat toGInfo Nothing) ci]
|
||||
pure $ CRSentPublicGroupInvitation user gInfo
|
||||
sendInv Nothing msg (CDGroupSnd toGInfo Nothing) (GroupChat toGInfo Nothing)
|
||||
_ -> throwCmdError "unsupported chat type"
|
||||
APIAddMember groupId contactId memRole -> withUser $ \user -> withGroupLock "addMember" groupId $ do
|
||||
-- TODO for large groups: no need to load all members to determine if contact is a member
|
||||
@@ -2889,9 +2889,13 @@ processChatCommand vr nm = \case
|
||||
AddMember gName cName memRole -> withUser $ \user -> do
|
||||
(groupId, contactId) <- withFastStore $ \db -> (,) <$> getGroupIdByName db user gName <*> getContactIdByName db user cName
|
||||
processChatCommand vr nm $ APIAddMember groupId contactId memRole
|
||||
SharePublicGroup gName cName -> withUser $ \user -> do
|
||||
(groupId, contactId) <- withFastStore $ \db -> (,) <$> getGroupIdByName db user gName <*> getContactIdByName db user cName
|
||||
processChatCommand vr nm $ APISharePublicGroup groupId (ChatRef CTDirect contactId Nothing)
|
||||
SharePublicGroup gName (ChatName cType toName) -> withUser $ \user -> do
|
||||
groupId <- withFastStore $ \db -> getGroupIdByName db user gName
|
||||
chatRef <- case cType of
|
||||
CTDirect -> ChatRef CTDirect <$> withFastStore (\db -> getContactIdByName db user toName) <*> pure Nothing
|
||||
CTGroup -> ChatRef CTGroup <$> withFastStore (\db -> getGroupIdByName db user toName) <*> pure Nothing
|
||||
_ -> throwCmdError "unsupported chat type"
|
||||
processChatCommand vr nm $ APISharePublicGroup groupId chatRef
|
||||
JoinGroup gName enableNtfs -> withUser $ \user -> do
|
||||
groupId <- withFastStore $ \db -> getGroupIdByName db user gName
|
||||
processChatCommand vr nm $ APIJoinGroup groupId enableNtfs
|
||||
@@ -4845,7 +4849,7 @@ chatCommandP =
|
||||
"/_ntf conn messages " *> (APIGetConnNtfMessages <$> connMsgsP),
|
||||
"/_add #" *> (APIAddMember <$> A.decimal <* A.space <*> A.decimal <*> memberRole),
|
||||
"/_share #" *> (APISharePublicGroup <$> A.decimal <* A.space <*> chatRefP),
|
||||
"/share " *> char_ '#' *> (SharePublicGroup <$> displayNameP <* A.space <* char_ '@' <*> displayNameP),
|
||||
"/share " *> char_ '#' *> (SharePublicGroup <$> displayNameP <* A.space <*> chatNameP),
|
||||
"/_join #" *> (APIJoinGroup <$> A.decimal <*> pure MFAll), -- needs to be changed to support in UI
|
||||
"/_accept member #" *> (APIAcceptMember <$> A.decimal <* A.space <*> A.decimal <*> memberRole),
|
||||
"/_delete member chat #" *> (APIDeleteMemberSupportChat <$> A.decimal <* A.space <*> A.decimal),
|
||||
|
||||
@@ -1029,6 +1029,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
XInfoProbe probe -> Nothing <$ xInfoProbe (COMGroupMember m'') probe
|
||||
XInfoProbeCheck probeHash -> Nothing <$ xInfoProbeCheck (COMGroupMember m'') probeHash
|
||||
XInfoProbeOk probe -> Nothing <$ xInfoProbeOk (COMGroupMember m'') probe
|
||||
XGrpInvPub pubGrpInv -> Nothing <$ processPublicGroupInvitationGroup gInfo' m'' conn' pubGrpInv msg brokerTs
|
||||
BFileChunk sharedMsgId chunk -> Nothing <$ bFileChunkGroup gInfo' sharedMsgId chunk msgMeta
|
||||
_ -> Nothing <$ messageError ("unsupported message: " <> tshow event)
|
||||
forM deliveryTaskContext_ $ \taskContext ->
|
||||
@@ -2317,6 +2318,11 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
ft <- withStore $ \db -> getDirectFileIdBySharedMsgId db user ct sharedMsgId >>= getRcvFileTransfer db user
|
||||
receiveInlineChunk ft chunk meta
|
||||
|
||||
processPublicGroupInvitationGroup :: GroupInfo -> GroupMember -> Connection -> PublicGroupInvitation -> RcvMessage -> UTCTime -> CM ()
|
||||
processPublicGroupInvitationGroup g m conn inv msg brokerTs = do
|
||||
(gInfo, _) <- rcvPublicGroupInvitation conn inv msg brokerTs (CDGroupRcv g Nothing m) (GroupChat g Nothing)
|
||||
toView $ CEvtReceivedPublicGroupInvitation {user, sharedGroupInfo = gInfo, contact_ = Nothing, fromGroupInfo_ = Just g, fromMember_ = Just m}
|
||||
|
||||
bFileChunkGroup :: GroupInfo -> SharedMsgId -> FileChunk -> MsgMeta -> CM ()
|
||||
bFileChunkGroup GroupInfo {groupId} sharedMsgId chunk meta = do
|
||||
ft <- withStore $ \db -> getGroupFileIdBySharedMsgId db userId groupId sharedMsgId >>= getRcvFileTransfer db user
|
||||
@@ -2415,7 +2421,12 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
sameGroupLinkId _ _ = False
|
||||
|
||||
processPublicGroupInvitation :: Contact -> Connection -> PublicGroupInvitation -> RcvMessage -> MsgMeta -> CM ()
|
||||
processPublicGroupInvitation ct conn PublicGroupInvitation {groupProfile, groupOwner, groupSize} msg msgMeta = do
|
||||
processPublicGroupInvitation ct conn inv msg msgMeta = do
|
||||
(gInfo, _) <- rcvPublicGroupInvitation conn inv msg (metaBrokerTs msgMeta) (CDDirectRcv ct) (DirectChat ct)
|
||||
toView $ CEvtReceivedPublicGroupInvitation {user, sharedGroupInfo = gInfo, contact_ = Just ct, fromGroupInfo_ = Nothing, fromMember_ = Nothing}
|
||||
|
||||
rcvPublicGroupInvitation :: forall c. (ChatTypeI c, ChatTypeQuotable c) => Connection -> PublicGroupInvitation -> RcvMessage -> UTCTime -> ChatDirection c 'MDRcv -> ChatInfo c -> CM (GroupInfo, ChatItem c 'MDRcv)
|
||||
rcvPublicGroupInvitation conn PublicGroupInvitation {groupProfile, groupOwner, groupSize} msg brokerTs cd cInfo = do
|
||||
unless (isJust $ publicGroup groupProfile) $ messageError "x.grp.inv.pub: not a public group"
|
||||
let oss = case groupOwner of
|
||||
Nothing -> Nothing
|
||||
@@ -2424,21 +2435,17 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
prepLink = PreparedConnLink {connFullLink = Nothing, connShortLink = sLnk_}
|
||||
gVar <- asks random
|
||||
subRole <- asks $ channelSubscriberRole . config
|
||||
(gInfo@GroupInfo {groupId, localDisplayName = gName, membership}, _) <- withStore $ \db ->
|
||||
(gInfo@GroupInfo {groupId, localDisplayName, membership = GroupMember {groupMemberId}}, _) <- withStore $ \db ->
|
||||
createPreparedGroup db gVar vr user groupProfile False prepLink Nothing True subRole (Just $ fromIntegral groupSize)
|
||||
withStore' $ \db -> setGroupChatHidden db user groupId True
|
||||
let GroupMember {groupMemberId} = membership
|
||||
content = CIRcvGroupInvitation (CIGroupInvitation {groupId, groupMemberId, localDisplayName = gName, groupProfile, status = CIGISPending, ownerSigStatus = oss}) GRObserver
|
||||
(ci, cInfo) <- saveRcvChatItemNoParse user (CDDirectRcv ct) msg brokerTs content
|
||||
let content = CIRcvGroupInvitation (CIGroupInvitation {groupId, groupMemberId, localDisplayName, groupProfile, status = CIGISPending, ownerSigStatus = oss}) GRObserver
|
||||
(ci, _) <- saveRcvChatItemNoParse user cd msg brokerTs content
|
||||
withStore' $ \db -> setGroupInvitationChatItemId db user groupId (chatItemId' ci)
|
||||
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDRcv cInfo ci]
|
||||
toView $ CEvtReceivedGroupInvitation {user, groupInfo = gInfo, contact = ct, fromMemberRole = GROwner, memberRole = GRObserver}
|
||||
-- start async link resolution for signature verification
|
||||
toView $ CEvtNewChatItems user [AChatItem chatTypeI SMDRcv cInfo ci]
|
||||
forM_ groupOwner $ \_ ->
|
||||
forM_ sLnk_ $ \sLnk ->
|
||||
void $ getAgentConnShortLinkAsync user CFGetGroupDataInv (Just conn) sLnk
|
||||
where
|
||||
brokerTs = metaBrokerTs msgMeta
|
||||
pure (gInfo, ci)
|
||||
|
||||
processPublicGroupLinkData :: User -> Contact -> Connection -> FixedLinkData 'CMContact -> ConnLinkData 'CMContact -> CM ()
|
||||
processPublicGroupLinkData _user _ct _conn _fixedLinkData _cData = do
|
||||
|
||||
@@ -472,6 +472,11 @@ chatEventToView hu ChatConfig {logLevel, showReactions, showReceipts, testView}
|
||||
in ttyUser u [sShow connId <> ": END"]
|
||||
CEvtSubscriptionStatus srv status conns -> [plain $ subStatusStr status <> " " <> show (length conns) <> " connections on server " <> showSMPServer srv]
|
||||
CEvtReceivedGroupInvitation {user = u, groupInfo = g, contact = c, memberRole = r} -> ttyUser u $ viewReceivedGroupInvitation g c r
|
||||
CEvtReceivedPublicGroupInvitation {user = u, sharedGroupInfo = g, contact_ = ct_, fromGroupInfo_ = fromG_, fromMember_ = m_} ->
|
||||
ttyUser u $ case (ct_, fromG_, m_) of
|
||||
(Just ct, _, _) -> [ttyFullContact ct <> " shared public group " <> ttyGroup' g]
|
||||
(_, Just fromG, Just m) -> [ttyGroup' fromG <> ": " <> ttyMember m <> " shared public group " <> ttyGroup' g]
|
||||
_ -> ["received public group invitation " <> ttyGroup' g]
|
||||
CEvtUserJoinedGroup u g m -> ttyUser u $ viewUserJoinedGroup g m
|
||||
CEvtGroupLinkDataUpdated u g groupLink relays relaysChanged
|
||||
| relaysChanged -> ttyUser u $ viewGroupLinkRelaysUpdated g groupLink relays
|
||||
|
||||
@@ -16,6 +16,7 @@ chatRelayTests = do
|
||||
it "relay profile updated in address" testRelayProfileUpdateInAddress
|
||||
describe "public group invitations" $ do
|
||||
it "share public group in direct chat" testSharePublicGroupDirect
|
||||
it "share public group in group chat" testSharePublicGroupInGroup
|
||||
|
||||
testGetSetChatRelays :: HasCallStack => TestParams -> IO ()
|
||||
testGetSetChatRelays ps =
|
||||
@@ -180,8 +181,23 @@ testSharePublicGroupDirect ps =
|
||||
connectUsers alice cath
|
||||
alice ##> "/share #team @cath"
|
||||
alice <## "shared public group #team"
|
||||
cath <## "#team: alice invites you to join the group as observer"
|
||||
cath <## "use /j team to accept"
|
||||
cath <## "alice (Alice) shared public group #team"
|
||||
|
||||
testSharePublicGroupInGroup :: HasCallStack => TestParams -> IO ()
|
||||
testSharePublicGroupInGroup ps =
|
||||
withNewTestChat ps "alice" aliceProfile $ \alice ->
|
||||
withNewTestChatOpts ps relayTestOpts "bob" bobProfile $ \bob ->
|
||||
withNewTestChat ps "cath" cathProfile $ \cath -> do
|
||||
bob ##> "/ad"
|
||||
(bobSLink, _cLink) <- getContactLinks bob True
|
||||
alice ##> ("/relays name=bob_relay " <> bobSLink)
|
||||
alice <## "ok"
|
||||
createChannelWithRelay "team" alice bob
|
||||
-- create a regular group with alice and cath
|
||||
createGroup2 "friends" alice cath
|
||||
alice ##> "/share #team #friends"
|
||||
alice <## "shared public group #team"
|
||||
cath <## "#friends: alice shared public group #team"
|
||||
|
||||
-- Create a public group with relay=1, wait for relay to join
|
||||
createChannelWithRelay :: HasCallStack => String -> TestCC -> TestCC -> IO ()
|
||||
|
||||
Reference in new issue
Block a user