mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-22 13:11:55 +00:00
core: include channel link and name when forwarding messages (#7409)
* core: include channel link and name when forwarding messages * wip * simplify * add member ID * refactor * refactor * refactor * update api types * store forward source group type * rename * api types * simpler layout * layout, translations * refactor ios * public * simpler * refactor kotlin * padding * padding --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
This commit is contained in:
1 parent
1196d362ee
commit
17cdef1692
65 files changed
+806
-162
No files matched your search
@@ -711,7 +711,7 @@ processChatCommand cxt nm = \case
|
||||
getForwardedFromItem user ChatItem {meta = CIMeta {itemForwarded}} = case itemForwarded of
|
||||
Just (CIFFContact _ _ (Just ctId) (Just fwdItemId)) ->
|
||||
Just <$> withFastStore (\db -> getAChatItem db cxt user (ChatRef CTDirect ctId Nothing) fwdItemId)
|
||||
Just (CIFFGroup _ _ (Just gId) (Just fwdItemId)) ->
|
||||
Just (CIFFGroup _ _ (Just gId) (Just fwdItemId) _ _ _) ->
|
||||
-- TODO [knocking] getAChatItem doesn't differentiate how to read based on scope - it should, instead of using group filter
|
||||
Just <$> withFastStore (\db -> getAChatItem db cxt user (ChatRef CTGroup gId Nothing) fwdItemId)
|
||||
_ -> pure Nothing
|
||||
@@ -1096,9 +1096,11 @@ processChatCommand cxt nm = \case
|
||||
catMaybes <$> mapM (\ci -> ciComposeMsgReq gInfo ci <$$> prepareMsgReq ci) items
|
||||
where
|
||||
ciComposeMsgReq :: GroupInfo -> CChatItem 'CTGroup -> (MsgContent, Maybe CryptoFile) -> ComposedMessageReq
|
||||
ciComposeMsgReq gInfo (CChatItem md ci@ChatItem {mentions, formattedText}) (mc, file) = do
|
||||
ciComposeMsgReq gInfo (CChatItem md ci@ChatItem {mentions, formattedText, meta = CIMeta {itemSharedMsgId}}) (mc, file) = do
|
||||
let itemId = chatItemId' ci
|
||||
ciff = forwardCIFF ci $ Just (CIFFGroup (forwardName gInfo) (toMsgDirection md) (Just fromChatId) (Just itemId))
|
||||
fwdMemberId = memberId' <$> chatItemMember gInfo ci
|
||||
fwdGroupType = itemSharedMsgId *> sourceGroupType gInfo
|
||||
ciff = forwardCIFF ci $ Just (CIFFGroup (forwardName gInfo) (toMsgDirection md) (Just fromChatId) (Just itemId) fwdMemberId itemSharedMsgId fwdGroupType)
|
||||
-- updates text to reflect current mentioned member names
|
||||
(mc', _, mentions') = updatedMentionNames mc formattedText mentions
|
||||
-- only includes mentions when forwarding to the same group
|
||||
@@ -1108,6 +1110,8 @@ processChatCommand cxt nm = \case
|
||||
where
|
||||
forwardName :: GroupInfo -> ContactName
|
||||
forwardName GroupInfo {groupProfile = GroupProfile {displayName}} = displayName
|
||||
sourceGroupType :: GroupInfo -> Maybe GroupType
|
||||
sourceGroupType GroupInfo {groupProfile = GroupProfile {publicGroup}} = (\PublicGroupProfile {groupType} -> groupType) <$> publicGroup
|
||||
CTLocal -> do
|
||||
(_, items) <- getCommandLocalChatItems user fromChatId itemIds
|
||||
catMaybes <$> mapM (\ci -> ciComposeMsgReq ci <$$> prepareMsgReq ci) items
|
||||
@@ -4769,7 +4773,9 @@ processChatCommand cxt nm = \case
|
||||
forM cmsFileInvs $ \((ComposedMessage {quotedItemId, msgContent = mc}, itemForwarded, _, _), fInv_) -> do
|
||||
(mc', quotedItem_) <- case (quotedItemId, itemForwarded) of
|
||||
(Nothing, Nothing) -> pure (mcSimple mc, Nothing)
|
||||
(Nothing, Just _) -> pure (mcForward mc, Nothing)
|
||||
(Nothing, Just ciff) -> do
|
||||
fl_ <- liftIO $ ciffForwardLink db ciff
|
||||
pure (mcForward fl_ mc, Nothing)
|
||||
(Just qiId, Nothing) -> do
|
||||
CChatItem _ qci@ChatItem {meta = CIMeta {itemTs, itemSharedMsgId}, formattedText, file} <-
|
||||
getDirectChatItem db user contactId qiId
|
||||
|
||||
@@ -208,7 +208,9 @@ prepareGroupMsg :: DB.Connection -> User -> GroupInfo -> Maybe MsgScope -> ShowG
|
||||
prepareGroupMsg db user g@GroupInfo {membership} msgScope showGroupAsSender mc mentions quotedItemId_ itemForwarded fInv_ timed_ live = do
|
||||
(mc', quotedItem_) <- case (quotedItemId_, itemForwarded) of
|
||||
(Nothing, Nothing) -> pure (mcSimple mc, Nothing)
|
||||
(Nothing, Just _) -> pure (mcForward mc, Nothing)
|
||||
(Nothing, Just ciff) -> do
|
||||
fl_ <- liftIO $ ciffForwardLink db ciff
|
||||
pure (mcForward fl_ mc, Nothing)
|
||||
(Just quotedItemId, Nothing) -> do
|
||||
CChatItem _ qci@ChatItem {meta = CIMeta {itemTs, itemSharedMsgId}, formattedText, mentions = quoteMentions, file} <-
|
||||
getGroupCIWithReactions db user g quotedItemId
|
||||
@@ -231,6 +233,56 @@ prepareGroupMsg db user g@GroupInfo {membership} msgScope showGroupAsSender mc m
|
||||
quoteData ChatItem {chatDir = CIChannelRcv, content = CIRcvMsgContent qmc} _ = pure (qmc, CIQGroupRcv Nothing, False, Nothing)
|
||||
quoteData _ _ = throwError SEInvalidQuote
|
||||
|
||||
-- re-forwarded message is attributed to the original source
|
||||
ciffForwardLink :: DB.Connection -> CIForwardedFrom -> IO (Maybe ForwardLink)
|
||||
ciffForwardLink db = \case
|
||||
CIFFGroup {groupId = Just gId, memberId, sharedMsgId_ = Just msgId} ->
|
||||
getGroupProfileById db gId >>= \case
|
||||
Just GroupProfile {displayName, publicGroup = Just PublicGroupProfile {groupLink, publicGroupId}} ->
|
||||
pure $ Just ForwardLink {displayName, groupLink, publicGroupId, memberId, msgId}
|
||||
_ -> pure Nothing
|
||||
CIFFGroupLink {chatName, groupLink, publicGroupId, memberId, sharedMsgId} ->
|
||||
pure $ Just ForwardLink {displayName = chatName, groupLink, publicGroupId, memberId, msgId = sharedMsgId}
|
||||
_ -> pure Nothing
|
||||
|
||||
rcvForwardedFrom :: DB.Connection -> User -> ChatDirection c 'MDRcv -> RcvMessage -> IO (Maybe CIForwardedFrom)
|
||||
rcvForwardedFrom db user chatDirection RcvMessage {chatMsgEvent} = case chatMsgEvent of
|
||||
ACME _ (XMsgNew MsgContainer {forward = Just True, forwardLink}) -> case forwardLink of
|
||||
Nothing -> pure $ Just CIFFUnknown
|
||||
Just fl@ForwardLink {displayName}
|
||||
| linkAllowed -> Just <$> forwardLinkCIFF db user fl
|
||||
| otherwise -> pure $ Just $ CIFFGroup displayName MDRcv Nothing Nothing Nothing Nothing Nothing
|
||||
_ -> pure Nothing
|
||||
where
|
||||
linkAllowed = case chatDirection of
|
||||
CDGroupRcv gInfo _ GroupMember {memberRole} -> allowed memberRole gInfo
|
||||
CDChannelRcv gInfo _ -> allowed GROwner gInfo
|
||||
_ -> True
|
||||
where
|
||||
allowed role = groupFeatureMemberAllowed' SGFSimplexLinks role . fullGroupPreferences
|
||||
|
||||
forwardLinkCIFF :: DB.Connection -> User -> ForwardLink -> IO CIForwardedFrom
|
||||
forwardLinkCIFF db user ForwardLink {displayName, groupLink, publicGroupId, memberId, msgId} =
|
||||
getGroupViaPublicGroupId db user publicGroupId >>= \case
|
||||
Just (gId, Just storedLink)
|
||||
| sameShortLinkContact groupLink storedLink -> do
|
||||
ciId_ <- itemId_ gId
|
||||
pure $ CIFFGroup displayName MDRcv (Just gId) ciId_ memberId (Just msgId) linkGroupType
|
||||
_ -> pure $ CIFFGroupLink displayName MDRcv groupLink publicGroupId memberId msgId linkGroupType
|
||||
where
|
||||
linkGroupType = case groupLink of
|
||||
CSLContact _ CCTChannel _ _ -> Just GTChannel
|
||||
CSLContact _ CCTGroup _ _ -> Just GTGroup
|
||||
_ -> Nothing
|
||||
itemId_ gId = case memberId of
|
||||
Nothing -> getGroupChatItemBySharedMsgId_ db user gId Nothing msgId
|
||||
Just mId ->
|
||||
getGroupMemberViaMemberId_ db user gId mId >>= \case
|
||||
Just (gmId, category) ->
|
||||
let scope = if category == GCUserMember then Nothing else Just gmId
|
||||
in getGroupChatItemBySharedMsgId_ db user gId scope msgId
|
||||
Nothing -> pure Nothing
|
||||
|
||||
updatedMentionNames :: MsgContent -> Maybe MarkdownList -> Map MemberName CIMention -> (MsgContent, Maybe MarkdownList, Map MemberName CIMention)
|
||||
updatedMentionNames mc ft_ mentions = case ft_ of
|
||||
Just ft
|
||||
@@ -2767,7 +2819,8 @@ saveRcvChatItem' user cd msg@RcvMessage {chatMsgEvent, msgSigned, forwardedByMem
|
||||
else pure $ toChatInfo cd
|
||||
let showAsGroup = case cd of CDChannelRcv {} -> True; _ -> False
|
||||
hasLink_ = ciContentHasLink content ft_
|
||||
(ciId, quotedItem, itemForwarded) <- createNewRcvChatItem db user cd msg sharedMsgId_ content itemTimed live userMention hasLink_ brokerTs createdAt
|
||||
itemForwarded <- rcvForwardedFrom db user cd msg
|
||||
(ciId, quotedItem) <- createNewRcvChatItem db user cd msg sharedMsgId_ content itemForwarded itemTimed live userMention hasLink_ brokerTs createdAt
|
||||
forM_ ciFile $ \CIFile {fileId} -> updateFileTransferChatItemId db fileId ciId createdAt
|
||||
let ci = mkChatItem_ cd showAsGroup ciId content (t, ft_) ciFile quotedItem sharedMsgId_ itemForwarded itemTimed live userMention hasLink_ brokerTs forwardedByMember (toMsgVerified (signMessagesRequired cd) msgSigned) createdAt
|
||||
ci' <- case toChatInfo cd of
|
||||
|
||||
@@ -1319,13 +1319,15 @@ itemDeletedTs = \case
|
||||
data CIForwardedFrom
|
||||
= CIFFUnknown
|
||||
| CIFFContact {chatName :: Text, msgDir :: MsgDirection, contactId :: Maybe ContactId, chatItemId :: Maybe ChatItemId}
|
||||
| CIFFGroup {chatName :: Text, msgDir :: MsgDirection, groupId :: Maybe GroupId, chatItemId :: Maybe ChatItemId}
|
||||
| CIFFGroup {chatName :: Text, msgDir :: MsgDirection, groupId :: Maybe GroupId, chatItemId :: Maybe ChatItemId, memberId :: Maybe MemberId, sharedMsgId_ :: Maybe SharedMsgId, groupType :: Maybe GroupType}
|
||||
| CIFFGroupLink {chatName :: Text, msgDir :: MsgDirection, groupLink :: ShortLinkContact, publicGroupId :: B64UrlByteString, memberId :: Maybe MemberId, sharedMsgId :: SharedMsgId, groupType :: Maybe GroupType}
|
||||
deriving (Show)
|
||||
|
||||
data CIForwardedFromTag
|
||||
= CIFFUnknown_
|
||||
| CIFFContact_
|
||||
| CIFFGroup_
|
||||
| CIFFGroupLink_
|
||||
|
||||
instance FromField CIForwardedFromTag where fromField = fromTextField_ textDecode
|
||||
|
||||
@@ -1336,11 +1338,13 @@ instance TextEncoding CIForwardedFromTag where
|
||||
"unknown" -> Just CIFFUnknown_
|
||||
"contact" -> Just CIFFContact_
|
||||
"group" -> Just CIFFGroup_
|
||||
"groupLink" -> Just CIFFGroupLink_
|
||||
_ -> Nothing
|
||||
textEncode = \case
|
||||
CIFFUnknown_ -> "unknown"
|
||||
CIFFContact_ -> "contact"
|
||||
CIFFGroup_ -> "group"
|
||||
CIFFGroupLink_ -> "groupLink"
|
||||
|
||||
data ChatItemInfo = ChatItemInfo
|
||||
{ itemVersions :: [ChatItemVersion],
|
||||
|
||||
@@ -688,7 +688,17 @@ data MsgContainer = MsgContainer
|
||||
asGroup :: Maybe Bool,
|
||||
quote :: Maybe QuotedMsg,
|
||||
parent :: Maybe MsgRef,
|
||||
forward :: Maybe Bool
|
||||
forward :: Maybe Bool,
|
||||
forwardLink :: Maybe ForwardLink
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
data ForwardLink = ForwardLink
|
||||
{ displayName :: Text,
|
||||
groupLink :: ShortLinkContact,
|
||||
publicGroupId :: B64UrlByteString,
|
||||
memberId :: Maybe MemberId,
|
||||
msgId :: SharedMsgId
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
@@ -704,7 +714,8 @@ mcSimple content =
|
||||
asGroup = Nothing,
|
||||
quote = Nothing,
|
||||
parent = Nothing,
|
||||
forward = Nothing
|
||||
forward = Nothing,
|
||||
forwardLink = Nothing
|
||||
}
|
||||
|
||||
mcQuote :: QuotedMsg -> MsgContent -> MsgContainer
|
||||
@@ -713,8 +724,8 @@ mcQuote q c = (mcSimple c) {quote = Just q}
|
||||
mcComment :: MsgRef -> MsgContent -> MsgContainer
|
||||
mcComment p c = (mcSimple c) {parent = Just p}
|
||||
|
||||
mcForward :: MsgContent -> MsgContainer
|
||||
mcForward c = (mcSimple c) {forward = Just True}
|
||||
mcForward :: Maybe ForwardLink -> MsgContent -> MsgContainer
|
||||
mcForward fl c = (mcSimple c) {forward = Just True, forwardLink = fl}
|
||||
|
||||
data MsgContent
|
||||
= MCText {text :: Text}
|
||||
@@ -896,6 +907,8 @@ instance ToJSON MsgContent where
|
||||
MCReport {text, reason} -> J.pairs $ "type" .= MCReport_ <> "text" .= text <> "reason" .= reason
|
||||
MCChat {text, chatLink, ownerSig} -> J.pairs $ "type" .= MCChat_ <> "text" .= text <> "chatLink" .= chatLink <> maybe mempty ("ownerSig" .=) ownerSig
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''ForwardLink)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''MsgContainer)
|
||||
|
||||
-- this limit reserves space for metadata in forwarded messages
|
||||
|
||||
@@ -63,6 +63,7 @@ module Simplex.Chat.Store.Groups
|
||||
getGroupMemberByMemberId,
|
||||
getCreateUnknownGMByMemberId,
|
||||
getGroupMemberIdViaMemberId,
|
||||
getGroupMemberViaMemberId_,
|
||||
getScopeMemberIdViaMemberId,
|
||||
getGroupMembers,
|
||||
getGroupMembersByIndexes,
|
||||
@@ -128,6 +129,8 @@ module Simplex.Chat.Store.Groups
|
||||
getRelayServedGroups,
|
||||
getRelayPublishableGroups,
|
||||
getRelayInactiveGroups,
|
||||
getGroupViaPublicGroupId,
|
||||
getGroupProfileById,
|
||||
createJoiningMember,
|
||||
getMemberJoinRequest,
|
||||
createJoiningMemberConnection,
|
||||
@@ -1212,11 +1215,16 @@ getScopeMemberIdViaMemberId db user g@GroupInfo {membership} sender scopeMemberI
|
||||
| otherwise = getGroupMemberIdViaMemberId db user g scopeMemberId
|
||||
|
||||
getGroupMemberIdViaMemberId :: DB.Connection -> User -> GroupInfo -> MemberId -> ExceptT StoreError IO GroupMemberId
|
||||
getGroupMemberIdViaMemberId db User {userId} GroupInfo {groupId} memberId =
|
||||
ExceptT . firstRow fromOnly (SEGroupMemberNotFoundByMemberId memberId) $
|
||||
getGroupMemberIdViaMemberId db user GroupInfo {groupId} memberId = do
|
||||
m_ <- liftIO $ getGroupMemberViaMemberId_ db user groupId memberId
|
||||
maybe (throwError $ SEGroupMemberNotFoundByMemberId memberId) (pure . fst) m_
|
||||
|
||||
getGroupMemberViaMemberId_ :: DB.Connection -> User -> GroupId -> MemberId -> IO (Maybe (GroupMemberId, GroupMemberCategory))
|
||||
getGroupMemberViaMemberId_ db User {userId} groupId memberId =
|
||||
maybeFirstRow id $
|
||||
DB.query
|
||||
db
|
||||
"SELECT group_member_id FROM group_members WHERE user_id = ? AND group_id = ? AND member_id = ?"
|
||||
"SELECT group_member_id, member_category FROM group_members WHERE user_id = ? AND group_id = ? AND member_id = ?"
|
||||
(userId, groupId, memberId)
|
||||
|
||||
getGroupMembers :: DB.Connection -> StoreCxt -> User -> GroupInfo -> IO [GroupMember]
|
||||
@@ -2018,6 +2026,20 @@ getRelayPublishableGroups db User {userId, userContactId} =
|
||||
where
|
||||
toRow ((gId, pgId) :. accessRow) = (gId, pgId, toPublicGroupAccess accessRow)
|
||||
|
||||
getGroupViaPublicGroupId :: DB.Connection -> User -> B64UrlByteString -> IO (Maybe (GroupId, Maybe ShortLinkContact))
|
||||
getGroupViaPublicGroupId db User {userId} publicGroupId =
|
||||
maybeFirstRow id $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT g.group_id, gp.group_link
|
||||
FROM groups g
|
||||
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
||||
WHERE g.user_id = ? AND gp.public_group_id = ?
|
||||
LIMIT 1
|
||||
|]
|
||||
(userId, publicGroupId)
|
||||
|
||||
getRelayInactiveGroups :: DB.Connection -> StoreCxt -> User -> NominalDiffTime -> IO [GroupInfo]
|
||||
getRelayInactiveGroups db cxt User {userId, userContactId} ttl = do
|
||||
currentTs <- getCurrentTime
|
||||
@@ -2759,26 +2781,28 @@ updateGroupPreferences db User {userId} g@GroupInfo {groupId, groupProfile = p}
|
||||
|
||||
updateGroupProfileFromMember :: DB.Connection -> User -> GroupInfo -> Profile -> ExceptT StoreError IO GroupInfo
|
||||
updateGroupProfileFromMember db user g@GroupInfo {groupId} Profile {displayName = n, fullName = fn, shortDescr = sd, description = descr, image = img} = do
|
||||
p <- getGroupProfile -- to avoid any race conditions with UI
|
||||
p_ <- liftIO $ getGroupProfileById db groupId -- to avoid any race conditions with UI
|
||||
p <- maybe (throwError $ SEGroupNotFound groupId) pure p_
|
||||
let g' = g {groupProfile = p} :: GroupInfo
|
||||
p' = p {displayName = n, fullName = fn, shortDescr = sd, description = descr, image = img} :: GroupProfile
|
||||
updateGroupProfile db user g' p'
|
||||
|
||||
getGroupProfileById :: DB.Connection -> GroupId -> IO (Maybe GroupProfile)
|
||||
getGroupProfileById db groupId =
|
||||
maybeFirstRow toGroupProfile $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image,
|
||||
gp.group_type, gp.group_link, gp.public_group_id,
|
||||
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
|
||||
gp.preferences, gp.member_admission
|
||||
FROM group_profiles gp
|
||||
JOIN groups g ON gp.group_profile_id = g.group_profile_id
|
||||
WHERE g.group_id = ?
|
||||
|]
|
||||
(Only groupId)
|
||||
where
|
||||
getGroupProfile =
|
||||
ExceptT $
|
||||
firstRow toGroupProfile (SEGroupNotFound groupId) $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image,
|
||||
gp.group_type, gp.group_link, gp.public_group_id,
|
||||
gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.group_domain_proof,
|
||||
gp.preferences, gp.member_admission
|
||||
FROM group_profiles gp
|
||||
JOIN groups g ON gp.group_profile_id = g.group_profile_id
|
||||
WHERE g.group_id = ?
|
||||
|]
|
||||
(Only groupId)
|
||||
toGroupProfile ((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (groupPreferences, memberAdmission)) =
|
||||
let publicGroupAccess = toPublicGroupAccess accessRow
|
||||
in GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_ publicGroupAccess, groupPreferences, memberAdmission}
|
||||
|
||||
@@ -100,6 +100,7 @@ module Simplex.Chat.Store.Messages
|
||||
getDirectChatItem,
|
||||
getDirectCIWithReactions,
|
||||
getDirectChatItemBySharedMsgId,
|
||||
getGroupChatItemBySharedMsgId_,
|
||||
getDirectChatItemsByAgentMsgId,
|
||||
getGroupChatItem,
|
||||
getGroupCIWithReactions,
|
||||
@@ -560,16 +561,13 @@ createNewSndChatItem db user chatDirection showGroupAsSender SndMessage {msgId,
|
||||
CIQGroupRcv (Just GroupMember {memberId}) -> (Just False, Just memberId)
|
||||
CIQGroupRcv Nothing -> (Just False, Nothing)
|
||||
|
||||
createNewRcvChatItem :: ChatTypeQuotable c => DB.Connection -> User -> ChatDirection c 'MDRcv -> RcvMessage -> Maybe SharedMsgId -> CIContent 'MDRcv -> Maybe CITimed -> Bool -> Bool -> Bool -> UTCTime -> UTCTime -> IO (ChatItemId, Maybe (CIQuote c), Maybe CIForwardedFrom)
|
||||
createNewRcvChatItem db user chatDirection RcvMessage {msgId, chatMsgEvent, msgSigned, signedMsg_, signedByGMId_, forwardedByMember} sharedMsgId_ ciContent timed live userMention hasLink itemTs createdAt = do
|
||||
createNewRcvChatItem :: ChatTypeQuotable c => DB.Connection -> User -> ChatDirection c 'MDRcv -> RcvMessage -> Maybe SharedMsgId -> CIContent 'MDRcv -> Maybe CIForwardedFrom -> Maybe CITimed -> Bool -> Bool -> Bool -> UTCTime -> UTCTime -> IO (ChatItemId, Maybe (CIQuote c))
|
||||
createNewRcvChatItem db user chatDirection RcvMessage {msgId, chatMsgEvent, msgSigned, signedMsg_, signedByGMId_, forwardedByMember} sharedMsgId_ ciContent itemForwarded timed live userMention hasLink itemTs createdAt = do
|
||||
let showAsGroup = case chatDirection of CDChannelRcv {} -> True; _ -> False
|
||||
ciId <- createNewChatItem_ db user chatDirection showAsGroup (Just msgId) sharedMsgId_ ciContent quoteRow itemForwarded timed live userMention hasLink itemTs forwardedByMember (toMsgVerified (signMessagesRequired chatDirection) msgSigned) signedMsg_ signedByGMId_ createdAt
|
||||
quotedItem <- mapM (getChatItemQuote_ db user chatDirection) quotedMsg
|
||||
pure (ciId, quotedItem, itemForwarded)
|
||||
pure (ciId, quotedItem)
|
||||
where
|
||||
itemForwarded = case chatMsgEvent of
|
||||
ACME _ (XMsgNew MsgContainer {forward}) | forward == Just True -> Just CIFFUnknown
|
||||
_ -> Nothing
|
||||
quotedMsg = cmToQuotedMsg chatMsgEvent
|
||||
quoteRow :: NewQuoteRow
|
||||
quoteRow = case quotedMsg of
|
||||
@@ -603,8 +601,9 @@ createNewChatItem_ db User {userId} chatDirection showGroupAsSender msgId_ share
|
||||
-- quote
|
||||
quoted_shared_msg_id, quoted_sent_at, quoted_content, quoted_sent, quoted_member_id,
|
||||
-- forwarded from
|
||||
fwd_from_tag, fwd_from_chat_name, fwd_from_msg_dir, fwd_from_contact_id, fwd_from_group_id, fwd_from_chat_item_id
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
fwd_from_tag, fwd_from_chat_name, fwd_from_msg_dir, fwd_from_contact_id, fwd_from_group_id, fwd_from_chat_item_id,
|
||||
fwd_from_group_type, fwd_from_group_link, fwd_from_public_group_id, fwd_from_member_id, fwd_from_shared_msg_id
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
((userId, msgId_) :. idsRow :. groupScopeRow :. itemRow :. quoteRow' :. forwardedFromRow)
|
||||
ciId <- insertedRowId db
|
||||
@@ -648,16 +647,20 @@ createNewChatItem_ db User {userId} chatDirection showGroupAsSender msgId_ share
|
||||
SMDSnd -> isJust mcTag_
|
||||
SMDRcv -> False
|
||||
mcTag_ = msgContentTag <$> ciMsgContent ciContent
|
||||
forwardedFromRow :: (Maybe CIForwardedFromTag, Maybe Text, Maybe MsgDirection, Maybe Int64, Maybe Int64, Maybe Int64)
|
||||
forwardedFromRow :: ChatItemForwardedFromRow
|
||||
forwardedFromRow = case itemForwarded of
|
||||
Nothing ->
|
||||
(Nothing, Nothing, Nothing, Nothing, Nothing, Nothing)
|
||||
(Nothing, Nothing, Nothing, Nothing, Nothing, Nothing) :. noLinkRow
|
||||
Just CIFFUnknown ->
|
||||
(Just CIFFUnknown_, Nothing, Nothing, Nothing, Nothing, Nothing)
|
||||
(Just CIFFUnknown_, Nothing, Nothing, Nothing, Nothing, Nothing) :. noLinkRow
|
||||
Just CIFFContact {chatName, msgDir, contactId, chatItemId} ->
|
||||
(Just CIFFContact_, Just chatName, Just msgDir, contactId, Nothing, chatItemId)
|
||||
Just CIFFGroup {chatName, msgDir, groupId, chatItemId} ->
|
||||
(Just CIFFGroup_, Just chatName, Just msgDir, Nothing, groupId, chatItemId)
|
||||
(Just CIFFContact_, Just chatName, Just msgDir, contactId, Nothing, chatItemId) :. noLinkRow
|
||||
Just CIFFGroup {chatName, msgDir, groupId, chatItemId, memberId, sharedMsgId_, groupType} ->
|
||||
(Just CIFFGroup_, Just chatName, Just msgDir, Nothing, groupId, chatItemId) :. (groupType, Nothing, Nothing, memberId, sharedMsgId_)
|
||||
Just CIFFGroupLink {chatName, msgDir, groupLink, publicGroupId, memberId, sharedMsgId = fwdSharedMsgId, groupType} ->
|
||||
(Just CIFFGroupLink_, Just chatName, Just msgDir, Nothing, Nothing, Nothing) :. (groupType, Just groupLink, Just publicGroupId, memberId, Just fwdSharedMsgId)
|
||||
noLinkRow :: ChatItemForwardedLinkRow
|
||||
noLinkRow = (Nothing, Nothing, Nothing, Nothing, Nothing)
|
||||
|
||||
ciTimedRow :: Maybe CITimed -> (Maybe Int, Maybe UTCTime)
|
||||
ciTimedRow (Just CITimed {ttl, deleteAt}) = (Just ttl, deleteAt)
|
||||
@@ -2277,7 +2280,9 @@ type MaybeCIFIleRow = (Maybe Int64, Maybe String, Maybe Integer, Maybe FilePath,
|
||||
|
||||
type ChatItemModeRow = (Maybe Int, Maybe UTCTime, Maybe BoolInt, BoolInt, BoolInt, Maybe MsgVerified)
|
||||
|
||||
type ChatItemForwardedFromRow = (Maybe CIForwardedFromTag, Maybe Text, Maybe MsgDirection, Maybe Int64, Maybe Int64, Maybe Int64)
|
||||
type ChatItemForwardedFromRow = (Maybe CIForwardedFromTag, Maybe Text, Maybe MsgDirection, Maybe Int64, Maybe Int64, Maybe Int64) :. ChatItemForwardedLinkRow
|
||||
|
||||
type ChatItemForwardedLinkRow = (Maybe GroupType, Maybe ShortLinkContact, Maybe B64UrlByteString, Maybe MemberId, Maybe SharedMsgId)
|
||||
|
||||
type ChatItemRow =
|
||||
(Int64, ChatItemTs, AMsgDirection, Text, Text, ACIStatus, Maybe BoolInt, Maybe SharedMsgId)
|
||||
@@ -2337,11 +2342,14 @@ toDirectChatItem currentTs (((itemId, itemTs, AMsgDirection msgDir, itemContentT
|
||||
ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt}
|
||||
|
||||
toCIForwardedFrom :: ChatItemForwardedFromRow -> Maybe CIForwardedFrom
|
||||
toCIForwardedFrom (fwdFromTag, fwdFromChatName, fwdFromMsgDir, fwdFromContactId, fwdFromGroupId, fwdFromChatItemId) =
|
||||
case (fwdFromTag, fwdFromChatName, fwdFromMsgDir, fwdFromContactId, fwdFromGroupId, fwdFromChatItemId) of
|
||||
toCIForwardedFrom (fwdFromRow :. (groupType_, groupLink_, publicGroupId_, memberId_, sharedMsgId_)) =
|
||||
case fwdFromRow of
|
||||
(Just CIFFUnknown_, Nothing, Nothing, Nothing, Nothing, Nothing) -> Just CIFFUnknown
|
||||
(Just CIFFContact_, Just chatName, Just msgDir, contactId, Nothing, ciId) -> Just $ CIFFContact chatName msgDir contactId ciId
|
||||
(Just CIFFGroup_, Just chatName, Just msgDir, Nothing, groupId, ciId) -> Just $ CIFFGroup chatName msgDir groupId ciId
|
||||
(Just CIFFGroup_, Just chatName, Just msgDir, Nothing, groupId, ciId) -> Just $ CIFFGroup chatName msgDir groupId ciId memberId_ sharedMsgId_ groupType_
|
||||
(Just CIFFGroupLink_, Just chatName, Just msgDir, Nothing, Nothing, Nothing)
|
||||
| Just groupLink <- groupLink_, Just publicGroupId <- publicGroupId_, Just sharedMsgId <- sharedMsgId_ ->
|
||||
Just $ CIFFGroupLink chatName msgDir groupLink publicGroupId memberId_ sharedMsgId groupType_
|
||||
_ -> Nothing
|
||||
|
||||
type GroupQuoteRow = QuoteRow :. MaybeGroupMemberRow
|
||||
@@ -2694,6 +2702,7 @@ getDirectChatItem db User {userId} contactId itemId = ExceptT $ do
|
||||
i.chat_item_id, i.item_ts, i.item_sent, i.item_content, i.item_text, i.item_status, i.via_proxy, i.shared_msg_id,
|
||||
i.item_deleted, i.item_deleted_ts, i.item_edited, i.created_at, i.updated_at,
|
||||
i.fwd_from_tag, i.fwd_from_chat_name, i.fwd_from_msg_dir, i.fwd_from_contact_id, i.fwd_from_group_id, i.fwd_from_chat_item_id,
|
||||
i.fwd_from_group_type, i.fwd_from_group_link, i.fwd_from_public_group_id, i.fwd_from_member_id, i.fwd_from_shared_msg_id,
|
||||
i.timed_ttl, i.timed_delete_at, i.item_live, i.user_mention, i.has_link, i.msg_signed,
|
||||
-- CIFile
|
||||
f.file_id, f.file_name, f.file_size, f.file_path, f.file_crypto_key, f.file_crypto_nonce, f.ci_file_status, f.protocol,
|
||||
@@ -3030,21 +3039,25 @@ markReceivedGroupReportsDeleted db User {userId} GroupInfo {groupId, membership}
|
||||
(DBCIDeleted, deletedTs, groupMemberId' membership, currentTs, userId, groupId, MCReport_, DBCINotDeleted)
|
||||
|
||||
getGroupChatItemBySharedMsgId :: DB.Connection -> User -> GroupInfo -> Maybe GroupMemberId -> SharedMsgId -> ExceptT StoreError IO (CChatItem 'CTGroup)
|
||||
getGroupChatItemBySharedMsgId db user@User {userId} g@GroupInfo {groupId} groupMemberId_ sharedMsgId = do
|
||||
itemId <-
|
||||
ExceptT . firstRow fromOnly (SEChatItemSharedMsgIdNotFound sharedMsgId) $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT chat_item_id
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id = ? AND group_member_id IS NOT DISTINCT FROM ? AND shared_msg_id = ?
|
||||
ORDER BY chat_item_id DESC
|
||||
LIMIT 1
|
||||
|]
|
||||
(userId, groupId, groupMemberId_, sharedMsgId)
|
||||
getGroupChatItemBySharedMsgId db user g@GroupInfo {groupId} groupMemberId_ sharedMsgId = do
|
||||
itemId_ <- liftIO $ getGroupChatItemBySharedMsgId_ db user groupId groupMemberId_ sharedMsgId
|
||||
itemId <- maybe (throwError $ SEChatItemSharedMsgIdNotFound sharedMsgId) pure itemId_
|
||||
getGroupCIWithReactions db user g itemId
|
||||
|
||||
getGroupChatItemBySharedMsgId_ :: DB.Connection -> User -> GroupId -> Maybe GroupMemberId -> SharedMsgId -> IO (Maybe ChatItemId)
|
||||
getGroupChatItemBySharedMsgId_ db User {userId} groupId groupMemberId_ sharedMsgId =
|
||||
maybeFirstRow fromOnly $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT chat_item_id
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id = ? AND group_member_id IS NOT DISTINCT FROM ? AND shared_msg_id = ?
|
||||
ORDER BY chat_item_id DESC
|
||||
LIMIT 1
|
||||
|]
|
||||
(userId, groupId, groupMemberId_, sharedMsgId)
|
||||
|
||||
getGroupMemberCIBySharedMsgId :: DB.Connection -> User -> GroupInfo -> MemberId -> SharedMsgId -> ExceptT StoreError IO (CChatItem 'CTGroup)
|
||||
getGroupMemberCIBySharedMsgId db user@User {userId} g@GroupInfo {groupId} memberId sharedMsgId = do
|
||||
itemId <-
|
||||
@@ -3083,6 +3096,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
|
||||
i.chat_item_id, i.item_ts, i.item_sent, i.item_content, i.item_text, i.item_status, i.via_proxy, i.shared_msg_id,
|
||||
i.item_deleted, i.item_deleted_ts, i.item_edited, i.created_at, i.updated_at,
|
||||
i.fwd_from_tag, i.fwd_from_chat_name, i.fwd_from_msg_dir, i.fwd_from_contact_id, i.fwd_from_group_id, i.fwd_from_chat_item_id,
|
||||
i.fwd_from_group_type, i.fwd_from_group_link, i.fwd_from_public_group_id, i.fwd_from_member_id, i.fwd_from_shared_msg_id,
|
||||
i.timed_ttl, i.timed_delete_at, i.item_live, i.user_mention, i.has_link, i.msg_signed,
|
||||
-- CIFile
|
||||
f.file_id, f.file_name, f.file_size, f.file_path, f.file_crypto_key, f.file_crypto_nonce, f.ci_file_status, f.protocol,
|
||||
@@ -3195,6 +3209,7 @@ getLocalChatItem db User {userId} folderId itemId = ExceptT $ do
|
||||
i.chat_item_id, i.item_ts, i.item_sent, i.item_content, i.item_text, i.item_status, i.via_proxy, i.shared_msg_id,
|
||||
i.item_deleted, i.item_deleted_ts, i.item_edited, i.created_at, i.updated_at,
|
||||
i.fwd_from_tag, i.fwd_from_chat_name, i.fwd_from_msg_dir, i.fwd_from_contact_id, i.fwd_from_group_id, i.fwd_from_chat_item_id,
|
||||
i.fwd_from_group_type, i.fwd_from_group_link, i.fwd_from_public_group_id, i.fwd_from_member_id, i.fwd_from_shared_msg_id,
|
||||
i.timed_ttl, i.timed_delete_at, i.item_live, i.user_mention, i.has_link, i.msg_signed,
|
||||
-- CIFile
|
||||
f.file_id, f.file_name, f.file_size, f.file_path, f.file_crypto_key, f.file_crypto_nonce, f.ci_file_status, f.protocol
|
||||
|
||||
@@ -47,6 +47,7 @@ import Simplex.Chat.Store.Postgres.Migrations.M20260716_signed_history
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20260720_server_roles
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20260723_contact_request_rejection
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20260813_auto_accept_group_invitations
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20260822_forward_link
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Text, Maybe Text)]
|
||||
@@ -93,7 +94,8 @@ schemaMigrations =
|
||||
("20260716_signed_history", m20260716_signed_history, Just down_m20260716_signed_history),
|
||||
("20260720_server_roles", m20260720_server_roles, Just down_m20260720_server_roles),
|
||||
("20260723_contact_request_rejection", m20260723_contact_request_rejection, Just down_m20260723_contact_request_rejection),
|
||||
("20260813_auto_accept_group_invitations", m20260813_auto_accept_group_invitations, Just down_m20260813_auto_accept_group_invitations)
|
||||
("20260813_auto_accept_group_invitations", m20260813_auto_accept_group_invitations, Just down_m20260813_auto_accept_group_invitations),
|
||||
("20260822_forward_link", m20260822_forward_link, Just down_m20260822_forward_link)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.Postgres.Migrations.M20260822_forward_link where
|
||||
|
||||
import Data.Text (Text)
|
||||
import Text.RawString.QQ (r)
|
||||
|
||||
m20260822_forward_link :: Text
|
||||
m20260822_forward_link =
|
||||
[r|
|
||||
ALTER TABLE chat_items ADD COLUMN fwd_from_group_type TEXT;
|
||||
ALTER TABLE chat_items ADD COLUMN fwd_from_group_link BYTEA;
|
||||
ALTER TABLE chat_items ADD COLUMN fwd_from_public_group_id BYTEA;
|
||||
ALTER TABLE chat_items ADD COLUMN fwd_from_member_id BYTEA;
|
||||
ALTER TABLE chat_items ADD COLUMN fwd_from_shared_msg_id BYTEA;
|
||||
|]
|
||||
|
||||
down_m20260822_forward_link :: Text
|
||||
down_m20260822_forward_link =
|
||||
[r|
|
||||
ALTER TABLE chat_items DROP COLUMN fwd_from_group_type;
|
||||
ALTER TABLE chat_items DROP COLUMN fwd_from_group_link;
|
||||
ALTER TABLE chat_items DROP COLUMN fwd_from_public_group_id;
|
||||
ALTER TABLE chat_items DROP COLUMN fwd_from_member_id;
|
||||
ALTER TABLE chat_items DROP COLUMN fwd_from_shared_msg_id;
|
||||
|]
|
||||
@@ -349,7 +349,12 @@ CREATE TABLE test_chat_schema.chat_items (
|
||||
item_msg_body bytea,
|
||||
item_chat_binding text,
|
||||
item_signatures bytea,
|
||||
item_signed_by_group_member_id bigint
|
||||
item_signed_by_group_member_id bigint,
|
||||
fwd_from_group_type text,
|
||||
fwd_from_group_link bytea,
|
||||
fwd_from_public_group_id bytea,
|
||||
fwd_from_member_id bytea,
|
||||
fwd_from_shared_msg_id bytea
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -170,6 +170,7 @@ import Simplex.Chat.Store.SQLite.Migrations.M20260716_signed_history
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20260720_server_roles
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20260723_contact_request_rejection
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20260813_auto_accept_group_invitations
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20260822_forward_link
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Query, Maybe Query)]
|
||||
@@ -339,7 +340,8 @@ schemaMigrations =
|
||||
("20260716_signed_history", m20260716_signed_history, Just down_m20260716_signed_history),
|
||||
("20260720_server_roles", m20260720_server_roles, Just down_m20260720_server_roles),
|
||||
("20260723_contact_request_rejection", m20260723_contact_request_rejection, Just down_m20260723_contact_request_rejection),
|
||||
("20260813_auto_accept_group_invitations", m20260813_auto_accept_group_invitations, Just down_m20260813_auto_accept_group_invitations)
|
||||
("20260813_auto_accept_group_invitations", m20260813_auto_accept_group_invitations, Just down_m20260813_auto_accept_group_invitations),
|
||||
("20260822_forward_link", m20260822_forward_link, Just down_m20260822_forward_link)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20260822_forward_link where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20260822_forward_link :: Query
|
||||
m20260822_forward_link =
|
||||
[sql|
|
||||
ALTER TABLE chat_items ADD COLUMN fwd_from_group_type TEXT;
|
||||
ALTER TABLE chat_items ADD COLUMN fwd_from_group_link BLOB;
|
||||
ALTER TABLE chat_items ADD COLUMN fwd_from_public_group_id BLOB;
|
||||
ALTER TABLE chat_items ADD COLUMN fwd_from_member_id BLOB;
|
||||
ALTER TABLE chat_items ADD COLUMN fwd_from_shared_msg_id BLOB;
|
||||
|]
|
||||
|
||||
down_m20260822_forward_link :: Query
|
||||
down_m20260822_forward_link =
|
||||
[sql|
|
||||
ALTER TABLE chat_items DROP COLUMN fwd_from_group_type;
|
||||
ALTER TABLE chat_items DROP COLUMN fwd_from_group_link;
|
||||
ALTER TABLE chat_items DROP COLUMN fwd_from_public_group_id;
|
||||
ALTER TABLE chat_items DROP COLUMN fwd_from_member_id;
|
||||
ALTER TABLE chat_items DROP COLUMN fwd_from_shared_msg_id;
|
||||
|]
|
||||
@@ -514,7 +514,12 @@ CREATE TABLE chat_items(
|
||||
item_msg_body BLOB,
|
||||
item_chat_binding TEXT,
|
||||
item_signatures BLOB,
|
||||
item_signed_by_group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL
|
||||
item_signed_by_group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL,
|
||||
fwd_from_group_type TEXT,
|
||||
fwd_from_group_link BLOB,
|
||||
fwd_from_public_group_id BLOB,
|
||||
fwd_from_member_id BLOB,
|
||||
fwd_from_shared_msg_id BLOB
|
||||
) STRICT;
|
||||
CREATE TABLE sqlite_sequence(name,seq);
|
||||
CREATE TABLE chat_item_messages(
|
||||
|
||||
@@ -790,7 +790,7 @@ viewChatItem chat ci@ChatItem {chatDir, meta = meta@CIMeta {itemForwarded, forwa
|
||||
prohibited = styled (colored Red) ("[unexpected chat item created, please report to developers]" :: String)
|
||||
|
||||
viewChatItemInfo :: AChatItem -> ChatItemInfo -> TimeZone -> [StyledString]
|
||||
viewChatItemInfo (AChatItem _ msgDir _ ChatItem {meta = CIMeta {itemTs, itemTimed, createdAt}}) ChatItemInfo {itemVersions, forwardedFromChatItem} tz =
|
||||
viewChatItemInfo (AChatItem _ msgDir _ ChatItem {meta = CIMeta {itemTs, itemTimed, createdAt, itemForwarded}}) ChatItemInfo {itemVersions, forwardedFromChatItem} tz =
|
||||
["sent at: " <> ts itemTs]
|
||||
<> receivedAt
|
||||
<> toBeDeletedAt
|
||||
@@ -822,7 +822,10 @@ viewChatItemInfo (AChatItem _ msgDir _ ChatItem {meta = CIMeta {itemTs, itemTime
|
||||
(SMDRcv, GroupChat gInfo _scopeInfo) -> Just $ "#" <> viewGroupName gInfo
|
||||
_ -> Nothing
|
||||
fwdItemId = "chat item id: " <> (T.pack . show $ aChatItemId fwdACI)
|
||||
_ -> []
|
||||
_ -> case itemForwarded of
|
||||
Just (CIFFGroup g _ _ _ _ _ _) -> ["forwarded from: #" <> (plain . viewName) g]
|
||||
Just (CIFFGroupLink g _ _ _ _ _ _) -> ["forwarded from: #" <> (plain . viewName) g]
|
||||
_ -> []
|
||||
|
||||
localTs :: TimeZone -> UTCTime -> String
|
||||
localTs tz ts = do
|
||||
@@ -1010,8 +1013,9 @@ forwardedFrom = \case
|
||||
CIFFUnknown -> ["-> forwarded"]
|
||||
CIFFContact c MDSnd _ _ -> ["<- you @" <> (plain . viewName) c]
|
||||
CIFFContact c MDRcv _ _ -> ["<- @" <> (plain . viewName) c]
|
||||
CIFFGroup g MDSnd _ _ -> ["<- you #" <> (plain . viewName) g]
|
||||
CIFFGroup g MDRcv _ _ -> ["<- #" <> (plain . viewName) g]
|
||||
CIFFGroup g MDSnd _ _ _ _ _ -> ["<- you #" <> (plain . viewName) g]
|
||||
CIFFGroup g MDRcv _ _ _ _ _ -> ["<- #" <> (plain . viewName) g]
|
||||
CIFFGroupLink g _ _ _ _ _ _ -> ["<- #" <> (plain . viewName) g]
|
||||
|
||||
sentByMember :: GroupInfo -> CIQDirection 'CTGroup -> Maybe GroupMember
|
||||
sentByMember GroupInfo {membership} = \case
|
||||
|
||||
Reference in new issue
Block a user