mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-26 13:08:02 +00:00
core, ios: send_v2 api to send messages as JSON (to support filenames with spaces) (#604)
This commit is contained in:
committed by
GitHub
parent
7928cdbfb8
commit
e80f617840
@@ -50,6 +50,7 @@ enum ChatCommand {
|
||||
case let .setFilesFolder(filesFolder): return "/_files_folder \(filesFolder)"
|
||||
case .apiGetChats: return "/_get chats pcc=on"
|
||||
case let .apiGetChat(type, id): return "/_get chat \(ref(type, id)) count=100"
|
||||
// TODO replace with /_send_v2
|
||||
case let .apiSendMessage(type, id, file, quotedItemId, mc):
|
||||
switch (file, quotedItemId) {
|
||||
case (nil, nil): return "/_send \(ref(type, id)) \(mc.cmdString)"
|
||||
@@ -57,6 +58,8 @@ enum ChatCommand {
|
||||
case let (nil, .some(quotedItemId)): return "/_send \(ref(type, id)) quoted \(quotedItemId) \(mc.cmdString)"
|
||||
case let (.some(file), .some(quotedItemId)): return "/_send \(ref(type, id)) file \(file) quoted \(quotedItemId) \(mc.cmdString)"
|
||||
}
|
||||
// case let .apiSendMessage(type, id, file, quotedItemId, mc):
|
||||
// return "/_send_v2 \(ref(type, id)) \(try! jsonEncoder.encode(ComposedMessage(filePath: file, quotedItemId: quotedItemId, msgContent: mc)))"
|
||||
case let .apiUpdateChatItem(type, id, itemId, mc): return "/_update item \(ref(type, id)) \(itemId) \(mc.cmdString)"
|
||||
case let .apiDeleteChatItem(type, id, itemId, mode): return "/_delete item \(ref(type, id)) \(itemId) \(mode.rawValue)"
|
||||
case let .apiRegisterToken(token): return "/_ntf register apns \(token)"
|
||||
@@ -301,6 +304,12 @@ enum ChatResponse: Decodable, Error {
|
||||
private var noDetails: String { get { "\(responseType): no details" } }
|
||||
}
|
||||
|
||||
struct ComposedMessage: Encodable {
|
||||
var filePath: String?
|
||||
var quotedItemId: Int64?
|
||||
var msgContent: MsgContent
|
||||
}
|
||||
|
||||
private func decodeCJSON<T: Decodable>(_ cjson: UnsafePointer<CChar>) -> T? {
|
||||
let s = String.init(cString: cjson)
|
||||
let d = s.data(using: .utf8)!
|
||||
|
||||
@@ -689,12 +689,7 @@ enum MsgContent {
|
||||
get {
|
||||
switch self {
|
||||
case let .text(text): return "text \(text)"
|
||||
case let .link(text: text, preview: preview):
|
||||
return "json {\"type\":\"link\",\"text\":\(encodeJSON(text)),\"preview\":\(encodeJSON(preview))}"
|
||||
case let .image(text: text, image: image):
|
||||
return "json {\"type\":\"image\",\"text\":\(encodeJSON(text)),\"image\":\(encodeJSON(image))}"
|
||||
case let .file(text): return "json {\"type\":\"file\",\"text\":\(encodeJSON(text))}"
|
||||
default: return ""
|
||||
default: return "json \(encodeJSON(self))"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -704,7 +699,6 @@ enum MsgContent {
|
||||
case text
|
||||
case preview
|
||||
case image
|
||||
case file
|
||||
}
|
||||
}
|
||||
|
||||
@@ -739,6 +733,32 @@ extension MsgContent: Decodable {
|
||||
}
|
||||
}
|
||||
|
||||
extension MsgContent: Encodable {
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
switch self {
|
||||
case let .text(text):
|
||||
try container.encode("text", forKey: .type)
|
||||
try container.encode(text, forKey: .text)
|
||||
case let .link(text, preview):
|
||||
try container.encode("link", forKey: .type)
|
||||
try container.encode(text, forKey: .text)
|
||||
try container.encode(preview, forKey: .preview)
|
||||
case let .image(text, image):
|
||||
try container.encode("image", forKey: .type)
|
||||
try container.encode(text, forKey: .text)
|
||||
try container.encode(image, forKey: .image)
|
||||
case let .file(text):
|
||||
try container.encode("file", forKey: .type)
|
||||
try container.encode(text, forKey: .text)
|
||||
// TODO use original JSON and type
|
||||
case let .unknown(_, text):
|
||||
try container.encode("text", forKey: .type)
|
||||
try container.encode(text, forKey: .text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FormattedText: Decodable {
|
||||
var text: String
|
||||
var format: Format?
|
||||
|
||||
@@ -200,7 +200,7 @@ processChatCommand = \case
|
||||
CTContactRequest -> pure $ chatCmdError "not implemented"
|
||||
CTContactConnection -> pure $ chatCmdError "not supported"
|
||||
APIGetChatItems _pagination -> pure $ chatCmdError "not implemented"
|
||||
APISendMessage (ChatRef cType chatId) file_ quotedItemId_ mc -> withUser $ \user@User {userId} -> withChatLock $ case cType of
|
||||
APISendMessage (ChatRef cType chatId) (ComposedMessage file_ quotedItemId_ mc) -> withUser $ \user@User {userId} -> withChatLock $ case cType of
|
||||
CTDirect -> do
|
||||
ct@Contact {localDisplayName = c} <- withStore $ \st -> getContact st userId chatId
|
||||
(fileInvitation_, ciFile_) <- unzipMaybe <$> setupSndFileTransfer ct
|
||||
@@ -287,7 +287,7 @@ processChatCommand = \case
|
||||
unzipMaybe t = (fst <$> t, snd <$> t)
|
||||
-- TODO discontinue
|
||||
APISendMessageQuote chatId quotedItemId mc ->
|
||||
processChatCommand $ APISendMessage chatId Nothing (Just quotedItemId) mc
|
||||
processChatCommand . APISendMessage chatId $ ComposedMessage Nothing (Just quotedItemId) mc
|
||||
APIUpdateChatItem (ChatRef cType chatId) itemId mc -> withUser $ \user@User {userId} -> withChatLock $ case cType of
|
||||
CTDirect -> do
|
||||
(ct@Contact {contactId, localDisplayName = c}, ci) <- withStore $ \st -> (,) <$> getContact st userId chatId <*> getDirectChatItem st userId chatId itemId
|
||||
@@ -515,7 +515,7 @@ processChatCommand = \case
|
||||
SendMessage chatName msg -> withUser $ \user -> do
|
||||
chatRef <- getChatRef user chatName
|
||||
let mc = MCText $ safeDecodeUtf8 msg
|
||||
processChatCommand $ APISendMessage chatRef Nothing Nothing mc
|
||||
processChatCommand . APISendMessage chatRef $ ComposedMessage Nothing Nothing mc
|
||||
SendMessageBroadcast msg -> withUser $ \user -> do
|
||||
contacts <- withStore (`getUserContacts` user)
|
||||
withChatLock . procCmd $ do
|
||||
@@ -533,7 +533,7 @@ processChatCommand = \case
|
||||
contactId <- withStore $ \st -> getContactIdByName st userId cName
|
||||
quotedItemId <- withStore $ \st -> getDirectChatItemIdByText st userId contactId msgDir (safeDecodeUtf8 quotedMsg)
|
||||
let mc = MCText $ safeDecodeUtf8 msg
|
||||
processChatCommand $ APISendMessage (ChatRef CTDirect contactId) Nothing (Just quotedItemId) mc
|
||||
processChatCommand . APISendMessage (ChatRef CTDirect contactId) $ ComposedMessage Nothing (Just quotedItemId) mc
|
||||
DeleteMessage chatName deletedMsg -> withUser $ \user -> do
|
||||
chatRef <- getChatRef user chatName
|
||||
deletedItemId <- getSentChatItemIdByText user chatRef deletedMsg
|
||||
@@ -618,7 +618,7 @@ processChatCommand = \case
|
||||
groupId <- withStore $ \st -> getGroupIdByName st user gName
|
||||
quotedItemId <- withStore $ \st -> getGroupChatItemIdByText st user groupId cName (safeDecodeUtf8 quotedMsg)
|
||||
let mc = MCText $ safeDecodeUtf8 msg
|
||||
processChatCommand $ APISendMessage (ChatRef CTGroup groupId) Nothing (Just quotedItemId) mc
|
||||
processChatCommand . APISendMessage (ChatRef CTGroup groupId) $ ComposedMessage Nothing (Just quotedItemId) mc
|
||||
LastMessages (Just chatName) count -> withUser $ \user -> do
|
||||
chatRef <- getChatRef user chatName
|
||||
CRLastMessages . aChatItems . chat <$> (processChatCommand . APIGetChat chatRef $ CPLast count)
|
||||
@@ -626,7 +626,7 @@ processChatCommand = \case
|
||||
CRLastMessages <$> getAllChatItems st user (CPLast count)
|
||||
SendFile chatName f -> withUser $ \user -> do
|
||||
chatRef <- getChatRef user chatName
|
||||
processChatCommand $ APISendMessage chatRef (Just f) Nothing (MCFile "")
|
||||
processChatCommand . APISendMessage chatRef $ ComposedMessage (Just f) Nothing (MCFile "")
|
||||
ReceiveFile fileId filePath_ -> withUser $ \user@User {userId} ->
|
||||
withChatLock . procCmd $ do
|
||||
ft <- withStore $ \st -> getRcvFileTransfer st userId fileId
|
||||
@@ -2097,7 +2097,8 @@ chatCommandP =
|
||||
<|> "/_get chats" *> (APIGetChats <$> (" pcc=on" $> True <|> " pcc=off" $> False <|> pure False))
|
||||
<|> "/_get chat " *> (APIGetChat <$> chatRefP <* A.space <*> chatPaginationP)
|
||||
<|> "/_get items count=" *> (APIGetChatItems <$> A.decimal)
|
||||
<|> "/_send " *> (APISendMessage <$> chatRefP <*> optional filePathTagged <*> optional quotedItemIdTagged <* A.space <*> msgContentP)
|
||||
<|> "/_send " *> (APISendMessage <$> chatRefP <*> composedMsgP)
|
||||
<|> "/_send_v2 " *> (APISendMessage <$> chatRefP <*> jsonP)
|
||||
<|> "/_send_quote " *> (APISendMessageQuote <$> chatRefP <* A.space <*> A.decimal <* A.space <*> msgContentP)
|
||||
<|> "/_update item " *> (APIUpdateChatItem <$> chatRefP <* A.space <*> A.decimal <* A.space <*> msgContentP)
|
||||
<|> "/_delete item " *> (APIDeleteChatItem <$> chatRefP <* A.space <*> A.decimal <* A.space <*> ciDeleteMode)
|
||||
@@ -2203,6 +2204,7 @@ chatCommandP =
|
||||
fullNameP name = do
|
||||
n <- (A.space *> A.takeByteString) <|> pure ""
|
||||
pure $ if B.null n then name else safeDecodeUtf8 n
|
||||
composedMsgP = ComposedMessage <$> optional filePathTagged <*> optional quotedItemIdTagged <* A.space <*> msgContentP
|
||||
filePath = T.unpack . safeDecodeUtf8 <$> A.takeByteString
|
||||
filePathTagged = " file " *> (T.unpack . safeDecodeUtf8 <$> A.takeTill (== ' '))
|
||||
quotedItemIdTagged = " quoted " *> A.decimal
|
||||
|
||||
@@ -14,7 +14,7 @@ import Control.Monad.Except
|
||||
import Control.Monad.IO.Unlift
|
||||
import Control.Monad.Reader
|
||||
import Crypto.Random (ChaChaDRG)
|
||||
import Data.Aeson (ToJSON)
|
||||
import Data.Aeson (FromJSON, ToJSON)
|
||||
import qualified Data.Aeson as J
|
||||
import Data.ByteString.Char8 (ByteString)
|
||||
import Data.Int (Int64)
|
||||
@@ -103,7 +103,7 @@ data ChatCommand
|
||||
| APIGetChats {pendingConnections :: Bool}
|
||||
| APIGetChat ChatRef ChatPagination
|
||||
| APIGetChatItems Int
|
||||
| APISendMessage ChatRef (Maybe FilePath) (Maybe ChatItemId) MsgContent
|
||||
| APISendMessage ChatRef ComposedMessage
|
||||
| APISendMessageQuote ChatRef ChatItemId MsgContent -- TODO discontinue
|
||||
| APIUpdateChatItem ChatRef ChatItemId MsgContent
|
||||
| APIDeleteChatItem ChatRef ChatItemId CIDeleteMode
|
||||
@@ -298,6 +298,13 @@ instance ToJSON PendingSubStatus where
|
||||
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
|
||||
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
|
||||
|
||||
data ComposedMessage = ComposedMessage
|
||||
{ filePath :: Maybe FilePath,
|
||||
quotedItemId :: Maybe ChatItemId,
|
||||
msgContent :: MsgContent
|
||||
}
|
||||
deriving (Show, Generic, FromJSON)
|
||||
|
||||
data ChatError
|
||||
= ChatError {errorType :: ChatErrorType}
|
||||
| ChatErrorAgent {agentError :: AgentErrorType}
|
||||
|
||||
Reference in New Issue
Block a user