diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift index 1445ea3dff..170f2d7cd5 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift @@ -24,7 +24,7 @@ struct CIFileView: View { .padding(.top, 5) .padding(.bottom, 3) if let file = file { - let prettyFileSize = ByteCountFormatter().string(fromByteCount: file.fileSize) + let prettyFileSize = ByteCountFormatter.string(fromByteCount: file.fileSize, countStyle: .binary) VStack(alignment: .leading, spacing: 2) { Text(file.fileName) .lineLimit(1) @@ -56,7 +56,7 @@ struct CIFileView: View { case .sndComplete: return false case .sndCancelled: return false case .rcvInvitation: return true - case .rcvAccepted: return true + case .rcvAccepted: return file.isSMP case .rcvTransfer: return false case .rcvComplete: return true case .rcvCancelled: return false @@ -67,7 +67,7 @@ struct CIFileView: View { private func fileSizeValid() -> Bool { if let file = file { - return file.fileSize <= MAX_FILE_SIZE + return file.fileSize <= getMaxFileSize(file.fileProtocol) } return false } @@ -85,17 +85,19 @@ struct CIFileView: View { } } } else { - let prettyMaxFileSize = ByteCountFormatter().string(fromByteCount: MAX_FILE_SIZE) + let prettyMaxFileSize = ByteCountFormatter.string(fromByteCount: getMaxFileSize(file.fileProtocol), countStyle: .binary) AlertManager.shared.showAlertMsg( title: "Large file!", message: "Your contact sent a file that is larger than currently supported maximum size (\(prettyMaxFileSize))." ) } case .rcvAccepted: - AlertManager.shared.showAlertMsg( - title: "Waiting for file", - message: "File will be received when your contact is online, please wait or check later!" - ) + if file.isSMP { + AlertManager.shared.showAlertMsg( + title: "Waiting for file", + message: "File will be received when your contact is online, please wait or check later!" + ) + } case .rcvComplete: logger.debug("CIFileView fileAction - in .rcvComplete") if let filePath = getLoadedFilePath(file) { @@ -111,8 +113,11 @@ struct CIFileView: View { if let file = file { switch file.fileStatus { case .sndStored: fileIcon("doc.fill") - // case .sndTransfer: ProgressView().frame(width: 30, height: 30) // TODO use for SMP files - case let .sndTransfer(sndProgress, sndTotal): progressCircle(sndProgress, sndTotal) + case let .sndTransfer(sndProgress, sndTotal): + switch file.fileProtocol { + case .xftp: progressCircle(sndProgress, sndTotal) + case .smp: progressView() + } case .sndComplete: fileIcon("doc.fill", innerIcon: "checkmark", innerIconSize: 10) case .sndCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10) case .rcvInvitation: @@ -122,8 +127,12 @@ struct CIFileView: View { fileIcon("doc.fill", color: .orange, innerIcon: "exclamationmark", innerIconSize: 12) } case .rcvAccepted: fileIcon("doc.fill", innerIcon: "ellipsis", innerIconSize: 12) - // case .rcvTransfer: ProgressView().frame(width: 30, height: 30) // TODO use for SMP files - case let .rcvTransfer(rcvProgress, rcvTotal): progressCircle(rcvProgress, rcvTotal) + case let .rcvTransfer(rcvProgress, rcvTotal): + if file.fileProtocol == .xftp && rcvProgress < rcvTotal { + progressCircle(rcvProgress, rcvTotal) + } else { + progressView() + } case .rcvComplete: fileIcon("doc.fill") case .rcvCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10) } @@ -152,6 +161,10 @@ struct CIFileView: View { } } + private func progressView() -> some View { + ProgressView().frame(width: 30, height: 30) + } + private func progressCircle(_ progress: Int64, _ total: Int64) -> some View { Circle() .trim(from: 0, to: Double(progress) / Double(total)) diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift index a32dd95286..a52cd72a22 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift @@ -41,10 +41,12 @@ struct CIImageView: View { // TODO image accepted alert? } case .rcvAccepted: - AlertManager.shared.showAlertMsg( - title: "Waiting for image", - message: "Image will be received when your contact is online, please wait or check later!" - ) + if file.isSMP { + AlertManager.shared.showAlertMsg( + title: "Waiting for image", + message: "Image will be received when your contact is online, please wait or check later!" + ) + } case .rcvTransfer: () // ? case .rcvComplete: () // ? case .rcvCancelled: () // TODO @@ -78,11 +80,7 @@ struct CIImageView: View { if let file = chatItem.file { switch file.fileStatus { case .sndTransfer: - ProgressView() - .progressViewStyle(.circular) - .frame(width: 20, height: 20) - .tint(.white) - .padding(8) + progressView() case .sndComplete: Image(systemName: "checkmark") .resizable() @@ -98,13 +96,17 @@ struct CIImageView: View { .foregroundColor(.white) .padding(11) case .rcvTransfer: - ProgressView() - .progressViewStyle(.circular) - .frame(width: 20, height: 20) - .tint(.white) - .padding(8) + progressView() default: EmptyView() } } } + + private func progressView() -> some View { + ProgressView() + .progressViewStyle(.circular) + .frame(width: 20, height: 20) + .tint(.white) + .padding(8) + } } diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift index 6b3bb48ed4..b06cac50b5 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift @@ -229,6 +229,8 @@ struct ComposeView: View { @State var pendingLinkUrl: URL? = nil @State var cancelledLinks: Set = [] + @AppStorage(GROUP_DEFAULT_XFTP_SEND_ENABLED, store: groupDefaults) private var xftpSendEnabled = false + @State private var showChooseSource = false @State private var showImagePicker = false @State private var showTakePhoto = false @@ -394,10 +396,10 @@ struct ComposeView: View { } fileURL.stopAccessingSecurityScopedResource() if let fileSize = fileSize, - fileSize <= MAX_FILE_SIZE { + fileSize <= maxFileSize { composeState = composeState.copy(preview: .filePreview(fileName: fileURL.lastPathComponent, file: fileURL)) } else { - let prettyMaxFileSize = ByteCountFormatter().string(fromByteCount: MAX_FILE_SIZE) + let prettyMaxFileSize = ByteCountFormatter.string(fromByteCount: maxFileSize, countStyle: .binary) AlertManager.shared.showAlertMsg( title: "Large file!", message: "Currently maximum supported file size is \(prettyMaxFileSize)." @@ -447,6 +449,11 @@ struct ComposeView: View { } } + private var maxFileSize: Int64 { + let fileProtocol: FileProtocol = xftpSendEnabled ? .xftp : .smp + return getMaxFileSize(fileProtocol) + } + private func sendLiveMessage() async { let typedMsg = composeState.message let lm = composeState.liveMessage diff --git a/apps/ios/Shared/Views/Database/DatabaseView.swift b/apps/ios/Shared/Views/Database/DatabaseView.swift index 11b5ef25db..d7b02baa62 100644 --- a/apps/ios/Shared/Views/Database/DatabaseView.swift +++ b/apps/ios/Shared/Views/Database/DatabaseView.swift @@ -185,7 +185,7 @@ struct DatabaseView: View { if fileCount == 0 { Text("No received or sent files") } else { - Text("\(fileCount) file(s) with total size of \(ByteCountFormatter().string(fromByteCount: Int64(size)))") + Text("\(fileCount) file(s) with total size of \(ByteCountFormatter.string(fromByteCount: Int64(size), countStyle: .binary))") } } } diff --git a/apps/ios/Shared/Views/UserSettings/ExperimentalFeaturesView.swift b/apps/ios/Shared/Views/UserSettings/ExperimentalFeaturesView.swift index fa8be9f065..3ad672c2bc 100644 --- a/apps/ios/Shared/Views/UserSettings/ExperimentalFeaturesView.swift +++ b/apps/ios/Shared/Views/UserSettings/ExperimentalFeaturesView.swift @@ -10,7 +10,7 @@ import SwiftUI import SimpleXChat struct ExperimentalFeaturesView: View { - @AppStorage(GROUP_DEFAULT_XFTP_SEND_ENABLED, store: UserDefaults(suiteName: APP_GROUP_NAME)!) private var xftpSendEnabled = false + @AppStorage(GROUP_DEFAULT_XFTP_SEND_ENABLED, store: groupDefaults) private var xftpSendEnabled = false var body: some View { List { diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index d58347e31f..f1e9ba0a4e 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -2215,9 +2215,17 @@ public struct CIFile: Decodable { public var fileSize: Int64 public var filePath: String? public var fileStatus: CIFileStatus + public var fileProtocol: FileProtocol public static func getSample(fileId: Int64 = 1, fileName: String = "test.txt", fileSize: Int64 = 100, filePath: String? = "test.txt", fileStatus: CIFileStatus = .rcvComplete) -> CIFile { - CIFile(fileId: fileId, fileName: fileName, fileSize: fileSize, filePath: filePath, fileStatus: fileStatus) + CIFile(fileId: fileId, fileName: fileName, fileSize: fileSize, filePath: filePath, fileStatus: fileStatus, fileProtocol: .xftp) + } + + public var isSMP: Bool { + switch self.fileProtocol { + case .smp: return true + default: return false + } } public var loaded: Bool { @@ -2237,6 +2245,11 @@ public struct CIFile: Decodable { } } +public enum FileProtocol: String, Decodable { + case smp = "smp" + case xftp = "xftp" +} + public enum CIFileStatus: Decodable { case sndStored case sndTransfer(sndProgress: Int64, sndTotal: Int64) diff --git a/apps/ios/SimpleXChat/FileUtils.swift b/apps/ios/SimpleXChat/FileUtils.swift index 09cc0b9965..3b183cad25 100644 --- a/apps/ios/SimpleXChat/FileUtils.swift +++ b/apps/ios/SimpleXChat/FileUtils.swift @@ -16,8 +16,9 @@ public let MAX_IMAGE_SIZE: Int64 = 236700 public let MAX_IMAGE_SIZE_AUTO_RCV: Int64 = MAX_IMAGE_SIZE * 2 -//public let MAX_FILE_SIZE_SMP: Int64 = 8000000 // TODO distinguish between XFTP and SMP files -public let MAX_FILE_SIZE: Int64 = 1_073_741_824 +public let MAX_FILE_SIZE_XFTP: Int64 = 1_073_741_824 + +public let MAX_FILE_SIZE_SMP: Int64 = 8000000 public let MAX_VOICE_MESSAGE_LENGTH = TimeInterval(30) @@ -43,7 +44,6 @@ func getAppDirectory() -> URL { dbContainerGroupDefault.get() == .group ? getGroupContainerDirectory() : getDocumentsDirectory() -// getDocumentsDirectory() } let DB_FILE_PREFIX = "simplex_v1" @@ -189,3 +189,10 @@ public func removeFile(_ fileName: String) { logger.error("FileUtils.removeFile error: \(error.localizedDescription)") } } + +public func getMaxFileSize(_ fileProtocol: FileProtocol) -> Int64 { + switch fileProtocol { + case .xftp: return MAX_FILE_SIZE_XFTP + case .smp: return MAX_FILE_SIZE_SMP + } +} diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 9db725d880..0fc3693dc2 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -476,7 +476,7 @@ processChatCommand = \case fileStatus <- case fileInline of Just IFMSent -> createSndDirectInlineFT db ct ft $> CIFSSndTransfer 0 1 _ -> pure CIFSSndStored - let ciFile = CIFile {fileId, fileName, fileSize, filePath = Just file, fileStatus} + let ciFile = CIFile {fileId, fileName, fileSize, filePath = Just file, fileStatus, fileProtocol = FPSMP} pure (fileInvitation, ciFile, ft) prepareMsg :: Maybe FileInvitation -> Maybe CITimed -> m (MsgContainer, Maybe (CIQuote 'CTDirect)) prepareMsg fInv_ timed_ = case quotedItemId_ of @@ -527,7 +527,7 @@ processChatCommand = \case chSize <- asks $ fileChunkSize . config withStore' $ \db -> do ft@FileTransferMeta {fileId} <- createSndGroupFileTransfer db userId gInfo file fileInvitation chSize - let ciFile = CIFile {fileId, fileName, fileSize, filePath = Just file, fileStatus} + let ciFile = CIFile {fileId, fileName, fileSize, filePath = Just file, fileStatus, fileProtocol = FPSMP} pure (fileInvitation, ciFile, ft) sendGroupFileInline :: [GroupMember] -> SharedMsgId -> FileTransferMeta -> m () sendGroupFileInline ms sharedMsgId ft@FileTransferMeta {fileInline} = @@ -591,7 +591,7 @@ processChatCommand = \case aFileId <- withAgent $ \a -> xftpSendFile a (aUserId user) fsFilePath n -- TODO CRSndFileStart event for XFTP ft@FileTransferMeta {fileId} <- withStore' $ \db -> createSndFileTransferXFTP db user contactOrGroup file fInv $ AgentSndFileId aFileId - let ciFile = CIFile {fileId, fileName, fileSize, filePath = Just file, fileStatus = CIFSSndStored} + let ciFile = CIFile {fileId, fileName, fileSize, filePath = Just file, fileStatus = CIFSSndStored, fileProtocol = FPXFTP} case contactOrGroup of CGContact Contact {activeConn} -> withStore' $ \db -> createSndFTDescrXFTP db user Nothing activeConn ft fileDescr CGGroup (Group _ ms) -> forM_ ms $ \m -> saveMemberFD m `catchError` (toView . CRChatError (Just user)) @@ -3062,14 +3062,15 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do processFileInvitation fInv_ mc createRcvFT = forM fInv_ $ \fInv@FileInvitation {fileName, fileSize} -> do ChatConfig {fileChunkSize} <- asks config inline <- receiveInlineMode fInv (Just mc) fileChunkSize - ft@RcvFileTransfer {fileId} <- withStore $ \db -> createRcvFT db fInv inline fileChunkSize + ft@RcvFileTransfer {fileId, xftpRcvFile} <- withStore $ \db -> createRcvFT db fInv inline fileChunkSize + let fileProtocol = if isJust xftpRcvFile then FPXFTP else FPSMP (filePath, fileStatus) <- case inline of Just IFMSent -> do fPath <- getRcvFilePath fileId Nothing fileName True withStore' $ \db -> startRcvInlineFT db user ft fPath inline pure (Just fPath, CIFSRcvAccepted) _ -> pure (Nothing, CIFSRcvInvitation) - pure CIFile {fileId, fileName, fileSize, filePath, fileStatus} + pure CIFile {fileId, fileName, fileSize, filePath, fileStatus, fileProtocol} messageUpdate :: Contact -> SharedMsgId -> MsgContent -> RcvMessage -> MsgMeta -> Maybe Int -> Maybe Bool -> m () messageUpdate ct@Contact {contactId, localDisplayName = c} sharedMsgId mc msg@RcvMessage {msgId} msgMeta ttl live_ = do @@ -3197,8 +3198,9 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do checkIntegrityCreateItem (CDDirectRcv ct) msgMeta ChatConfig {fileChunkSize} <- asks config inline <- receiveInlineMode fInv Nothing fileChunkSize - RcvFileTransfer {fileId} <- withStore $ \db -> createRcvFileTransfer db userId ct fInv inline fileChunkSize - let ciFile = Just $ CIFile {fileId, fileName, fileSize, filePath = Nothing, fileStatus = CIFSRcvInvitation} + RcvFileTransfer {fileId, xftpRcvFile} <- withStore $ \db -> createRcvFileTransfer db userId ct fInv inline fileChunkSize + let fileProtocol = if isJust xftpRcvFile then FPXFTP else FPSMP + ciFile = Just $ CIFile {fileId, fileName, fileSize, filePath = Nothing, fileStatus = CIFSRcvInvitation, fileProtocol} ci <- saveRcvChatItem' user (CDDirectRcv ct) msg sharedMsgId_ msgMeta (CIRcvMsgContent $ MCFile "") ciFile Nothing False toView $ CRNewChatItem user (AChatItem SCTDirect SMDRcv (DirectChat ct) ci) whenContactNtfs user ct $ do @@ -3210,8 +3212,9 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do processGroupFileInvitation' gInfo m@GroupMember {localDisplayName = c} fInv@FileInvitation {fileName, fileSize} msg@RcvMessage {sharedMsgId_} msgMeta = do ChatConfig {fileChunkSize} <- asks config inline <- receiveInlineMode fInv Nothing fileChunkSize - RcvFileTransfer {fileId} <- withStore $ \db -> createRcvGroupFileTransfer db userId m fInv inline fileChunkSize - let ciFile = Just $ CIFile {fileId, fileName, fileSize, filePath = Nothing, fileStatus = CIFSRcvInvitation} + RcvFileTransfer {fileId, xftpRcvFile} <- withStore $ \db -> createRcvGroupFileTransfer db userId m fInv inline fileChunkSize + let fileProtocol = if isJust xftpRcvFile then FPXFTP else FPSMP + ciFile = Just $ CIFile {fileId, fileName, fileSize, filePath = Nothing, fileStatus = CIFSRcvInvitation, fileProtocol} ci <- saveRcvChatItem' user (CDGroupRcv gInfo m) msg sharedMsgId_ msgMeta (CIRcvMsgContent $ MCFile "") ciFile Nothing False groupMsgToView gInfo m ci msgMeta let g = groupName' gInfo diff --git a/src/Simplex/Chat/Messages.hs b/src/Simplex/Chat/Messages.hs index f84d1ab299..8040ea9423 100644 --- a/src/Simplex/Chat/Messages.hs +++ b/src/Simplex/Chat/Messages.hs @@ -16,6 +16,7 @@ module Simplex.Chat.Messages where import Control.Applicative ((<|>)) import Data.Aeson (FromJSON, ToJSON) import qualified Data.Aeson as J +import qualified Data.Aeson.Encoding as JE import qualified Data.Attoparsec.ByteString.Char8 as A import qualified Data.ByteString.Base64 as B64 import qualified Data.ByteString.Lazy.Char8 as LB @@ -409,7 +410,8 @@ data CIFile (d :: MsgDirection) = CIFile fileName :: String, fileSize :: Integer, filePath :: Maybe FilePath, -- local file path - fileStatus :: CIFileStatus d + fileStatus :: CIFileStatus d, + fileProtocol :: FileProtocol } deriving (Show, Generic) @@ -417,6 +419,26 @@ instance MsgDirectionI d => ToJSON (CIFile d) where toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True} toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True} +data FileProtocol = FPSMP | FPXFTP + deriving (Eq, Show, Ord) + +instance FromField FileProtocol where fromField = fromTextField_ textDecode + +instance ToField FileProtocol where toField = toField . textEncode + +instance ToJSON FileProtocol where + toJSON = J.String . textEncode + toEncoding = JE.text . textEncode + +instance TextEncoding FileProtocol where + textDecode = \case + "smp" -> Just FPSMP + "xftp" -> Just FPXFTP + _ -> Nothing + textEncode = \case + FPSMP -> "smp" + FPXFTP -> "xftp" + data CIFileStatus (d :: MsgDirection) where CIFSSndStored :: CIFileStatus 'MDSnd CIFSSndTransfer :: {sndProgress :: Int64, sndTotal :: Int64} -> CIFileStatus 'MDSnd diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index becdcbb45f..67e3d9a6e2 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -2681,8 +2681,8 @@ createSndDirectFileTransfer db userId Contact {contactId} filePath FileInvitatio currentTs <- getCurrentTime DB.execute db - "INSERT INTO files (user_id, contact_id, file_name, file_path, file_size, chunk_size, file_inline, ci_file_status, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)" - (userId, contactId, fileName, filePath, fileSize, chunkSize, fileInline, CIFSSndStored, currentTs, currentTs) + "INSERT INTO files (user_id, contact_id, file_name, file_path, file_size, chunk_size, file_inline, ci_file_status, protocol, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)" + ((userId, contactId, fileName, filePath, fileSize, chunkSize) :. (fileInline, CIFSSndStored, FPSMP, currentTs, currentTs)) fileId <- insertedRowId db forM_ acId_ $ \acId -> do Connection {connId} <- createSndFileConnection_ db userId fileId acId @@ -2708,8 +2708,8 @@ createSndGroupFileTransfer db userId GroupInfo {groupId} filePath FileInvitation currentTs <- getCurrentTime DB.execute db - "INSERT INTO files (user_id, group_id, file_name, file_path, file_size, chunk_size, file_inline, ci_file_status, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)" - (userId, groupId, fileName, filePath, fileSize, chunkSize, fileInline, CIFSSndStored, currentTs, currentTs) + "INSERT INTO files (user_id, group_id, file_name, file_path, file_size, chunk_size, file_inline, ci_file_status, protocol, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)" + ((userId, groupId, fileName, filePath, fileSize, chunkSize) :. (fileInline, CIFSSndStored, FPSMP, currentTs, currentTs)) fileId <- insertedRowId db pure FileTransferMeta {fileId, xftpSndFile = Nothing, fileName, filePath, fileSize, fileInline, chunkSize, cancelled = False} @@ -2788,8 +2788,8 @@ createSndFileTransferXFTP db User {userId} contactOrGroup filePath FileInvitatio xftpSndFile = Just XFTPSndFile {agentSndFileId, privateSndFileDescr = Nothing} DB.execute db - "INSERT INTO files (contact_id, group_id, user_id, file_name, file_path, file_size, chunk_size, agent_snd_file_id, ci_file_status, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)" - (contactAndGroupIds contactOrGroup :. (userId, fileName, filePath, fileSize, chunkSize, agentSndFileId, CIFSSndStored, currentTs, currentTs)) + "INSERT INTO files (contact_id, group_id, user_id, file_name, file_path, file_size, chunk_size, agent_snd_file_id, ci_file_status, protocol, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)" + (contactAndGroupIds contactOrGroup :. (userId, fileName, filePath, fileSize, chunkSize, agentSndFileId, CIFSSndStored, FPXFTP, currentTs, currentTs)) fileId <- insertedRowId db pure FileTransferMeta {fileId, xftpSndFile, fileName, filePath, fileSize, fileInline = Nothing, chunkSize, cancelled = False} @@ -2976,15 +2976,16 @@ deleteSndFileChunks db SndFileTransfer {fileId, connId} = createRcvFileTransfer :: DB.Connection -> UserId -> Contact -> FileInvitation -> Maybe InlineFileMode -> Integer -> ExceptT StoreError IO RcvFileTransfer createRcvFileTransfer db userId Contact {contactId, localDisplayName = c} f@FileInvitation {fileName, fileSize, fileConnReq, fileInline, fileDescr} rcvFileInline chunkSize = do currentTs <- liftIO getCurrentTime - fileId <- liftIO $ do - DB.execute - db - "INSERT INTO files (user_id, contact_id, file_name, file_size, chunk_size, file_inline, ci_file_status, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)" - (userId, contactId, fileName, fileSize, chunkSize, fileInline, CIFSRcvInvitation, currentTs, currentTs) - insertedRowId db rfd_ <- mapM (createRcvFD_ db userId currentTs) fileDescr let rfdId = (fileDescrId :: RcvFileDescr -> Int64) <$> rfd_ xftpRcvFile = (\rfd -> XFTPRcvFile {rcvFileDescription = rfd, agentRcvFileId = Nothing, agentRcvFileDeleted = False}) <$> rfd_ + fileProtocol = if isJust rfd_ then FPXFTP else FPSMP + fileId <- liftIO $ do + DB.execute + db + "INSERT INTO files (user_id, contact_id, file_name, file_size, chunk_size, file_inline, ci_file_status, protocol, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)" + (userId, contactId, fileName, fileSize, chunkSize, fileInline, CIFSRcvInvitation, fileProtocol, currentTs, currentTs) + insertedRowId db liftIO $ DB.execute db @@ -2995,15 +2996,16 @@ createRcvFileTransfer db userId Contact {contactId, localDisplayName = c} f@File createRcvGroupFileTransfer :: DB.Connection -> UserId -> GroupMember -> FileInvitation -> Maybe InlineFileMode -> Integer -> ExceptT StoreError IO RcvFileTransfer createRcvGroupFileTransfer db userId GroupMember {groupId, groupMemberId, localDisplayName = c} f@FileInvitation {fileName, fileSize, fileConnReq, fileInline, fileDescr} rcvFileInline chunkSize = do currentTs <- liftIO getCurrentTime - fileId <- liftIO $ do - DB.execute - db - "INSERT INTO files (user_id, group_id, file_name, file_size, chunk_size, file_inline, ci_file_status, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)" - (userId, groupId, fileName, fileSize, chunkSize, fileInline, CIFSRcvInvitation, currentTs, currentTs) - insertedRowId db rfd_ <- mapM (createRcvFD_ db userId currentTs) fileDescr let rfdId = (fileDescrId :: RcvFileDescr -> Int64) <$> rfd_ xftpRcvFile = (\rfd -> XFTPRcvFile {rcvFileDescription = rfd, agentRcvFileId = Nothing, agentRcvFileDeleted = False}) <$> rfd_ + fileProtocol = if isJust rfd_ then FPXFTP else FPSMP + fileId <- liftIO $ do + DB.execute + db + "INSERT INTO files (user_id, group_id, file_name, file_size, chunk_size, file_inline, ci_file_status, protocol, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)" + (userId, groupId, fileName, fileSize, chunkSize, fileInline, CIFSRcvInvitation, fileProtocol, currentTs, currentTs) + insertedRowId db liftIO $ DB.execute db @@ -3677,7 +3679,7 @@ getDirectChatPreviews_ db user@User {userId} = do -- ChatItem i.chat_item_id, i.item_ts, i.item_content, i.item_text, i.item_status, i.shared_msg_id, i.item_deleted, i.item_edited, i.created_at, i.updated_at, i.timed_ttl, i.timed_delete_at, i.item_live, -- CIFile - f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, + f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, f.protocol, -- DirectQuote ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent FROM contacts ct @@ -3742,7 +3744,7 @@ getGroupChatPreviews_ db User {userId, userContactId} = do -- ChatItem i.chat_item_id, i.item_ts, i.item_content, i.item_text, i.item_status, i.shared_msg_id, i.item_deleted, i.item_edited, i.created_at, i.updated_at, i.timed_ttl, i.timed_delete_at, i.item_live, -- CIFile - f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, + f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, f.protocol, -- Maybe GroupMember - sender m.group_member_id, m.group_id, m.member_id, m.member_role, m.member_category, m.member_status, m.invited_by, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, @@ -3907,7 +3909,7 @@ getDirectChatLast_ db user@User {userId} contactId count search = do -- ChatItem i.chat_item_id, i.item_ts, i.item_content, i.item_text, i.item_status, i.shared_msg_id, i.item_deleted, i.item_edited, i.created_at, i.updated_at, i.timed_ttl, i.timed_delete_at, i.item_live, -- CIFile - f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, + f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, f.protocol, -- DirectQuote ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent FROM chat_items i @@ -3938,7 +3940,7 @@ getDirectChatAfter_ db user@User {userId} contactId afterChatItemId count search -- ChatItem i.chat_item_id, i.item_ts, i.item_content, i.item_text, i.item_status, i.shared_msg_id, i.item_deleted, i.item_edited, i.created_at, i.updated_at, i.timed_ttl, i.timed_delete_at, i.item_live, -- CIFile - f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, + f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, f.protocol, -- DirectQuote ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent FROM chat_items i @@ -3970,7 +3972,7 @@ getDirectChatBefore_ db user@User {userId} contactId beforeChatItemId count sear -- ChatItem i.chat_item_id, i.item_ts, i.item_content, i.item_text, i.item_status, i.shared_msg_id, i.item_deleted, i.item_edited, i.created_at, i.updated_at, i.timed_ttl, i.timed_delete_at, i.item_live, -- CIFile - f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, + f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, f.protocol, -- DirectQuote ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent FROM chat_items i @@ -4338,7 +4340,7 @@ getDirectChatItem db User {userId} contactId itemId = ExceptT $ do -- ChatItem i.chat_item_id, i.item_ts, i.item_content, i.item_text, i.item_status, i.shared_msg_id, i.item_deleted, i.item_edited, i.created_at, i.updated_at, i.timed_ttl, i.timed_delete_at, i.item_live, -- CIFile - f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, + f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, f.protocol, -- DirectQuote ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent FROM chat_items i @@ -4482,7 +4484,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do -- ChatItem i.chat_item_id, i.item_ts, i.item_content, i.item_text, i.item_status, i.shared_msg_id, i.item_deleted, i.item_edited, i.created_at, i.updated_at, i.timed_ttl, i.timed_delete_at, i.item_live, -- CIFile - f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, + f.file_id, f.file_name, f.file_size, f.file_path, f.ci_file_status, f.protocol, -- GroupMember m.group_member_id, m.group_id, m.member_id, m.member_role, m.member_category, m.member_status, m.invited_by, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, @@ -4736,7 +4738,7 @@ type ChatStatsRow = (Int, ChatItemId, Bool) toChatStats :: ChatStatsRow -> ChatStats toChatStats (unreadCount, minUnreadItemId, unreadChat) = ChatStats {unreadCount, minUnreadItemId, unreadChat} -type MaybeCIFIleRow = (Maybe Int64, Maybe String, Maybe Integer, Maybe FilePath, Maybe ACIFileStatus) +type MaybeCIFIleRow = (Maybe Int64, Maybe String, Maybe Integer, Maybe FilePath, Maybe ACIFileStatus, Maybe FileProtocol) type ChatItemModeRow = (Maybe Int, Maybe UTCTime, Maybe Bool) @@ -4756,7 +4758,7 @@ toQuote (quotedItemId, quotedSharedMsgId, quotedSentAt, quotedMsgContent, _) dir CIQuote <$> dir <*> pure quotedItemId <*> pure quotedSharedMsgId <*> quotedSentAt <*> quotedMsgContent <*> (parseMaybeMarkdownList . msgContentText <$> quotedMsgContent) toDirectChatItem :: TimeZone -> UTCTime -> ChatItemRow :. QuoteRow -> Either StoreError (CChatItem 'CTDirect) -toDirectChatItem tz currentTs (((itemId, itemTs, itemContent, itemText, itemStatus, sharedMsgId, itemDeleted, itemEdited, createdAt, updatedAt) :. (timedTTL, timedDeleteAt, itemLive) :. (fileId_, fileName_, fileSize_, filePath, fileStatus_)) :. quoteRow) = +toDirectChatItem tz currentTs (((itemId, itemTs, itemContent, itemText, itemStatus, sharedMsgId, itemDeleted, itemEdited, createdAt, updatedAt) :. (timedTTL, timedDeleteAt, itemLive) :. (fileId_, fileName_, fileSize_, filePath, fileStatus_, fileProtocol_)) :. quoteRow) = case (itemContent, itemStatus, fileStatus_) of (ACIContent SMDSnd ciContent, ACIStatus SMDSnd ciStatus, Just (AFS SMDSnd fileStatus)) -> Right $ cItem SMDSnd CIDirectSnd ciStatus ciContent (maybeCIFile fileStatus) @@ -4770,8 +4772,8 @@ toDirectChatItem tz currentTs (((itemId, itemTs, itemContent, itemText, itemStat where maybeCIFile :: CIFileStatus d -> Maybe (CIFile d) maybeCIFile fileStatus = - case (fileId_, fileName_, fileSize_) of - (Just fileId, Just fileName, Just fileSize) -> Just CIFile {fileId, fileName, fileSize, filePath, fileStatus} + case (fileId_, fileName_, fileSize_, fileProtocol_) of + (Just fileId, Just fileName, Just fileSize, Just fileProtocol) -> Just CIFile {fileId, fileName, fileSize, filePath, fileStatus, fileProtocol} _ -> Nothing cItem :: MsgDirectionI d => SMsgDirection d -> CIDirection 'CTDirect d -> CIStatus d -> CIContent d -> Maybe (CIFile d) -> CChatItem 'CTDirect cItem d chatDir ciStatus content file = @@ -4803,7 +4805,7 @@ toGroupQuote qr@(_, _, _, _, quotedSent) quotedMember_ = toQuote qr $ direction direction _ _ = Nothing toGroupChatItem :: TimeZone -> UTCTime -> Int64 -> ChatItemRow :. MaybeGroupMemberRow :. GroupQuoteRow :. MaybeGroupMemberRow -> Either StoreError (CChatItem 'CTGroup) -toGroupChatItem tz currentTs userContactId (((itemId, itemTs, itemContent, itemText, itemStatus, sharedMsgId, itemDeleted, itemEdited, createdAt, updatedAt) :. (timedTTL, timedDeleteAt, itemLive) :. (fileId_, fileName_, fileSize_, filePath, fileStatus_)) :. memberRow_ :. (quoteRow :. quotedMemberRow_) :. deletedByGroupMemberRow_) = do +toGroupChatItem tz currentTs userContactId (((itemId, itemTs, itemContent, itemText, itemStatus, sharedMsgId, itemDeleted, itemEdited, createdAt, updatedAt) :. (timedTTL, timedDeleteAt, itemLive) :. (fileId_, fileName_, fileSize_, filePath, fileStatus_, fileProtocol_)) :. memberRow_ :. (quoteRow :. quotedMemberRow_) :. deletedByGroupMemberRow_) = do let member_ = toMaybeGroupMember userContactId memberRow_ quotedMember_ = toMaybeGroupMember userContactId quotedMemberRow_ deletedByGroupMember_ = toMaybeGroupMember userContactId deletedByGroupMemberRow_ @@ -4820,8 +4822,8 @@ toGroupChatItem tz currentTs userContactId (((itemId, itemTs, itemContent, itemT where maybeCIFile :: CIFileStatus d -> Maybe (CIFile d) maybeCIFile fileStatus = - case (fileId_, fileName_, fileSize_) of - (Just fileId, Just fileName, Just fileSize) -> Just CIFile {fileId, fileName, fileSize, filePath, fileStatus} + case (fileId_, fileName_, fileSize_, fileProtocol_) of + (Just fileId, Just fileName, Just fileSize, Just fileProtocol) -> Just CIFile {fileId, fileName, fileSize, filePath, fileStatus, fileProtocol} _ -> Nothing cItem :: MsgDirectionI d => SMsgDirection d -> CIDirection 'CTGroup d -> CIStatus d -> CIContent d -> Maybe GroupMember -> Maybe (CIFile d) -> Maybe GroupMember -> CChatItem 'CTGroup cItem d chatDir ciStatus content quotedMember_ file deletedByGroupMember_ =