core: file protocol field; ios: distinguish behavior and look of XFTP and SMP files (#2090)

* core: file protocol field; ios: distinguish behavior and look of XFTP and SMP files

* remove unused method

* count style

* corrections

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
spaced4ndy
2023-03-28 19:20:06 +01:00
committed by GitHub
co-authored by Evgeny Poberezkin
parent 6a99a4f1ae
commit ef05fa4905
10 changed files with 145 additions and 76 deletions
@@ -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))
@@ -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)
}
}
@@ -229,6 +229,8 @@ struct ComposeView: View {
@State var pendingLinkUrl: URL? = nil
@State var cancelledLinks: Set<String> = []
@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
@@ -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))")
}
}
}
@@ -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 {
+14 -1
View File
@@ -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)
+10 -3
View File
@@ -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
}
}
+12 -9
View File
@@ -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
+23 -1
View File
@@ -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
+34 -32
View File
@@ -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_ =