core, ui: create dummy member record when admin forwards a message from an unknown member (#3651)
* core: create dummy member record when admin forwards a message from an unknown member * comments * update unknown member if announced * change removed * change unknown name, revert diff * revert diff * ios * update ios library * android * remove changes in iOS project file * rename event * remove unknown category * android --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
parent
d9d270f00e
commit
bfe5d51df7
@ -1822,9 +1822,16 @@ public struct GroupMember: Identifiable, Decodable {
|
||||
public var chatViewName: String {
|
||||
get {
|
||||
let p = memberProfile
|
||||
return p.localAlias == ""
|
||||
let name = (
|
||||
p.localAlias == ""
|
||||
? p.displayName + (p.fullName == "" || p.fullName == p.displayName ? "" : " / \(p.fullName)")
|
||||
: p.localAlias
|
||||
)
|
||||
return (
|
||||
memberStatus == .memUnknown
|
||||
? String.localizedStringWithFormat(NSLocalizedString("_Previous member_ %@", comment: "previous/unknown group member"), name)
|
||||
: name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1833,6 +1840,7 @@ public struct GroupMember: Identifiable, Decodable {
|
||||
case .memRemoved: return false
|
||||
case .memLeft: return false
|
||||
case .memGroupDeleted: return false
|
||||
case .memUnknown: return false
|
||||
case .memInvited: return false
|
||||
case .memIntroduced: return false
|
||||
case .memIntroInvited: return false
|
||||
@ -1849,6 +1857,7 @@ public struct GroupMember: Identifiable, Decodable {
|
||||
case .memRemoved: return false
|
||||
case .memLeft: return false
|
||||
case .memGroupDeleted: return false
|
||||
case .memUnknown: return false
|
||||
case .memInvited: return false
|
||||
case .memIntroduced: return true
|
||||
case .memIntroInvited: return true
|
||||
@ -1953,6 +1962,7 @@ public enum GroupMemberStatus: String, Decodable {
|
||||
case memRemoved = "removed"
|
||||
case memLeft = "left"
|
||||
case memGroupDeleted = "deleted"
|
||||
case memUnknown = "unknown"
|
||||
case memInvited = "invited"
|
||||
case memIntroduced = "introduced"
|
||||
case memIntroInvited = "intro-inv"
|
||||
@ -1967,6 +1977,7 @@ public enum GroupMemberStatus: String, Decodable {
|
||||
case .memRemoved: return "removed"
|
||||
case .memLeft: return "left"
|
||||
case .memGroupDeleted: return "group deleted"
|
||||
case .memUnknown: return "unknown status"
|
||||
case .memInvited: return "invited"
|
||||
case .memIntroduced: return "connecting (introduced)"
|
||||
case .memIntroInvited: return "connecting (introduction invitation)"
|
||||
@ -1983,6 +1994,7 @@ public enum GroupMemberStatus: String, Decodable {
|
||||
case .memRemoved: return "removed"
|
||||
case .memLeft: return "left"
|
||||
case .memGroupDeleted: return "group deleted"
|
||||
case .memUnknown: return "unknown"
|
||||
case .memInvited: return "invited"
|
||||
case .memIntroduced: return "connecting"
|
||||
case .memIntroInvited: return "connecting"
|
||||
|
@ -1268,12 +1268,19 @@ data class GroupMember (
|
||||
val verified get() = activeConn?.connectionCode != null
|
||||
|
||||
val chatViewName: String
|
||||
get() = memberProfile.localAlias.ifEmpty { displayName + (if (fullName == "" || fullName == displayName) "" else " / $fullName") }
|
||||
get() {
|
||||
val name = memberProfile.localAlias.ifEmpty { displayName + (if (fullName == "" || fullName == displayName) "" else " / $fullName") }
|
||||
return if (memberStatus == GroupMemberStatus.MemUnknown)
|
||||
String.format(generalGetString(MR.strings.previous_member_vName), name)
|
||||
else
|
||||
name
|
||||
}
|
||||
|
||||
val memberActive: Boolean get() = when (this.memberStatus) {
|
||||
GroupMemberStatus.MemRemoved -> false
|
||||
GroupMemberStatus.MemLeft -> false
|
||||
GroupMemberStatus.MemGroupDeleted -> false
|
||||
GroupMemberStatus.MemUnknown -> false
|
||||
GroupMemberStatus.MemInvited -> false
|
||||
GroupMemberStatus.MemIntroduced -> false
|
||||
GroupMemberStatus.MemIntroInvited -> false
|
||||
@ -1288,6 +1295,7 @@ data class GroupMember (
|
||||
GroupMemberStatus.MemRemoved -> false
|
||||
GroupMemberStatus.MemLeft -> false
|
||||
GroupMemberStatus.MemGroupDeleted -> false
|
||||
GroupMemberStatus.MemUnknown -> false
|
||||
GroupMemberStatus.MemInvited -> false
|
||||
GroupMemberStatus.MemIntroduced -> true
|
||||
GroupMemberStatus.MemIntroInvited -> true
|
||||
@ -1377,6 +1385,7 @@ enum class GroupMemberStatus {
|
||||
@SerialName("removed") MemRemoved,
|
||||
@SerialName("left") MemLeft,
|
||||
@SerialName("deleted") MemGroupDeleted,
|
||||
@SerialName("unknown") MemUnknown,
|
||||
@SerialName("invited") MemInvited,
|
||||
@SerialName("introduced") MemIntroduced,
|
||||
@SerialName("intro-inv") MemIntroInvited,
|
||||
@ -1390,6 +1399,7 @@ enum class GroupMemberStatus {
|
||||
MemRemoved -> generalGetString(MR.strings.group_member_status_removed)
|
||||
MemLeft -> generalGetString(MR.strings.group_member_status_left)
|
||||
MemGroupDeleted -> generalGetString(MR.strings.group_member_status_group_deleted)
|
||||
MemUnknown -> generalGetString(MR.strings.group_member_status_unknown)
|
||||
MemInvited -> generalGetString(MR.strings.group_member_status_invited)
|
||||
MemIntroduced -> generalGetString(MR.strings.group_member_status_introduced)
|
||||
MemIntroInvited -> generalGetString(MR.strings.group_member_status_intro_invitation)
|
||||
@ -1404,6 +1414,7 @@ enum class GroupMemberStatus {
|
||||
MemRemoved -> generalGetString(MR.strings.group_member_status_removed)
|
||||
MemLeft -> generalGetString(MR.strings.group_member_status_left)
|
||||
MemGroupDeleted -> generalGetString(MR.strings.group_member_status_group_deleted)
|
||||
MemUnknown -> generalGetString(MR.strings.group_member_status_unknown_short)
|
||||
MemInvited -> generalGetString(MR.strings.group_member_status_invited)
|
||||
MemIntroduced -> generalGetString(MR.strings.group_member_status_connecting)
|
||||
MemIntroInvited -> generalGetString(MR.strings.group_member_status_connecting)
|
||||
|
@ -1217,6 +1217,7 @@
|
||||
<string name="group_member_status_removed">removed</string>
|
||||
<string name="group_member_status_left">left</string>
|
||||
<string name="group_member_status_group_deleted">group deleted</string>
|
||||
<string name="group_member_status_unknown">unknown status</string>
|
||||
<string name="group_member_status_invited">invited</string>
|
||||
<string name="group_member_status_introduced">connecting (introduced)</string>
|
||||
<string name="group_member_status_intro_invitation">connecting (introduction invitation)</string>
|
||||
@ -1227,6 +1228,9 @@
|
||||
<string name="group_member_status_creator">creator</string>
|
||||
|
||||
<string name="group_member_status_connecting">connecting</string>
|
||||
<string name="group_member_status_unknown_short">unknown</string>
|
||||
|
||||
<string name="previous_member_vName"><![CDATA[<i>Previous member</i> %1$s]]></string>
|
||||
|
||||
<!-- AddGroupMembersView.kt -->
|
||||
<string name="no_contacts_to_add">No contacts to add</string>
|
||||
|
@ -5100,16 +5100,24 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
_ -> pure conn'
|
||||
|
||||
xGrpMemNew :: GroupInfo -> GroupMember -> MemberInfo -> RcvMessage -> UTCTime -> m ()
|
||||
xGrpMemNew gInfo m memInfo@(MemberInfo memId memRole _ memberProfile) msg brokerTs = do
|
||||
xGrpMemNew gInfo m memInfo@(MemberInfo memId memRole _ _) msg brokerTs = do
|
||||
checkHostRole m memRole
|
||||
unless (sameMemberId memId $ membership gInfo) $
|
||||
withStore' (\db -> runExceptT $ getGroupMemberByMemberId db user gInfo memId) >>= \case
|
||||
Right unknownMember@GroupMember {memberStatus = GSMemUnknown} -> do
|
||||
updatedMember <- withStore $ \db -> updateUnknownMemberAnnounced db user m unknownMember memInfo
|
||||
toView $ CRUnknownMemberAnnounced user gInfo m unknownMember updatedMember
|
||||
memberAnnouncedToView updatedMember
|
||||
Right _ -> messageError "x.grp.mem.new error: member already exists"
|
||||
Left _ -> do
|
||||
newMember@GroupMember {groupMemberId} <- withStore $ \db -> createNewGroupMember db user gInfo m memInfo GCPostMember GSMemAnnounced
|
||||
ci <- saveRcvChatItem user (CDGroupRcv gInfo m) msg brokerTs (CIRcvGroupEvent $ RGEMemberAdded groupMemberId memberProfile)
|
||||
newMember <- withStore $ \db -> createNewGroupMember db user gInfo m memInfo GCPostMember GSMemAnnounced
|
||||
memberAnnouncedToView newMember
|
||||
where
|
||||
memberAnnouncedToView announcedMember@GroupMember {groupMemberId, memberProfile} = do
|
||||
let event = RGEMemberAdded groupMemberId (fromLocalProfile memberProfile)
|
||||
ci <- saveRcvChatItem user (CDGroupRcv gInfo m) msg brokerTs (CIRcvGroupEvent event)
|
||||
groupMsgToView gInfo ci
|
||||
toView $ CRJoinedGroupMemberConnecting user gInfo m newMember
|
||||
toView $ CRJoinedGroupMemberConnecting user gInfo m announcedMember
|
||||
|
||||
xGrpMemIntro :: GroupInfo -> GroupMember -> MemberInfo -> m ()
|
||||
xGrpMemIntro gInfo@GroupInfo {chatSettings} m@GroupMember {memberRole, localDisplayName = c} memInfo@(MemberInfo memId _ memChatVRange _) = do
|
||||
@ -5355,8 +5363,14 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
xGrpMsgForward :: GroupInfo -> GroupMember -> MemberId -> ChatMessage 'Json -> UTCTime -> m ()
|
||||
xGrpMsgForward gInfo@GroupInfo {groupId} m@GroupMember {memberRole, localDisplayName} memberId msg msgTs = do
|
||||
when (memberRole < GRAdmin) $ throwChatError (CEGroupContactRole localDisplayName)
|
||||
author <- withStore $ \db -> getGroupMemberByMemberId db user gInfo memberId
|
||||
processForwardedMsg author msg
|
||||
withStore' (\db -> runExceptT $ getGroupMemberByMemberId db user gInfo memberId) >>= \case
|
||||
Right author -> processForwardedMsg author msg
|
||||
Left (SEGroupMemberNotFoundByMemberId _) -> do
|
||||
let name = T.take 7 . safeDecodeUtf8 . B64.encode . unMemberId $ memberId
|
||||
unknownAuthor <- withStore $ \db -> createNewUnknownGroupMember db vr user gInfo memberId name
|
||||
toView $ CRUnknownMemberCreated user gInfo m unknownAuthor
|
||||
processForwardedMsg unknownAuthor msg
|
||||
Left e -> throwError $ ChatErrorStore e
|
||||
where
|
||||
-- Note: forwarded group events (see forwardedGroupMsg) should include msgId to be deduplicated
|
||||
processForwardedMsg :: GroupMember -> ChatMessage 'Json -> m ()
|
||||
|
@ -630,6 +630,8 @@ data ChatResponse
|
||||
| CRDeletedMember {user :: User, groupInfo :: GroupInfo, byMember :: GroupMember, deletedMember :: GroupMember}
|
||||
| CRDeletedMemberUser {user :: User, groupInfo :: GroupInfo, member :: GroupMember}
|
||||
| CRLeftMember {user :: User, groupInfo :: GroupInfo, member :: GroupMember}
|
||||
| CRUnknownMemberCreated {user :: User, groupInfo :: GroupInfo, forwardedByMember :: GroupMember, member :: GroupMember}
|
||||
| CRUnknownMemberAnnounced {user :: User, groupInfo :: GroupInfo, announcingMember :: GroupMember, unknownMember :: GroupMember, announcedMember :: GroupMember}
|
||||
| CRGroupEmpty {user :: User, groupInfo :: GroupInfo}
|
||||
| CRGroupRemoved {user :: User, groupInfo :: GroupInfo}
|
||||
| CRGroupDeleted {user :: User, groupInfo :: GroupInfo, member :: GroupMember}
|
||||
|
@ -110,6 +110,8 @@ module Simplex.Chat.Store.Groups
|
||||
updateMemberProfile,
|
||||
getXGrpLinkMemReceived,
|
||||
setXGrpLinkMemReceived,
|
||||
createNewUnknownGroupMember,
|
||||
updateUnknownMemberAnnounced,
|
||||
)
|
||||
where
|
||||
|
||||
@ -594,11 +596,9 @@ getGroupSummary db User {userId} groupId = do
|
||||
JOIN group_members m USING (group_id)
|
||||
WHERE g.user_id = ?
|
||||
AND g.group_id = ?
|
||||
AND m.member_status != ?
|
||||
AND m.member_status != ?
|
||||
AND m.member_status != ?
|
||||
AND m.member_status NOT IN (?,?,?,?)
|
||||
|]
|
||||
(userId, groupId, GSMemRemoved, GSMemLeft, GSMemInvited)
|
||||
(userId, groupId, GSMemRemoved, GSMemLeft, GSMemUnknown, GSMemInvited)
|
||||
pure GroupSummary {currentMembers = fromMaybe 0 currentMembers_}
|
||||
|
||||
getContactGroupPreferences :: DB.Connection -> User -> Contact -> IO [FullGroupPreferences]
|
||||
@ -682,13 +682,13 @@ getGroupMembersForExpiration db user@User {userId, userContactId} GroupInfo {gro
|
||||
( groupMemberQuery
|
||||
<> [sql|
|
||||
WHERE m.group_id = ? AND m.user_id = ? AND (m.contact_id IS NULL OR m.contact_id != ?)
|
||||
AND m.member_status IN (?, ?, ?)
|
||||
AND m.member_status IN (?, ?, ?, ?)
|
||||
AND m.group_member_id NOT IN (
|
||||
SELECT DISTINCT group_member_id FROM chat_items
|
||||
)
|
||||
|]
|
||||
)
|
||||
(userId, groupId, userId, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted)
|
||||
(userId, groupId, userId, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted, GSMemUnknown)
|
||||
|
||||
toContactMember :: User -> (GroupMemberRow :. MaybeConnectionRow) -> GroupMember
|
||||
toContactMember User {userContactId} (memberRow :. connRow) =
|
||||
@ -1339,10 +1339,10 @@ getGroupInfoByGroupLinkHash db vr user@User {userId, userContactId} (groupLinkHa
|
||||
FROM groups g
|
||||
JOIN group_members mu ON mu.group_id = g.group_id
|
||||
WHERE g.user_id = ? AND g.via_group_link_uri_hash IN (?,?)
|
||||
AND mu.contact_id = ? AND mu.member_status NOT IN (?,?,?)
|
||||
AND mu.contact_id = ? AND mu.member_status NOT IN (?,?,?,?)
|
||||
LIMIT 1
|
||||
|]
|
||||
(userId, groupLinkHash1, groupLinkHash2, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted)
|
||||
(userId, groupLinkHash1, groupLinkHash2, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted, GSMemUnknown)
|
||||
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getGroupInfo db vr user) groupId_
|
||||
|
||||
getGroupIdByName :: DB.Connection -> User -> GroupName -> ExceptT StoreError IO GroupId
|
||||
@ -1965,3 +1965,52 @@ setXGrpLinkMemReceived db mId xGrpLinkMemReceived = do
|
||||
db
|
||||
"UPDATE group_members SET xgrplinkmem_received = ?, updated_at = ? WHERE group_member_id = ?"
|
||||
(xGrpLinkMemReceived, currentTs, mId)
|
||||
|
||||
createNewUnknownGroupMember :: DB.Connection -> VersionRange -> User -> GroupInfo -> MemberId -> Text -> ExceptT StoreError IO GroupMember
|
||||
createNewUnknownGroupMember db vr user@User {userId, userContactId} GroupInfo {groupId} memberId memberName = do
|
||||
currentTs <- liftIO getCurrentTime
|
||||
let memberProfile = profileFromName memberName
|
||||
(localDisplayName, profileId) <- createNewMemberProfile_ db user memberProfile currentTs
|
||||
groupMemberId <- liftIO $ do
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
INSERT INTO group_members
|
||||
( group_id, member_id, member_role, member_category, member_status, invited_by,
|
||||
user_id, local_display_name, contact_id, contact_profile_id, created_at, updated_at,
|
||||
peer_chat_min_version, peer_chat_max_version)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (groupId, memberId, GRAuthor, GCPreMember, GSMemUnknown, fromInvitedBy userContactId IBUnknown)
|
||||
:. (userId, localDisplayName, Nothing :: (Maybe Int64), profileId, currentTs, currentTs)
|
||||
:. (minV, maxV)
|
||||
)
|
||||
insertedRowId db
|
||||
getGroupMemberById db user groupMemberId
|
||||
where
|
||||
VersionRange minV maxV = vr
|
||||
|
||||
updateUnknownMemberAnnounced :: DB.Connection -> User -> GroupMember -> GroupMember -> MemberInfo -> ExceptT StoreError IO GroupMember
|
||||
updateUnknownMemberAnnounced db user@User {userId} invitingMember unknownMember@GroupMember {groupMemberId, memberChatVRange} MemberInfo {memberRole, v, profile} = do
|
||||
_ <- updateMemberProfile db user unknownMember profile
|
||||
currentTs <- liftIO getCurrentTime
|
||||
liftIO $
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE group_members
|
||||
SET member_role = ?,
|
||||
member_category = ?,
|
||||
member_status = ?,
|
||||
invited_by_group_member_id = ?,
|
||||
peer_chat_min_version = ?,
|
||||
peer_chat_max_version = ?,
|
||||
updated_at = ?
|
||||
WHERE user_id = ? AND group_member_id = ?
|
||||
|]
|
||||
( (memberRole, GCPostMember, GSMemAnnounced, groupMemberId' invitingMember)
|
||||
:. (minV, maxV, currentTs, userId, groupMemberId)
|
||||
)
|
||||
getGroupMemberById db user groupMemberId
|
||||
where
|
||||
VersionRange minV maxV = maybe (fromJVersionRange memberChatVRange) fromChatVRange v
|
||||
|
@ -825,6 +825,7 @@ data GroupMemberStatus
|
||||
= GSMemRemoved -- member who was removed from the group
|
||||
| GSMemLeft -- member who left the group
|
||||
| GSMemGroupDeleted -- user member of the deleted group
|
||||
| GSMemUnknown -- unknown member, whose message was forwarded by an admin (likely member wasn't introduced due to not being a current member, but message was included in history)
|
||||
| GSMemInvited -- member is sent to or received invitation to join the group
|
||||
| GSMemIntroduced -- user received x.grp.mem.intro for this member (only with GCPreMember)
|
||||
| GSMemIntroInvited -- member is sent to or received from intro invitation
|
||||
@ -851,6 +852,7 @@ memberActive m = case memberStatus m of
|
||||
GSMemRemoved -> False
|
||||
GSMemLeft -> False
|
||||
GSMemGroupDeleted -> False
|
||||
GSMemUnknown -> False
|
||||
GSMemInvited -> False
|
||||
GSMemIntroduced -> False
|
||||
GSMemIntroInvited -> False
|
||||
@ -869,6 +871,7 @@ memberCurrent' = \case
|
||||
GSMemRemoved -> False
|
||||
GSMemLeft -> False
|
||||
GSMemGroupDeleted -> False
|
||||
GSMemUnknown -> False
|
||||
GSMemInvited -> False
|
||||
GSMemIntroduced -> True
|
||||
GSMemIntroInvited -> True
|
||||
@ -883,6 +886,7 @@ memberRemoved m = case memberStatus m of
|
||||
GSMemRemoved -> True
|
||||
GSMemLeft -> True
|
||||
GSMemGroupDeleted -> True
|
||||
GSMemUnknown -> False
|
||||
GSMemInvited -> False
|
||||
GSMemIntroduced -> False
|
||||
GSMemIntroInvited -> False
|
||||
@ -897,6 +901,7 @@ instance TextEncoding GroupMemberStatus where
|
||||
"removed" -> Just GSMemRemoved
|
||||
"left" -> Just GSMemLeft
|
||||
"deleted" -> Just GSMemGroupDeleted
|
||||
"unknown" -> Just GSMemUnknown
|
||||
"invited" -> Just GSMemInvited
|
||||
"introduced" -> Just GSMemIntroduced
|
||||
"intro-inv" -> Just GSMemIntroInvited
|
||||
@ -910,6 +915,7 @@ instance TextEncoding GroupMemberStatus where
|
||||
GSMemRemoved -> "removed"
|
||||
GSMemLeft -> "left"
|
||||
GSMemGroupDeleted -> "deleted"
|
||||
GSMemUnknown -> "unknown"
|
||||
GSMemInvited -> "invited"
|
||||
GSMemIntroduced -> "introduced"
|
||||
GSMemIntroInvited -> "intro-inv"
|
||||
|
@ -178,6 +178,8 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
CRGroupLinkConnecting u g _ -> ttyUser u [ttyGroup' g <> ": joining the group..."]
|
||||
CRUserDeletedMember u g m -> ttyUser u [ttyGroup' g <> ": you removed " <> ttyMember m <> " from the group"]
|
||||
CRLeftMemberUser u g -> ttyUser u $ [ttyGroup' g <> ": you left the group"] <> groupPreserved g
|
||||
CRUnknownMemberCreated u g fwdM um -> ttyUser u [ttyGroup' g <> ": " <> ttyMember fwdM <> " forwarded a message from an unknown member, creating unknown member record " <> ttyMember um]
|
||||
CRUnknownMemberAnnounced u g _ um m -> ttyUser u [ttyGroup' g <> ": unknown member " <> ttyMember um <> " updated to " <> ttyMember m]
|
||||
CRGroupDeletedUser u g -> ttyUser u [ttyGroup' g <> ": you deleted the group"]
|
||||
CRRcvFileDescrReady _ _ -> []
|
||||
CRRcvFileDescrNotReady _ _ -> []
|
||||
@ -963,6 +965,7 @@ viewGroupMembers (Group GroupInfo {membership} members) = map groupMember . filt
|
||||
status m = case memberStatus m of
|
||||
GSMemRemoved -> ["removed"]
|
||||
GSMemLeft -> ["left"]
|
||||
GSMemUnknown -> ["status unknown"]
|
||||
GSMemInvited -> ["not yet joined"]
|
||||
GSMemConnected -> ["connected"]
|
||||
GSMemComplete -> ["connected"]
|
||||
|
@ -132,6 +132,7 @@ chatGroupTests = do
|
||||
it "deleted message is not included" testGroupHistoryDeletedMessage
|
||||
it "disappearing message is sent as disappearing" testGroupHistoryDisappearingMessage
|
||||
it "welcome message (group description) is sent after history" testGroupHistoryWelcomeMessage
|
||||
it "unknown member messages are processed" testGroupHistoryUnknownMember
|
||||
where
|
||||
_0 = supportedChatVRange -- don't create direct connections
|
||||
_1 = groupCreateDirectVRange
|
||||
@ -5179,3 +5180,71 @@ testGroupHistoryWelcomeMessage =
|
||||
[alice, cath] *<# "#team bob> 2"
|
||||
cath #> "#team 3"
|
||||
[alice, bob] *<# "#team cath> 3"
|
||||
|
||||
testGroupHistoryUnknownMember :: HasCallStack => FilePath -> IO ()
|
||||
testGroupHistoryUnknownMember =
|
||||
testChat4 aliceProfile bobProfile cathProfile danProfile $
|
||||
\alice bob cath dan -> do
|
||||
createGroup3 "team" alice bob cath
|
||||
|
||||
threadDelay 1000000
|
||||
|
||||
alice #> "#team hi from alice"
|
||||
[bob, cath] *<# "#team alice> hi from alice"
|
||||
|
||||
threadDelay 1000000
|
||||
|
||||
bob #> "#team hi from bob"
|
||||
[alice, cath] *<# "#team bob> hi from bob"
|
||||
|
||||
threadDelay 1000000
|
||||
|
||||
cath #> "#team hi from cath"
|
||||
[alice, bob] *<# "#team cath> hi from cath"
|
||||
|
||||
bob ##> "/l team"
|
||||
concurrentlyN_
|
||||
[ do
|
||||
bob <## "#team: you left the group"
|
||||
bob <## "use /d #team to delete the group",
|
||||
alice <## "#team: bob left the group",
|
||||
cath <## "#team: bob left the group"
|
||||
]
|
||||
|
||||
connectUsers alice dan
|
||||
addMember "team" alice dan GRAdmin
|
||||
dan ##> "/j team"
|
||||
concurrentlyN_
|
||||
[ alice <## "#team: dan joined the group",
|
||||
dan
|
||||
<### [ "#team: you joined the group",
|
||||
WithTime "#team alice> hi from alice [>>]",
|
||||
StartsWith "#team: alice forwarded a message from an unknown member, creating unknown member record",
|
||||
EndsWith "hi from bob [>>]",
|
||||
WithTime "#team cath> hi from cath [>>]",
|
||||
"#team: member cath (Catherine) is connected"
|
||||
],
|
||||
do
|
||||
cath <## "#team: alice added dan (Daniel) to the group (connecting...)"
|
||||
cath <## "#team: new member dan is connected"
|
||||
]
|
||||
|
||||
dan ##> "/_get chat #1 count=100"
|
||||
r <- chat <$> getTermLine dan
|
||||
r `shouldContain` [(0, "hi from alice"), (0, "hi from bob"), (0, "hi from cath")]
|
||||
|
||||
dan ##> "/ms team"
|
||||
dan
|
||||
<### [ "dan (Daniel): admin, you, connected",
|
||||
"alice (Alice): owner, host, connected",
|
||||
"cath (Catherine): admin, connected",
|
||||
EndsWith "author, status unknown"
|
||||
]
|
||||
|
||||
-- message delivery works after sending history
|
||||
alice #> "#team 1"
|
||||
[cath, dan] *<# "#team alice> 1"
|
||||
cath #> "#team 2"
|
||||
[alice, dan] *<# "#team cath> 2"
|
||||
dan #> "#team 3"
|
||||
[alice, cath] *<# "#team dan> 3"
|
||||
|
Loading…
Reference in New Issue
Block a user