diff --git a/src/Simplex/Chat/Library/Internal.hs b/src/Simplex/Chat/Library/Internal.hs index f91ee19bce..4ff677f379 100644 --- a/src/Simplex/Chat/Library/Internal.hs +++ b/src/Simplex/Chat/Library/Internal.hs @@ -1195,6 +1195,9 @@ sendHistory user gInfo@GroupInfo {membership} m@GroupMember {activeConn = Just c where descrEvent_ :: Maybe (ChatMsgEvent 'Json) descrEvent_ + -- in channels sendHistory runs on the relay, which cannot author XMsgNew (GRRelay < GRObserver); + -- the welcome message reaches new members via the channel link data instead + | useRelays' gInfo = Nothing | m `supportsVersion` groupHistoryIncludeWelcomeVersion = do let GroupInfo {groupProfile = GroupProfile {description}} = gInfo fmap (\descr -> XMsgNew $ MCSimple $ extMsgContent (MCText descr) Nothing) description @@ -1299,7 +1302,8 @@ setGroupLinkData nm user gInfo gLink = do (conn, groupRelays) <- withFastStore $ \db -> (,) <$> getGroupLinkConnection db vr user gInfo <*> liftIO (getConnectedGroupRelays db gInfo) let (userLinkData, crClientData) = groupLinkData gInfo gLink groupRelays - sLnk <- shortenShortLink' . toShortGroupLink =<< withAgent (\a -> setConnShortLink a nm (aConnId conn) SCMContact userLinkData (Just crClientData)) + tagShortLink = if useRelays' gInfo then toShortChannelLink else toShortGroupLink + sLnk <- shortenShortLink' . tagShortLink =<< withAgent (\a -> setConnShortLink a nm (aConnId conn) SCMContact userLinkData (Just crClientData)) withFastStore' $ \db -> setGroupLinkShortLink db gLink sLnk setGroupLinkDataAsync :: User -> GroupInfo -> GroupLink -> CM () diff --git a/src/Simplex/Chat/Library/Subscriber.hs b/src/Simplex/Chat/Library/Subscriber.hs index a8e9bcfdf5..2676224631 100644 --- a/src/Simplex/Chat/Library/Subscriber.hs +++ b/src/Simplex/Chat/Library/Subscriber.hs @@ -3138,7 +3138,10 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = (ci, cInfo) <- saveRcvChatItemNoParse user cd msg brokerTs (CIRcvGroupEvent $ RGEGroupUpdated p') groupMsgToView cInfo ci createGroupFeatureChangedItems user cd CIRcvGroupFeature g g'' - void $ forkIO $ void $ setGroupLinkData' NRMBackground user g'' + -- in channels, link data is updated by the owner making the change in runUpdateGroupProfile; + -- other owners receiving the update do not refresh the same link + unless (useRelays' g'') $ + void $ forkIO $ void $ setGroupLinkData' NRMBackground user g'' Just _ -> updateGroupPrefs_ msgSigned g m $ fromMaybe defaultBusinessGroupPrefs $ groupPreferences p' pure $ Just DJSGroup {jobSpec = DJDeliveryJob {includePending = True}} diff --git a/tests/ChatTests/Groups.hs b/tests/ChatTests/Groups.hs index 3a75a00a20..aedbb5e1e9 100644 --- a/tests/ChatTests/Groups.hs +++ b/tests/ChatTests/Groups.hs @@ -251,6 +251,8 @@ chatGroupTests = do it "should share same incognito profile with all relays" testChannels2RelaysIncognito describe "channel operations" $ do it "should update channel profile (signed)" testChannelUpdateProfileSigned + it "should preserve working link after profile update" testChannelLinkAfterProfileUpdate + it "should preserve working link after welcome message update" testChannelLinkAfterWelcomeUpdate it "should update channel preferences (signed)" testChannelUpdatePrefsSigned it "should change member role (signed)" testChannelChangeRoleSigned it "should block member for all (signed)" testChannelBlockMemberSigned @@ -8537,7 +8539,7 @@ memberJoinChannel gName relays owners shortLink fullLink member = do ] ] <> [ do - relay <## (mFullName <> ": accepting request to join group #team...") + relay <## (mFullName <> ": accepting request to join group #" <> gName <> "...") relay <## ("#" <> gName <> ": " <> mName <> " joined the group") | relay <- relays ] @@ -8569,7 +8571,7 @@ memberJoinChannelIncognito gName relays owners shortLink fullLink member = do ] ] <> [ do - relay <## (memIncognito <> ": accepting request to join group #team...") + relay <## (memIncognito <> ": accepting request to join group #" <> gName <> "...") relay <## ("#" <> gName <> ": " <> memIncognito <> " joined the group") | relay <- relays ] @@ -8770,6 +8772,78 @@ testChannelUpdateProfileSigned ps = ] alice #$> ("/_get chat #1 count=1", chat, [(1, "group profile updated (signed)")]) +testChannelLinkAfterProfileUpdate :: HasCallStack => TestParams -> IO () +testChannelLinkAfterProfileUpdate ps = + withNewTestChat ps "alice" aliceProfile $ \alice -> + withNewTestChatOpts ps relayTestOpts "bob" bobProfile $ \bob -> + withNewTestChat ps "cath" cathProfile $ \cath -> + withNewTestChat ps "dan" danProfile $ \dan -> do + (shortLink, fullLink) <- prepareChannel1Relay "team" alice bob + memberJoinChannel "team" [bob] [alice] shortLink fullLink cath + + -- owner updates channel profile + alice ##> "/gp team my_team My team description" + alice <## "changed to #my_team (My team description)" + concurrentlyN_ + [ do + bob <## "alice updated group #team: (signed)" + bob <## "changed to #my_team (My team description)", + do + cath <## "alice updated group #team: (signed)" + cath <## "changed to #my_team (My team description)" + ] + alice #$> ("/_get chat #1 count=1", chat, [(1, "group profile updated (signed)")]) + + -- late subscriber joins via the same channel link after profile update + threadDelay 100000 + alice ##> "/show link #my_team" + (shortLink', fullLink') <- getGroupLinks alice "my_team" GRMember False + shortLink' `shouldBe` shortLink + fullLink' `shouldBe` fullLink + memberJoinChannel "my_team" [bob] [alice] shortLink' fullLink' dan + + alice #> "#my_team hi" + bob <# "#my_team> hi" + [cath, dan] *<# "#my_team> hi [>>]" + +testChannelLinkAfterWelcomeUpdate :: HasCallStack => TestParams -> IO () +testChannelLinkAfterWelcomeUpdate ps = + withNewTestChat ps "alice" aliceProfile $ \alice -> + withNewTestChatOpts ps relayTestOpts "bob" bobProfile $ \bob -> + withNewTestChat ps "cath" cathProfile $ \cath -> + withNewTestChat ps "dan" danProfile $ \dan -> do + (shortLink, fullLink) <- prepareChannel1Relay "team" alice bob + memberJoinChannel "team" [bob] [alice] shortLink fullLink cath + + -- owner updates channel welcome message + alice ##> "/set welcome #team welcome to team" + alice <## "welcome message changed to:" + alice <## "welcome to team" + concurrentlyN_ + [ do + bob <## "alice updated group #team: (signed)" + bob <## "welcome message changed to:" + bob <## "welcome to team", + do + cath <## "alice updated group #team: (signed)" + cath <## "welcome message changed to:" + cath <## "welcome to team" + ] + alice #$> ("/_get chat #1 count=1", chat, [(1, "group profile updated (signed)")]) + + -- re-fetch updated link, late subscriber joins + threadDelay 100000 + alice ##> "/show link #team" + (shortLink', fullLink') <- getGroupLinks alice "team" GRMember False + shortLink' `shouldBe` shortLink + fullLink' `shouldBe` fullLink + memberJoinChannel "team" [bob] [alice] shortLink' fullLink' dan + dan #$> ("/_get chat #1 count=100", chat, groupFeaturesNoE2E <> [(0, "welcome to team"), (0, e2eeInfoNoPQStr), (0, "connected")]) + + alice #> "#team hi" + bob <# "#team> hi" + [cath, dan] *<# "#team> hi [>>]" + testChannelUpdatePrefsSigned :: HasCallStack => TestParams -> IO () testChannelUpdatePrefsSigned ps = withNewTestChat ps "alice" aliceProfile $ \alice ->