diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index b0035be242..bbe08d5934 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -364,6 +364,15 @@ processChatCommand = \case quotedItemId <- withStore $ \st -> getDirectChatItemIdByText st userId contactId msgDir (safeDecodeUtf8 quotedMsg) let mc = MCText $ safeDecodeUtf8 msg processChatCommand $ APISendMessageQuote CTDirect contactId quotedItemId mc + DeleteMessage cName deletedMsg -> withUser $ \User {userId} -> do + contactId <- withStore $ \st -> getContactIdByName st userId cName + deletedItemId <- withStore $ \st -> getDirectChatItemIdByText st userId contactId SMDSnd (safeDecodeUtf8 deletedMsg) + processChatCommand $ APIDeleteChatItem CTDirect contactId deletedItemId CIDMBroadcast + EditMessage cName editedMsg msg -> withUser $ \User {userId} -> do + contactId <- withStore $ \st -> getContactIdByName st userId cName + editedItemId <- withStore $ \st -> getDirectChatItemIdByText st userId contactId SMDSnd (safeDecodeUtf8 editedMsg) + let mc = MCText $ safeDecodeUtf8 msg + processChatCommand $ APIUpdateChatItem CTDirect contactId editedItemId mc NewGroup gProfile -> withUser $ \user -> do gVar <- asks idsDrg CRGroupCreated <$> withStore (\st -> createNewGroup st gVar user gProfile) @@ -444,6 +453,15 @@ processChatCommand = \case quotedItemId <- withStore $ \st -> getGroupChatItemIdByText st user groupId cName (safeDecodeUtf8 quotedMsg) let mc = MCText $ safeDecodeUtf8 msg processChatCommand $ APISendMessageQuote CTGroup groupId quotedItemId mc + DeleteGroupMessage gName deletedMsg -> withUser $ \user@User {localDisplayName} -> do + groupId <- withStore $ \st -> getGroupIdByName st user gName + deletedItemId <- withStore $ \st -> getGroupChatItemIdByText st user groupId (Just localDisplayName) (safeDecodeUtf8 deletedMsg) + processChatCommand $ APIDeleteChatItem CTGroup groupId deletedItemId CIDMBroadcast + EditGroupMessage gName editedMsg msg -> withUser $ \user@User {localDisplayName} -> do + groupId <- withStore $ \st -> getGroupIdByName st user gName + editedItemId <- withStore $ \st -> getGroupChatItemIdByText st user groupId (Just localDisplayName) (safeDecodeUtf8 editedMsg) + let mc = MCText $ safeDecodeUtf8 msg + processChatCommand $ APIUpdateChatItem CTGroup groupId editedItemId mc SendFile cName f -> withUser $ \user@User {userId} -> withChatLock $ do (fileSize, chSize) <- checkSndFile f contact <- withStore $ \st -> getContactByName st userId cName @@ -1650,7 +1668,7 @@ chatCommandP = <|> ("/help files" <|> "/help file" <|> "/hf") $> ChatHelp HSFiles <|> ("/help groups" <|> "/help group" <|> "/hg") $> ChatHelp HSGroups <|> ("/help address" <|> "/ha") $> ChatHelp HSMyAddress - <|> ("/help replies" <|> "/hr") $> ChatHelp HSQuotes + <|> ("/help messages" <|> "/hm") $> ChatHelp HSMessages <|> ("/help" <|> "/h") $> ChatHelp HSMain <|> ("/group #" <|> "/group " <|> "/g #" <|> "/g ") *> (NewGroup <$> groupProfile) <|> ("/add #" <|> "/add " <|> "/a #" <|> "/a ") *> (AddMember <$> displayName <* A.space <*> displayName <*> memberRole) @@ -1663,6 +1681,8 @@ chatCommandP = <|> A.char '#' *> (SendGroupMessage <$> displayName <* A.space <*> A.takeByteString) <|> (">#" <|> "> #") *> (SendGroupMessageQuote <$> displayName <* A.space <*> pure Nothing <*> quotedMsg <*> A.takeByteString) <|> (">#" <|> "> #") *> (SendGroupMessageQuote <$> displayName <* A.space <* optional (A.char '@') <*> (Just <$> displayName) <* A.space <*> quotedMsg <*> A.takeByteString) + <|> ("\\#" <|> "\\ #") *> (DeleteGroupMessage <$> displayName <* A.space <*> A.takeByteString) + <|> ("!#" <|> "! #") *> (EditGroupMessage <$> displayName <* A.space <*> quotedMsg <*> A.takeByteString) <|> ("/contacts" <|> "/cs") $> ListContacts <|> ("/connect " <|> "/c ") *> (Connect <$> ((Just <$> strP) <|> A.takeByteString $> Nothing)) <|> ("/connect" <|> "/c") $> AddContact @@ -1670,6 +1690,8 @@ chatCommandP = <|> A.char '@' *> (SendMessage <$> displayName <* A.space <*> A.takeByteString) <|> (">@" <|> "> @") *> sendMsgQuote (AMsgDirection SMDRcv) <|> (">>@" <|> ">> @") *> sendMsgQuote (AMsgDirection SMDSnd) + <|> ("\\@" <|> "\\ @") *> (DeleteMessage <$> displayName <* A.space <*> A.takeByteString) + <|> ("!@" <|> "! @") *> (EditMessage <$> displayName <* A.space <*> quotedMsg <*> A.takeByteString) <|> "/feed " *> (SendMessageBroadcast <$> A.takeByteString) <|> ("/file #" <|> "/f #") *> (SendGroupFile <$> displayName <* A.space <*> filePath) <|> ("/file @" <|> "/file " <|> "/f @" <|> "/f ") *> (SendFile <$> displayName <* A.space <*> filePath) diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 7c468b31db..132cc5ef5e 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -79,7 +79,7 @@ data ChatController = ChatController config :: ChatConfig } -data HelpSection = HSMain | HSFiles | HSGroups | HSMyAddress | HSMarkdown | HSQuotes +data HelpSection = HSMain | HSFiles | HSGroups | HSMyAddress | HSMarkdown | HSMessages deriving (Show, Generic) instance ToJSON HelpSection where @@ -120,6 +120,8 @@ data ChatCommand | SendMessage ContactName ByteString | SendMessageQuote {contactName :: ContactName, msgDir :: AMsgDirection, quotedMsg :: ByteString, message :: ByteString} | SendMessageBroadcast ByteString + | DeleteMessage ContactName ByteString + | EditMessage {contactName :: ContactName, editedMsg :: ByteString, message :: ByteString} | NewGroup GroupProfile | AddMember GroupName ContactName GroupMemberRole | JoinGroup GroupName @@ -131,6 +133,8 @@ data ChatCommand | ListGroups | SendGroupMessage GroupName ByteString | SendGroupMessageQuote {groupName :: GroupName, contactName_ :: Maybe ContactName, quotedMsg :: ByteString, message :: ByteString} + | DeleteGroupMessage GroupName ByteString + | EditGroupMessage {groupName :: ContactName, editedMsg :: ByteString, message :: ByteString} | SendFile ContactName FilePath | SendGroupFile GroupName FilePath | ReceiveFile FileTransferId (Maybe FilePath) diff --git a/src/Simplex/Chat/Help.hs b/src/Simplex/Chat/Help.hs index 58c8fbb3e3..b3d1784eb3 100644 --- a/src/Simplex/Chat/Help.hs +++ b/src/Simplex/Chat/Help.hs @@ -7,7 +7,7 @@ module Simplex.Chat.Help filesHelpInfo, groupsHelpInfo, myAddressHelpInfo, - quotesHelpInfo, + messagesHelpInfo, markdownInfo, ) where @@ -83,7 +83,7 @@ chatHelpInfo = green "Create your address: " <> highlight "/address", "", green "Other commands:", - indent <> highlight "/help " <> " - help on: files, groups, address, replies, smp_servers", + indent <> highlight "/help " <> " - help on: messages, files, groups, address", indent <> highlight "/profile " <> " - show / update user profile", indent <> highlight "/delete " <> " - delete contact and all messages with them", indent <> highlight "/contacts " <> " - list contacts", @@ -143,8 +143,8 @@ myAddressHelpInfo = "The commands may be abbreviated: " <> listHighlight ["/ad", "/da", "/sa", "/ac", "/rc"] ] -quotesHelpInfo :: [StyledString] -quotesHelpInfo = +messagesHelpInfo :: [StyledString] +messagesHelpInfo = map styleMarkdown [ green "Sending replies to messages", @@ -152,7 +152,17 @@ quotesHelpInfo = indent <> highlight "> @alice (hi) " <> " - to reply to alice's most recent message", indent <> highlight ">> @alice (hi) " <> " - to quote user's most recent message to alice", indent <> highlight "> #team (hi) " <> " - to quote most recent message in the group from any member", - indent <> highlight "> #team @alice (hi) " <> " - to quote alice's most recent message in the group #team" + indent <> highlight "> #team @alice (hi) " <> " - to quote alice's most recent message in the group #team", + "", + green "Deleting sent messages (for everyone)", + "To delete a message that starts with \"hi\":", + indent <> highlight "\\ @alice hi " <> " - to delete your message to alice", + indent <> highlight "\\ #team hi " <> " - to delete your message in the group #team", + "", + green "Editing sent messages", + "To edit a message that starts with \"hi\":", + indent <> highlight "! @alice (hi) " <> " - to edit your message to alice", + indent <> highlight "! #team (hi) " <> " - to edit your message in the group #team" ] markdownInfo :: [StyledString] diff --git a/src/Simplex/Chat/Terminal/Input.hs b/src/Simplex/Chat/Terminal/Input.hs index 5c7059f950..cc7aa47cf5 100644 --- a/src/Simplex/Chat/Terminal/Input.hs +++ b/src/Simplex/Chat/Terminal/Input.hs @@ -101,9 +101,9 @@ updateTermState ac tw (key, ms) ts@TerminalState {inputString = s, inputPosition _ -> ts where insertCharsWithContact cs - | null s && cs /= "@" && cs /= "#" && cs /= "/" && cs /= ">" = + | null s && cs /= "@" && cs /= "#" && cs /= "/" && cs /= ">" && cs /= "\\" && cs /= "!" = insertChars $ contactPrefix <> cs - | s == ">" && cs == " " = + | (s == ">" || s == "\\" || s == "!") && cs == " " = insertChars $ cs <> contactPrefix | otherwise = insertChars cs insertChars = ts' . if p >= length s then append else insert diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index f415fc216e..f6cd89f824 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -60,7 +60,7 @@ responseToView testView = \case HSFiles -> filesHelpInfo HSGroups -> groupsHelpInfo HSMyAddress -> myAddressHelpInfo - HSQuotes -> quotesHelpInfo + HSMessages -> messagesHelpInfo HSMarkdown -> markdownInfo CRWelcome user -> chatWelcome user CRContactsList cs -> viewContactsList cs @@ -207,7 +207,7 @@ viewItemUpdate chat ChatItem {chatDir, meta, content, quotedItem} = case chat of where from = ttyFromContactEdited c quote = maybe [] (directQuote chatDir) quotedItem - CIDirectSnd -> ["item updated"] + CIDirectSnd -> ["message updated"] GroupChat g -> case chatDir of CIGroupRcv GroupMember {localDisplayName = m} -> case content of CIRcvMsgContent mc -> viewReceivedMessage from quote meta mc @@ -215,7 +215,7 @@ viewItemUpdate chat ChatItem {chatDir, meta, content, quotedItem} = case chat of where from = ttyFromGroupEdited g m quote = maybe [] (groupQuote g) quotedItem - CIGroupSnd -> ["item updated"] + CIGroupSnd -> ["message updated"] _ -> [] viewItemDelete :: ChatInfo c -> ChatItem c d -> ChatItem c' d' -> [StyledString] @@ -223,14 +223,14 @@ viewItemDelete chat ChatItem {chatDir, meta, content = deletedContent} ChatItem DirectChat Contact {localDisplayName = c} -> case (chatDir, deletedContent, toContent) of (CIDirectRcv, CIRcvMsgContent mc, CIRcvDeleted mode) -> case mode of CIDMBroadcast -> viewReceivedMessage (ttyFromContactDeleted c) [] meta mc - CIDMInternal -> ["item deleted"] - (CIDirectSnd, _, _) -> ["item deleted"] + CIDMInternal -> ["message deleted"] + (CIDirectSnd, _, _) -> ["message deleted"] _ -> [] GroupChat g -> case (chatDir, deletedContent, toContent) of (CIGroupRcv GroupMember {localDisplayName = m}, CIRcvMsgContent mc, CIRcvDeleted mode) -> case mode of CIDMBroadcast -> viewReceivedMessage (ttyFromGroupDeleted g m) [] meta mc - CIDMInternal -> ["item deleted"] - (CIGroupSnd, _, _) -> ["item deleted"] + CIDMInternal -> ["message deleted"] + (CIGroupSnd, _, _) -> ["message deleted"] _ -> [] _ -> [] diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index b8583dcd7c..ad0e238e04 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -174,7 +174,7 @@ testDirectMessageUpdate = alice #$> ("/_get chat @2 count=100", chat', [((1, "hello 🙂"), Nothing), ((0, "hi alice"), Just (1, "hello 🙂"))]) bob #$> ("/_get chat @2 count=100", chat', [((0, "hello 🙂"), Nothing), ((1, "hi alice"), Just (0, "hello 🙂"))]) - alice #$> ("/_update item @2 1 text hey 👋", id, "item updated") + alice #$> ("/_update item @2 1 text hey 👋", id, "message updated") bob <# "alice> [edited] hey 👋" alice #$> ("/_get chat @2 count=100", chat', [((1, "hey 👋"), Nothing), ((0, "hi alice"), Just (1, "hello 🙂"))]) @@ -190,7 +190,7 @@ testDirectMessageUpdate = alice #$> ("/_get chat @2 count=100", chat', [((1, "hey 👋"), Nothing), ((0, "hi alice"), Just (1, "hello 🙂")), ((0, "hey alice"), Just (1, "hey 👋"))]) bob #$> ("/_get chat @2 count=100", chat', [((0, "hey 👋"), Nothing), ((1, "hi alice"), Just (0, "hello 🙂")), ((1, "hey alice"), Just (0, "hey 👋"))]) - alice #$> ("/_update item @2 1 text greetings 🤝", id, "item updated") + alice #$> ("/_update item @2 1 text greetings 🤝", id, "message updated") bob <# "alice> [edited] greetings 🤝" alice #$> ("/_update item @2 2 text updating bob's message", id, "cannot update this item") @@ -198,11 +198,11 @@ testDirectMessageUpdate = alice #$> ("/_get chat @2 count=100", chat', [((1, "greetings 🤝"), Nothing), ((0, "hi alice"), Just (1, "hello 🙂")), ((0, "hey alice"), Just (1, "hey 👋"))]) bob #$> ("/_get chat @2 count=100", chat', [((0, "greetings 🤝"), Nothing), ((1, "hi alice"), Just (0, "hello 🙂")), ((1, "hey alice"), Just (0, "hey 👋"))]) - bob #$> ("/_update item @2 2 text hey Alice", id, "item updated") + bob #$> ("/_update item @2 2 text hey Alice", id, "message updated") alice <# "bob> [edited] > hello 🙂" alice <## " hey Alice" - bob #$> ("/_update item @2 3 text greetings Alice", id, "item updated") + bob #$> ("/_update item @2 3 text greetings Alice", id, "message updated") alice <# "bob> [edited] > hey 👋" alice <## " greetings Alice" @@ -226,8 +226,8 @@ testDirectMessageDelete = alice <# "bob> > hello 🙂" alice <## " hey alic" - alice #$> ("/_delete item @2 1 internal", id, "item deleted") - alice #$> ("/_delete item @2 2 internal", id, "item deleted") + alice #$> ("/_delete item @2 1 internal", id, "message deleted") + alice #$> ("/_delete item @2 2 internal", id, "message deleted") alice #$$> ("/_get chats", [("@bob", "")]) alice #$> ("/_get chat @2 count=100", chat, []) @@ -235,7 +235,7 @@ testDirectMessageDelete = alice #$> ("/_update item @2 1 text updating deleted message", id, "cannot update this item") alice #$> ("/_send_quote @2 1 text quoting deleted message", id, "cannot reply to this message") - bob #$> ("/_update item @2 2 text hey alice", id, "item updated") + bob #$> ("/_update item @2 2 text hey alice", id, "message updated") alice <# "bob> [edited] hey alice" alice #$$> ("/_get chats", [("@bob", "hey alice")]) @@ -245,14 +245,14 @@ testDirectMessageDelete = bob #> "@alice how are you?" alice <# "bob> how are you?" - bob #$> ("/_delete item @2 3 broadcast", id, "item deleted") + bob #$> ("/_delete item @2 3 broadcast", id, "message deleted") alice <# "bob> [deleted] how are you?" - alice #$> ("/_delete item @2 1 broadcast", id, "item deleted") + alice #$> ("/_delete item @2 1 broadcast", id, "message deleted") bob <# "alice> [deleted] hello 🙂" alice #$> ("/_delete item @2 2 broadcast", id, "cannot delete this item") - alice #$> ("/_delete item @2 2 internal", id, "item deleted") + alice #$> ("/_delete item @2 2 internal", id, "message deleted") alice #$$> ("/_get chats", [("@bob", "this item is deleted (broadcast)")]) alice #$> ("/_get chat @2 count=100", chat, [(0, "this item is deleted (broadcast)")]) @@ -752,7 +752,7 @@ testGroupMessageUpdate = (bob <# "#team alice> hello!") (cath <# "#team alice> hello!") - alice #$> ("/_update item #1 1 text hey 👋", id, "item updated") + alice #$> ("/_update item #1 1 text hey 👋", id, "message updated") concurrently_ (bob <# "#team alice> [edited] hey 👋") (cath <# "#team alice> [edited] hey 👋") @@ -780,7 +780,7 @@ testGroupMessageUpdate = bob #$> ("/_get chat #1 count=100", chat', [((0, "hey 👋"), Nothing), ((1, "hi alice"), Just (0, "hey 👋"))]) cath #$> ("/_get chat #1 count=100", chat', [((0, "hey 👋"), Nothing), ((0, "hi alice"), Just (0, "hey 👋"))]) - alice #$> ("/_update item #1 1 text greetings 🤝", id, "item updated") + alice #$> ("/_update item #1 1 text greetings 🤝", id, "message updated") concurrently_ (bob <# "#team alice> [edited] greetings 🤝") (cath <# "#team alice> [edited] greetings 🤝") @@ -816,7 +816,7 @@ testGroupMessageDelete = (bob <# "#team alice> hello!") (cath <# "#team alice> hello!") - alice #$> ("/_delete item #1 1 internal", id, "item deleted") + alice #$> ("/_delete item #1 1 internal", id, "message deleted") alice #$> ("/_get chat #1 count=100", chat, []) bob #$> ("/_get chat #1 count=100", chat, [(0, "hello!")]) @@ -844,18 +844,18 @@ testGroupMessageDelete = bob #$> ("/_get chat #1 count=100", chat', [((0, "hello!"), Nothing), ((1, "hi alic"), Just (0, "hello!"))]) cath #$> ("/_get chat #1 count=100", chat', [((0, "hello!"), Nothing), ((0, "hi alic"), Just (0, "hello!"))]) - alice #$> ("/_delete item #1 1 broadcast", id, "item deleted") + alice #$> ("/_delete item #1 1 broadcast", id, "message deleted") concurrently_ (bob <# "#team alice> [deleted] hello!") (cath <# "#team alice> [deleted] hello!") - alice #$> ("/_delete item #1 2 internal", id, "item deleted") + alice #$> ("/_delete item #1 2 internal", id, "message deleted") alice #$> ("/_get chat #1 count=100", chat', []) bob #$> ("/_get chat #1 count=100", chat', [((0, "this item is deleted (broadcast)"), Nothing), ((1, "hi alic"), Just (0, "hello!"))]) cath #$> ("/_get chat #1 count=100", chat', [((0, "this item is deleted (broadcast)"), Nothing), ((0, "hi alic"), Just (0, "hello!"))]) - bob #$> ("/_update item #1 2 text hi alice", id, "item updated") + bob #$> ("/_update item #1 2 text hi alice", id, "message updated") concurrently_ (alice <# "#team bob> [edited] hi alice") ( do @@ -874,13 +874,13 @@ testGroupMessageDelete = (alice <# "#team cath> how are you?") (bob <# "#team cath> how are you?") - cath #$> ("/_delete item #1 3 broadcast", id, "item deleted") + cath #$> ("/_delete item #1 3 broadcast", id, "message deleted") concurrently_ (alice <# "#team cath> [deleted] how are you?") (bob <# "#team cath> [deleted] how are you?") alice #$> ("/_delete item #1 2 broadcast", id, "cannot delete this item") - alice #$> ("/_delete item #1 2 internal", id, "item deleted") + alice #$> ("/_delete item #1 2 internal", id, "message deleted") alice #$> ("/_get chat #1 count=100", chat', [((0, "this item is deleted (broadcast)"), Nothing)]) bob #$> ("/_get chat #1 count=100", chat', [((0, "this item is deleted (broadcast)"), Nothing), ((1, "hi alice"), Just (0, "hello!")), ((0, "this item is deleted (broadcast)"), Nothing)])