From f00cfa9108f44817e6579361fe101d63c4599ea2 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Thu, 30 Mar 2023 19:45:18 +0400 Subject: [PATCH] core, mobile: CRSndFileCompleteXFTP event (#2107) --- .../java/chat/simplex/app/model/SimpleXAPI.kt | 5 ++++ apps/ios/Shared/Model/SimpleXAPI.swift | 2 ++ apps/ios/SimpleXChat/APITypes.swift | 3 +++ src/Simplex/Chat.hs | 27 +++++++++++-------- src/Simplex/Chat/View.hs | 9 ++++++- tests/ChatTests/Files.hs | 12 ++++----- 6 files changed, 40 insertions(+), 18 deletions(-) diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt index ebc0c7e22..554a900b5 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt @@ -1440,6 +1440,8 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a chatItemSimpleUpdate(r.user, r.chatItem) is CR.SndFileProgressXFTP -> chatItemSimpleUpdate(r.user, r.chatItem) + is CR.SndFileCompleteXFTP -> + chatItemSimpleUpdate(r.user, r.chatItem) is CR.CallInvitation -> { chatModel.callManager.reportNewIncomingCall(r.callInvitation) } @@ -3077,6 +3079,7 @@ sealed class CR { @Serializable @SerialName("sndFileCancelled") class SndFileCancelled(val user: User, val chatItem: AChatItem, val fileTransferMeta: FileTransferMeta, val sndFileTransfers: List): CR() @Serializable @SerialName("sndFileRcvCancelled") class SndFileRcvCancelled(val user: User, val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR() @Serializable @SerialName("sndFileProgressXFTP") class SndFileProgressXFTP(val user: User, val chatItem: AChatItem, val fileTransferMeta: FileTransferMeta, val sentSize: Long, val totalSize: Long): CR() + @Serializable @SerialName("sndFileCompleteXFTP") class SndFileCompleteXFTP(val user: User, val chatItem: AChatItem, val fileTransferMeta: FileTransferMeta): CR() @Serializable @SerialName("callInvitation") class CallInvitation(val callInvitation: RcvCallInvitation): CR() @Serializable @SerialName("callOffer") class CallOffer(val user: User, val contact: Contact, val callType: CallType, val offer: WebRTCSession, val sharedKey: String? = null, val askConfirmation: Boolean): CR() @Serializable @SerialName("callAnswer") class CallAnswer(val user: User, val contact: Contact, val answer: WebRTCSession): CR() @@ -3181,6 +3184,7 @@ sealed class CR { is SndFileRcvCancelled -> "sndFileRcvCancelled" is SndFileStart -> "sndFileStart" is SndFileProgressXFTP -> "sndFileProgressXFTP" + is SndFileCompleteXFTP -> "sndFileCompleteXFTP" is CallInvitation -> "callInvitation" is CallOffer -> "callOffer" is CallAnswer -> "callAnswer" @@ -3287,6 +3291,7 @@ sealed class CR { is SndFileRcvCancelled -> withUser(user, json.encodeToString(chatItem)) is SndFileStart -> withUser(user, json.encodeToString(chatItem)) is SndFileProgressXFTP -> withUser(user, "chatItem: ${json.encodeToString(chatItem)}\nsentSize: $sentSize\ntotalSize: $totalSize") + is SndFileCompleteXFTP -> withUser(user, json.encodeToString(chatItem)) is CallInvitation -> "contact: ${callInvitation.contact.id}\ncallType: $callInvitation.callType\nsharedKey: ${callInvitation.sharedKey ?: ""}" is CallOffer -> withUser(user, "contact: ${contact.id}\ncallType: $callType\nsharedKey: ${sharedKey ?: ""}\naskConfirmation: $askConfirmation\noffer: ${json.encodeToString(offer)}") is CallAnswer -> withUser(user, "contact: ${contact.id}\nanswer: ${json.encodeToString(answer)}") diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index dc1a69e25..54d78810f 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -1357,6 +1357,8 @@ func processReceivedMsg(_ res: ChatResponse) async { chatItemSimpleUpdate(user, aChatItem) case let .sndFileProgressXFTP(user, aChatItem, _, _, _): chatItemSimpleUpdate(user, aChatItem) + case let .sndFileCompleteXFTP(user, aChatItem, _): + chatItemSimpleUpdate(user, aChatItem) case let .callInvitation(invitation): m.callInvitations[invitation.contact.id] = invitation activateCall(invitation) diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 48429538a..22eea39a7 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -459,6 +459,7 @@ public enum ChatResponse: Decodable, Error { case sndFileCancelled(user: User, chatItem: AChatItem, fileTransferMeta: FileTransferMeta, sndFileTransfers: [SndFileTransfer]) case sndFileRcvCancelled(user: User, chatItem: AChatItem, sndFileTransfer: SndFileTransfer) case sndFileProgressXFTP(user: User, chatItem: AChatItem, fileTransferMeta: FileTransferMeta, sentSize: Int64, totalSize: Int64) + case sndFileCompleteXFTP(user: User, chatItem: AChatItem, fileTransferMeta: FileTransferMeta) case callInvitation(callInvitation: RcvCallInvitation) case callOffer(user: User, contact: Contact, callType: CallType, offer: WebRTCSession, sharedKey: String?, askConfirmation: Bool) case callAnswer(user: User, contact: Contact, answer: WebRTCSession) @@ -568,6 +569,7 @@ public enum ChatResponse: Decodable, Error { case .sndFileCancelled: return "sndFileCancelled" case .sndFileRcvCancelled: return "sndFileRcvCancelled" case .sndFileProgressXFTP: return "sndFileProgressXFTP" + case .sndFileCompleteXFTP: return "sndFileCompleteXFTP" case .callInvitation: return "callInvitation" case .callOffer: return "callOffer" case .callAnswer: return "callAnswer" @@ -680,6 +682,7 @@ public enum ChatResponse: Decodable, Error { case let .sndFileCancelled(u, chatItem, _, _): return withUser(u, String(describing: chatItem)) case let .sndFileRcvCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem)) case let .sndFileProgressXFTP(u, chatItem, _, sentSize, totalSize): return withUser(u, "chatItem: \(String(describing: chatItem))\nsentSize: \(sentSize)\ntotalSize: \(totalSize)") + case let .sndFileCompleteXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem)) case let .callInvitation(inv): return String(describing: inv) case let .callOffer(u, contact, callType, offer, sharedKey, askConfirmation): return withUser(u, "contact: \(contact.id)\ncallType: \(String(describing: callType))\nsharedKey: \(sharedKey ?? "")\naskConfirmation: \(askConfirmation)\noffer: \(String(describing: offer))") case let .callAnswer(u, contact, answer): return withUser(u, "contact: \(contact.id)\nanswer: \(String(describing: answer))") diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 774eb6895..f90af36ae 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -2311,8 +2311,10 @@ processAgentMsgSndFile _corrId aFileId msg = (_, _, SMDSnd, GroupChat g@GroupInfo {groupId}) -> do ms <- withStore' $ \db -> getGroupMembers db user g forM_ (zip rfds $ memberFTs ms) $ \mt -> sendToMember mt `catchError` (toView . CRChatError (Just user)) - -- TODO update database status and send event to view CRSndFileCompleteXFTP - pure () + ci' <- withStore $ \db -> do + liftIO $ updateCIFileStatus db user fileId CIFSSndComplete + getChatItemByFileId db user fileId + toView $ CRSndFileCompleteXFTP user ci' ft where memberFTs :: [GroupMember] -> [(Connection, SndFileTransfer)] memberFTs ms = M.elems $ M.intersectionWith (,) (M.fromList mConns') (M.fromList sfts') @@ -2329,11 +2331,10 @@ processAgentMsgSndFile _corrId aFileId msg = _ -> pure () _ -> pure () -- TODO error? SFERR e -> do - throwChatError $ CEXFTPSndFile fileId (AgentSndFileId aFileId) e -- update chat item status -- send status to view -- agentXFTPDeleteSndFile - pure () + throwChatError $ CEXFTPSndFile fileId (AgentSndFileId aFileId) e where sendFileDescription :: SndFileTransfer -> ValidFileDescription 'FRecipient -> SharedMsgId -> (ChatMsgEvent 'Json -> m (SndMessage, Int64)) -> m Int64 sendFileDescription sft rfd msgId sendMsg = do @@ -2385,10 +2386,10 @@ processAgentMsgRcvFile _corrId aFileId msg = agentXFTPDeleteRcvFile user aFileId fileId toView $ CRRcvFileComplete user ci RFERR e -> do - throwChatError $ CEXFTPRcvFile fileId (AgentRcvFileId aFileId) e -- update chat item status -- send status to view agentXFTPDeleteRcvFile user aFileId fileId + throwChatError $ CEXFTPRcvFile fileId (AgentRcvFileId aFileId) e processAgentMessageConn :: forall m. ChatMonad m => User -> ACorrId -> ConnId -> ACommand 'Agent 'AEConn -> m () processAgentMessageConn user _ agentConnId END = @@ -3292,13 +3293,17 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do checkSndInlineFTComplete :: Connection -> AgentMsgId -> m () checkSndInlineFTComplete conn agentMsgId = do - ft_ <- withStore' $ \db -> getSndFTViaMsgDelivery db user conn agentMsgId - forM_ ft_ $ \ft@SndFileTransfer {fileId} -> do - ci <- withStore $ \db -> do - liftIO $ updateSndFileStatus db ft FSComplete - liftIO $ deleteSndFileChunks db ft + sft_ <- withStore' $ \db -> getSndFTViaMsgDelivery db user conn agentMsgId + forM_ sft_ $ \sft@SndFileTransfer {fileId} -> do + ci@(AChatItem _ _ _ ChatItem {file}) <- withStore $ \db -> do + liftIO $ updateSndFileStatus db sft FSComplete + liftIO $ deleteSndFileChunks db sft updateDirectCIFileStatus db user fileId CIFSSndComplete - toView $ CRSndFileComplete user ci ft + case file of + Just CIFile {fileProtocol = FPXFTP} -> do + ft <- withStore $ \db -> getFileTransferMeta db user fileId + toView $ CRSndFileCompleteXFTP user ci ft + _ -> toView $ CRSndFileComplete user ci sft allowSendInline :: Integer -> Maybe InlineFileMode -> m Bool allowSendInline fileSize = \case diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 3104f1dd1..d1be04e1e 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -153,7 +153,7 @@ responseToView user_ ChatConfig {logLevel, testView} liveItems ts = \case CRSndFileComplete u _ ft -> ttyUser u $ sendingFile_ "completed" ft CRSndFileStartXFTP _ _ _ -> [] CRSndFileProgressXFTP _ _ _ _ _ -> [] - CRSndFileCompleteXFTP _ _ _ -> [] + CRSndFileCompleteXFTP u ci _ -> ttyUser u $ uploadedFile ci CRSndFileCancelledXFTP _ _ _ -> [] CRSndFileRcvCancelled u _ ft@SndFileTransfer {recipientDisplayName = c} -> ttyUser u [ttyContact c <> " cancelled receiving " <> sndFile ft] @@ -1064,6 +1064,13 @@ sendingFile_ :: StyledString -> SndFileTransfer -> [StyledString] sendingFile_ status ft@SndFileTransfer {recipientDisplayName = c} = [status <> " sending " <> sndFile ft <> " to " <> ttyContact c] +uploadedFile :: AChatItem -> [StyledString] +uploadedFile (AChatItem _ _ (DirectChat Contact {localDisplayName = c}) ChatItem {file = Just CIFile {fileId, fileName}, chatDir = CIDirectSnd}) = + ["uploaded " <> fileTransferStr fileId fileName <> " for " <> ttyContact c] +uploadedFile (AChatItem _ _ (GroupChat g) ChatItem {file = Just CIFile {fileId, fileName}, chatDir = CIGroupSnd}) = + ["uploaded " <> fileTransferStr fileId fileName <> " for " <> ttyGroup' g] +uploadedFile _ = ["uploaded file"] -- shouldn't happen + sndFile :: SndFileTransfer -> StyledString sndFile SndFileTransfer {fileId, fileName} = fileTransferStr fileId fileName diff --git a/tests/ChatTests/Files.hs b/tests/ChatTests/Files.hs index 98fe18e41..5887330e6 100644 --- a/tests/ChatTests/Files.hs +++ b/tests/ChatTests/Files.hs @@ -936,7 +936,7 @@ testXFTPFileTransfer = bob ##> "/fr 1 ./tests/tmp" bob <## "saving file 1 from alice to ./tests/tmp/test.pdf" -- alice <## "started sending file 1 (test.pdf) to bob" -- TODO "started uploading" ? - alice <## "completed sending file 1 (test.pdf) to bob" + alice <## "uploaded file 1 (test.pdf) for bob" bob <## "started receiving file 1 (test.pdf) from alice" bob <## "completed receiving file 1 (test.pdf) from alice" @@ -962,8 +962,8 @@ testXFTPGroupFileTransfer = cath <# "#team alice> sends file test.pdf (266.0 KiB / 272376 bytes)" cath <## "use /fr 1 [/ | ] to receive it" ] - -- alice <## "started sending file 1 (test.pdf) to bob" -- TODO "started uploading" ? - -- alice <## "completed sending file 1 (test.pdf) to bob" -- TODO "completed uploading" ? + -- alice <## "started sending file 1 (test.pdf) to #team" -- TODO "started uploading" ? + alice <## "uploaded file 1 (test.pdf) for #team" bob ##> "/fr 1 ./tests/tmp" bob @@ -1006,7 +1006,7 @@ testXFTPWithChangedConfig = bob ##> "/fr 1 ./tests/tmp" bob <## "saving file 1 from alice to ./tests/tmp/test.pdf" -- alice <## "started sending file 1 (test.pdf) to bob" -- TODO "started uploading" ? - alice <## "completed sending file 1 (test.pdf) to bob" + alice <## "uploaded file 1 (test.pdf) for bob" bob <## "started receiving file 1 (test.pdf) from alice" bob <## "completed receiving file 1 (test.pdf) from alice" @@ -1045,7 +1045,7 @@ testXFTPWithRelativePaths = bob ##> "/fr 1" bob <## "saving file 1 from alice to test.pdf" -- alice <## "started sending file 1 (test.pdf) to bob" -- TODO "started uploading" ? - alice <## "completed sending file 1 (test.pdf) to bob" + alice <## "uploaded file 1 (test.pdf) for bob" bob <## "started receiving file 1 (test.pdf) from alice" bob <## "completed receiving file 1 (test.pdf) from alice" @@ -1067,7 +1067,7 @@ testXFTPContinueRcv tmp = do bob <# "alice> sends file test.pdf (266.0 KiB / 272376 bytes)" bob <## "use /fr 1 [/ | ] to receive it" -- alice <## "started sending file 1 (test.pdf) to bob" -- TODO "started uploading" ? - alice <## "completed sending file 1 (test.pdf) to bob" + alice <## "uploaded file 1 (test.pdf) for bob" -- server is down - file is not received withTestChatCfg tmp cfg "bob" $ \bob -> do