mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-03-14 09:45:42 +00:00
core: fixes for mentions (initial chat load, update mentions, markdown) (#5603)
* core: fix mentions markdown * test * core: fix initial load for the first unread item * core: fix updating messages with mentions * fix CPP * query plans
This commit is contained in:
parent
43e374cf20
commit
82dffd55a9
6 changed files with 51 additions and 22 deletions
|
@ -1793,7 +1793,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
|||
ts@(_, ft_) = msgContentTexts mc
|
||||
live = fromMaybe False live_
|
||||
updateRcvChatItem = do
|
||||
cci <- withStore $ \db -> getGroupChatItemBySharedMsgId db user groupId groupMemberId sharedMsgId
|
||||
cci <- withStore $ \db -> getGroupChatItemBySharedMsgId db user gInfo groupMemberId sharedMsgId
|
||||
case cci of
|
||||
CChatItem SMDRcv ci@ChatItem {chatDir = CIGroupRcv m', meta = CIMeta {itemLive}, content = CIRcvMsgContent oldMC} ->
|
||||
if sameMemberId memberId m'
|
||||
|
@ -1992,9 +1992,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
|||
receiveFileChunk ft Nothing meta chunk
|
||||
|
||||
xFileCancelGroup :: GroupInfo -> GroupMember -> SharedMsgId -> CM ()
|
||||
xFileCancelGroup GroupInfo {groupId} GroupMember {groupMemberId, memberId} sharedMsgId = do
|
||||
xFileCancelGroup g@GroupInfo {groupId} GroupMember {groupMemberId, memberId} sharedMsgId = do
|
||||
fileId <- withStore $ \db -> getGroupFileIdBySharedMsgId db userId groupId sharedMsgId
|
||||
CChatItem msgDir ChatItem {chatDir} <- withStore $ \db -> getGroupChatItemBySharedMsgId db user groupId groupMemberId sharedMsgId
|
||||
CChatItem msgDir ChatItem {chatDir} <- withStore $ \db -> getGroupChatItemBySharedMsgId db user g groupMemberId sharedMsgId
|
||||
case (msgDir, chatDir) of
|
||||
(SMDRcv, CIGroupRcv m) -> do
|
||||
if sameMemberId memberId m
|
||||
|
|
|
@ -173,7 +173,7 @@ markdownP = mconcat <$> A.many' fragmentP
|
|||
'`' -> formattedP '`' Snippet
|
||||
'#' -> A.char '#' *> secretP
|
||||
'!' -> coloredP <|> wordP
|
||||
'@' -> mentionP
|
||||
'@' -> mentionP <|> wordP
|
||||
_
|
||||
| isDigit c -> phoneP <|> wordP
|
||||
| otherwise -> wordP
|
||||
|
|
|
@ -127,6 +127,7 @@ module Simplex.Chat.Store.Messages
|
|||
)
|
||||
where
|
||||
|
||||
import qualified Control.Exception as E
|
||||
import Control.Monad
|
||||
import Control.Monad.Except
|
||||
import Control.Monad.IO.Class
|
||||
|
@ -1335,8 +1336,8 @@ getGroupChatAround_ db user g contentFilter aroundId count search = do
|
|||
getGroupChatAround' db user g contentFilter aroundId count search stats
|
||||
|
||||
getGroupChatAround' :: DB.Connection -> User -> GroupInfo -> Maybe MsgContentTag -> ChatItemId -> Int -> String -> ChatStats -> ExceptT StoreError IO (Chat 'CTGroup, Maybe NavigationInfo)
|
||||
getGroupChatAround' db user g@GroupInfo {groupId} contentFilter aroundId count search stats = do
|
||||
aroundCI <- getGroupChatItem db user groupId aroundId
|
||||
getGroupChatAround' db user g contentFilter aroundId count search stats = do
|
||||
aroundCI <- getGroupCIWithReactions db user g aroundId
|
||||
let beforeRange = GRBefore (chatItemTs aroundCI) (cChatItemId aroundCI)
|
||||
afterRange = GRAfter (chatItemTs aroundCI) (cChatItemId aroundCI)
|
||||
beforeIds <- liftIO $ getGroupChatItemIDs db user g contentFilter beforeRange count search
|
||||
|
@ -2316,9 +2317,12 @@ updateGroupCIMentions db g ci@ChatItem {mentions} mentions'
|
|||
unless (null mentions) $ deleteMentions
|
||||
if null mentions'
|
||||
then pure ci
|
||||
else createGroupCIMentions db g ci mentions'
|
||||
-- This is a fallback for the error that should not happen in practice.
|
||||
-- In theory, it may happen in item mentions in database are different from item record.
|
||||
else createMentions `E.catch` \e -> if constraintError e then deleteMentions >> createMentions else E.throwIO e
|
||||
where
|
||||
deleteMentions = DB.execute db "DELETE FROM chat_item_mentions WHERE chat_item_id = ?" (Only $ chatItemId' ci)
|
||||
createMentions = createGroupCIMentions db g ci mentions'
|
||||
|
||||
deleteGroupChatItem :: DB.Connection -> User -> GroupInfo -> ChatItem 'CTGroup d -> IO ()
|
||||
deleteGroupChatItem db User {userId} g@GroupInfo {groupId} ci = do
|
||||
|
@ -2439,8 +2443,8 @@ markMessageReportsDeleted db User {userId} GroupInfo {groupId} ChatItem {meta =
|
|||
|]
|
||||
(DBCIDeleted, deletedTs, groupMemberId, currentTs, userId, groupId, MCReport_, itemSharedMsgId)
|
||||
|
||||
getGroupChatItemBySharedMsgId :: DB.Connection -> User -> GroupId -> GroupMemberId -> SharedMsgId -> ExceptT StoreError IO (CChatItem 'CTGroup)
|
||||
getGroupChatItemBySharedMsgId db user@User {userId} groupId groupMemberId sharedMsgId = do
|
||||
getGroupChatItemBySharedMsgId :: DB.Connection -> User -> GroupInfo -> 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
|
||||
|
@ -2453,7 +2457,7 @@ getGroupChatItemBySharedMsgId db user@User {userId} groupId groupMemberId shared
|
|||
LIMIT 1
|
||||
|]
|
||||
(userId, groupId, groupMemberId, sharedMsgId)
|
||||
getGroupChatItem db user groupId itemId
|
||||
getGroupCIWithReactions db user g itemId
|
||||
|
||||
getGroupMemberCIBySharedMsgId :: DB.Connection -> User -> GroupId -> MemberId -> SharedMsgId -> ExceptT StoreError IO (CChatItem 'CTGroup)
|
||||
getGroupMemberCIBySharedMsgId db user@User {userId} groupId memberId sharedMsgId = do
|
||||
|
@ -2739,8 +2743,8 @@ getAChatItemBySharedMsgId db user cd sharedMsgId = case cd of
|
|||
CDDirectRcv ct@Contact {contactId} -> do
|
||||
(CChatItem msgDir ci) <- getDirectChatItemBySharedMsgId db user contactId sharedMsgId
|
||||
pure $ AChatItem SCTDirect msgDir (DirectChat ct) ci
|
||||
CDGroupRcv g@GroupInfo {groupId} GroupMember {groupMemberId} -> do
|
||||
(CChatItem msgDir ci) <- getGroupChatItemBySharedMsgId db user groupId groupMemberId sharedMsgId
|
||||
CDGroupRcv g GroupMember {groupMemberId} -> do
|
||||
(CChatItem msgDir ci) <- getGroupChatItemBySharedMsgId db user g groupMemberId sharedMsgId
|
||||
pure $ AChatItem SCTGroup msgDir (GroupChat g) ci
|
||||
|
||||
getChatItemVersions :: DB.Connection -> ChatItemId -> IO [ChatItemVersion]
|
||||
|
|
|
@ -4887,6 +4887,10 @@ Query: DELETE FROM calls WHERE user_id = ? AND contact_id = ?
|
|||
Plan:
|
||||
SEARCH calls USING INDEX idx_calls_contact_id (contact_id=?)
|
||||
|
||||
Query: DELETE FROM chat_item_mentions WHERE chat_item_id = ?
|
||||
Plan:
|
||||
SEARCH chat_item_mentions USING COVERING INDEX idx_chat_item_mentions_chat_item_id (chat_item_id=?)
|
||||
|
||||
Query: DELETE FROM chat_item_moderations WHERE group_id = ? AND item_member_id = ? AND shared_msg_id = ?
|
||||
Plan:
|
||||
SEARCH chat_item_moderations USING COVERING INDEX idx_chat_item_moderations_group (group_id=? AND item_member_id=? AND shared_msg_id=?)
|
||||
|
|
|
@ -5964,7 +5964,13 @@ testMemberMention =
|
|||
[ alice <# "#team cath> hello @Alice",
|
||||
bob <# "#team cath> hello @Alice"
|
||||
]
|
||||
cath ##> "! #team hello @alice @bob"
|
||||
cath ##> "! #team hello @alice" -- make it a mention
|
||||
cath <# "#team [edited] hello @alice"
|
||||
concurrentlyN_
|
||||
[ alice <# "#team cath> [edited] hello @alice",
|
||||
bob <# "#team cath> [edited] hello @alice"
|
||||
]
|
||||
cath ##> "! #team hello @alice @bob" -- add a mention
|
||||
cath <# "#team [edited] hello @alice @bob"
|
||||
concurrentlyN_
|
||||
[ alice <# "#team cath> [edited] hello @alice @bob",
|
||||
|
|
|
@ -43,23 +43,26 @@ s <<== ft = T.concat (map markdownText ft) `shouldBe` s
|
|||
(<<==>>) :: Text -> MarkdownList -> Expectation
|
||||
s <<==>> ft = (s ==>> ft) >> (s <<== ft)
|
||||
|
||||
bold :: Text -> Markdown
|
||||
bold = markdown Bold
|
||||
|
||||
textFormat :: Spec
|
||||
textFormat = describe "text format (bold)" do
|
||||
it "correct markdown" do
|
||||
"this is *bold formatted* text"
|
||||
<==> "this is " <> markdown Bold "bold formatted" <> " text"
|
||||
<==> "this is " <> bold "bold formatted" <> " text"
|
||||
"*bold formatted* text"
|
||||
<==> markdown Bold "bold formatted" <> " text"
|
||||
<==> bold "bold formatted" <> " text"
|
||||
"this is *bold*"
|
||||
<==> "this is " <> markdown Bold "bold"
|
||||
<==> "this is " <> bold "bold"
|
||||
" *bold* text"
|
||||
<==> " " <> markdown Bold "bold" <> " text"
|
||||
<==> " " <> bold "bold" <> " text"
|
||||
" *bold* text"
|
||||
<==> " " <> markdown Bold "bold" <> " text"
|
||||
<==> " " <> bold "bold" <> " text"
|
||||
"this is *bold* "
|
||||
<==> "this is " <> markdown Bold "bold" <> " "
|
||||
<==> "this is " <> bold "bold" <> " "
|
||||
"this is *bold* "
|
||||
<==> "this is " <> markdown Bold "bold" <> " "
|
||||
<==> "this is " <> bold "bold" <> " "
|
||||
it "ignored as markdown" do
|
||||
"this is * unformatted * text"
|
||||
<==> "this is * unformatted * text"
|
||||
|
@ -73,9 +76,11 @@ textFormat = describe "text format (bold)" do
|
|||
<==> "this is*unformatted* text"
|
||||
"this is *unformatted text"
|
||||
<==> "this is *unformatted text"
|
||||
"*this* is *unformatted text"
|
||||
<==> bold "this" <> " is *unformatted text"
|
||||
it "ignored internal markdown" do
|
||||
"this is *long _bold_ (not italic)* text"
|
||||
<==> "this is " <> markdown Bold "long _bold_ (not italic)" <> " text"
|
||||
<==> "this is " <> bold "long _bold_ (not italic)" <> " text"
|
||||
"snippet: `this is *bold text*`"
|
||||
<==> "snippet: " <> markdown Snippet "this is *bold text*"
|
||||
|
||||
|
@ -113,6 +118,8 @@ secretText = describe "secret text" do
|
|||
<==> "this is#unformatted# text"
|
||||
"this is #unformatted text"
|
||||
<==> "this is #unformatted text"
|
||||
"*this* is #unformatted text"
|
||||
<==> bold "this" <> " is #unformatted text"
|
||||
it "ignored internal markdown" do
|
||||
"snippet: `this is #secret_text#`"
|
||||
<==> "snippet: " <> markdown Snippet "this is #secret_text#"
|
||||
|
@ -150,6 +157,8 @@ textColor = describe "text color (red)" do
|
|||
<==> "this is!1 unformatted! text"
|
||||
"this is !1 unformatted text"
|
||||
<==> "this is !1 unformatted text"
|
||||
"*this* is !1 unformatted text"
|
||||
<==> bold "this" <> " is !1 unformatted text"
|
||||
it "ignored internal markdown" do
|
||||
"this is !1 long *red* (not bold)! text"
|
||||
<==> "this is " <> red "long *red* (not bold)" <> " text"
|
||||
|
@ -179,6 +188,7 @@ textWithUri = describe "text with Uri" do
|
|||
it "ignored as markdown" do
|
||||
"_https://simplex.chat" <==> "_https://simplex.chat"
|
||||
"this is _https://simplex.chat" <==> "this is _https://simplex.chat"
|
||||
"this is https://" <==> "this is https://"
|
||||
it "SimpleX links" do
|
||||
let inv = "/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D"
|
||||
("https://simplex.chat" <> inv) <==> simplexLink XLInvitation ("simplex:" <> inv) ["smp.simplex.im"] ("https://simplex.chat" <> inv)
|
||||
|
@ -208,6 +218,7 @@ textWithEmail = describe "text with Email" do
|
|||
"this is chat @simplex.chat" <==> "this is chat " <> mention "simplex.chat" "@simplex.chat"
|
||||
"this is chat@ simplex.chat" <==> "this is chat@ simplex.chat"
|
||||
"this is chat @ simplex.chat" <==> "this is chat @ simplex.chat"
|
||||
"*this* is chat @ simplex.chat" <==> bold "this" <> " is chat @ simplex.chat"
|
||||
|
||||
phone :: Text -> Markdown
|
||||
phone = Markdown $ Just Phone
|
||||
|
@ -227,8 +238,9 @@ textWithPhone = describe "text with Phone" do
|
|||
<==> "test " <> phone "+44 (0) 7777.777.777" <> " " <> uri "https://simplex.chat" <> " test"
|
||||
it "ignored as markdown (too short)" $
|
||||
"test 077777 test" <==> "test 077777 test"
|
||||
it "ignored as markdown (double spaces)" $
|
||||
it "ignored as markdown (double spaces)" $ do
|
||||
"test 07777 777 777 test" <==> "test 07777 777 777 test"
|
||||
"*test* 07777 777 777 test" <==> bold "test" <> " 07777 777 777 test"
|
||||
|
||||
mention :: Text -> Text -> Markdown
|
||||
mention = Markdown . Just . Mention
|
||||
|
@ -243,7 +255,10 @@ textWithMentions = describe "text with mentions" do
|
|||
"hello @'alice jones'!" <==> "hello " <> mention "alice jones" "@'alice jones'" <> "!"
|
||||
it "ignored as markdown" $ do
|
||||
"hello @'alice jones!" <==> "hello @'alice jones!"
|
||||
"hello @bob @'alice jones!" <==> "hello " <> mention "bob" "@bob" <> " @'alice jones!"
|
||||
"hello @ alice!" <==> "hello @ alice!"
|
||||
"hello @bob @ alice!" <==> "hello " <> mention "bob" "@bob" <> " @ alice!"
|
||||
"hello @bob @" <==> "hello " <> mention "bob" "@bob" <> " @"
|
||||
|
||||
uri' :: Text -> FormattedText
|
||||
uri' = FormattedText $ Just Uri
|
||||
|
|
Loading…
Add table
Reference in a new issue