diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index 62e282206..b656950eb 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -15,6 +15,7 @@ private let memberImageSize: CGFloat = 34 struct ChatView: View { @EnvironmentObject var chatModel: ChatModel @Environment(\.colorScheme) var colorScheme + @Environment(\.dismiss) var dismiss @State @ObservedObject var chat: Chat @State private var showChatInfoSheet: Bool = false @State private var showAddMembersSheet: Bool = false @@ -70,6 +71,9 @@ struct ChatView: View { } } } + .onChange(of: chatModel.chatId) { _ in + if chatModel.chatId == nil { dismiss() } + } .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button { diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index ef485f308..638ce711b 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -18,7 +18,13 @@ struct ChatListView: View { var body: some View { ZStack(alignment: .topLeading) { - NavigationView { + NavStackCompat( + isActive: Binding( + get: { ChatModel.shared.chatId != nil }, + set: { _ in } + ), + destination: chatView + ) { VStack { if chatModel.chats.isEmpty { onboardingButtons() @@ -30,7 +36,6 @@ struct ChatListView: View { } } } - .navigationViewStyle(.stack) if userPickerVisible { Rectangle().fill(.white.opacity(0.001)).onTapGesture { withAnimation { @@ -111,15 +116,6 @@ struct ChatListView: View { .sheet(isPresented: $showSettings) { SettingsView(showSettings: $showSettings) } - .background( - NavigationLink( - destination: chatView(), - isActive: Binding( - get: { chatModel.chatId != nil }, - set: { _ in } - ) - ) { EmptyView() } - ) } private func unreadBadge(_ text: Text? = Text(" "), size: CGFloat = 18) -> some View { diff --git a/apps/ios/Shared/Views/Helpers/NavStackCompat.swift b/apps/ios/Shared/Views/Helpers/NavStackCompat.swift new file mode 100644 index 000000000..6e3b89c9b --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/NavStackCompat.swift @@ -0,0 +1,43 @@ +// +// NavStackCompat.swift +// SimpleX (iOS) +// +// Created by Evgeny on 23/01/2023. +// Copyright © 2023 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +struct NavStackCompat : View { + let isActive: Binding + let destination: () -> D + let content: () -> C + + var body: some View { + if #available(iOS 16, *) { + NavigationStack(path: Binding( + get: { isActive.wrappedValue ? [true] : [] }, + set: { _ in } + )) { + ZStack { + NavigationLink(value: true) { EmptyView() } + content() + } + .navigationDestination(for: Bool.self) { show in + if show { destination() } + } + } + } else { + NavigationView { + ZStack { + NavigationLink( + destination: destination(), + isActive: isActive + ) { EmptyView() } + content() + } + } + .navigationViewStyle(.stack) + } + } +} diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 4853819fb..8ff1e5b47 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -112,6 +112,7 @@ 5CC2C0FC2809BF11000C35E3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FA2809BF11000C35E3 /* Localizable.strings */; }; 5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */; }; 5CCA7DF32905735700C8FEBA /* AcceptRequestsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCA7DF22905735700C8FEBA /* AcceptRequestsView.swift */; }; + 5CCB939C297EFCB100399E78 /* NavStackCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */; }; 5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; }; 5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */; }; 5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CDCAD472818589900503DA2 /* NotificationService.swift */; }; @@ -345,6 +346,7 @@ 5CC2C0FB2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; 5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = ""; }; 5CCA7DF22905735700C8FEBA /* AcceptRequestsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AcceptRequestsView.swift; sourceTree = ""; }; + 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavStackCompat.swift; sourceTree = ""; }; 5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = ""; }; 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanToConnectView.swift; sourceTree = ""; }; 5CDCAD452818589900503DA2 /* SimpleX NSE.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "SimpleX NSE.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -543,6 +545,7 @@ 5C00164328A26FBC0094D739 /* ContextMenu.swift */, 5CA7DFC229302AF000F7FDDE /* AppSheet.swift */, 18415A7F0F189D87DEFEABCA /* PressedButtonStyle.swift */, + 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */, ); path = Helpers; sourceTree = ""; @@ -1057,6 +1060,7 @@ 5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */, 5C9C2DA52894777E00CC63B1 /* GroupProfileView.swift in Sources */, 5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */, + 5CCB939C297EFCB100399E78 /* NavStackCompat.swift in Sources */, 5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */, 5C971E1D27AEBEF600C8A3CE /* ChatInfoView.swift in Sources */, 5CBD285C29575B8E00EC2CF4 /* WhatsNewView.swift in Sources */, diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 1329d0cab..2f8d5e750 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -2667,7 +2667,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do (filePath, fileStatus) <- case inline of Just IFMSent -> do fPath <- getRcvFilePath fileId Nothing fileName - withStore' $ \db -> startRcvInlineFT db user ft fPath + withStore' $ \db -> startRcvInlineFT db user ft fPath inline pure (Just fPath, CIFSRcvAccepted) _ -> pure (Nothing, CIFSRcvInvitation) pure CIFile {fileId, fileName, fileSize, filePath, fileStatus} @@ -3413,9 +3413,9 @@ cancelRcvFileTransfer user ft@RcvFileTransfer {fileId, fileStatus, rcvFileInline updateRcvFileStatus db ft FSCancelled deleteRcvFileChunks db ft when (isNothing rcvFileInline) $ case fileStatus of - RFSAccepted RcvFileInfo {connId, agentConnId} -> + RFSAccepted RcvFileInfo {connId = Just connId, agentConnId = Just agentConnId} -> deleteAgentConnectionAsync' user connId agentConnId - RFSConnected RcvFileInfo {connId, agentConnId} -> + RFSConnected RcvFileInfo {connId = Just connId, agentConnId = Just agentConnId} -> deleteAgentConnectionAsync' user connId agentConnId _ -> pure () @@ -3425,7 +3425,7 @@ cancelSndFile user FileTransferMeta {fileId} fts sendCancel = do forM_ fts $ \ft' -> cancelSndFileTransfer user ft' sendCancel cancelSndFileTransfer :: ChatMonad m => User -> SndFileTransfer -> Bool -> m () -cancelSndFileTransfer user ft@SndFileTransfer {connId, agentConnId = agentConnId@(AgentConnId acId), fileStatus} sendCancel = +cancelSndFileTransfer user ft@SndFileTransfer {connId, agentConnId = agentConnId@(AgentConnId acId), fileStatus, fileInline} sendCancel = unless (fileStatus == FSCancelled || fileStatus == FSComplete) $ do withStore' $ \db -> do updateSndFileStatus db ft FSCancelled @@ -3433,7 +3433,7 @@ cancelSndFileTransfer user ft@SndFileTransfer {connId, agentConnId = agentConnId when sendCancel $ withAgent (\a -> void (sendMessage a acId SMP.noMsgFlags $ smpEncode FileChunkCancel)) `catchError` (toView . CRChatError (Just user)) - deleteAgentConnectionAsync' user connId agentConnId + when (isNothing fileInline) $ deleteAgentConnectionAsync' user connId agentConnId closeFileHandle :: ChatMonad m => Int64 -> (ChatController -> TVar (Map Int64 Handle)) -> m () closeFileHandle fileId files = do diff --git a/src/Simplex/Chat/Migrations/M20221012_inline_files.hs b/src/Simplex/Chat/Migrations/M20221012_inline_files.hs index 51133d6b6..4b069f088 100644 --- a/src/Simplex/Chat/Migrations/M20221012_inline_files.hs +++ b/src/Simplex/Chat/Migrations/M20221012_inline_files.hs @@ -10,10 +10,10 @@ m20221012_inline_files = [sql| DROP INDEX idx_messages_direct_shared_msg_id; -ALTER TABLE files ADD COLUMN file_inline TEXT; -ALTER TABLE rcv_files ADD COLUMN rcv_file_inline TEXT; -ALTER TABLE rcv_files ADD COLUMN file_inline TEXT; -ALTER TABLE snd_files ADD COLUMN file_inline TEXT; +ALTER TABLE files ADD COLUMN file_inline TEXT; -- based on offer, determined by file sender for both sides +ALTER TABLE rcv_files ADD COLUMN rcv_file_inline TEXT; -- actual mode when receiving file, determined when invitation is accepted +ALTER TABLE rcv_files ADD COLUMN file_inline TEXT; -- based on offer, determined when invitation is processed +ALTER TABLE snd_files ADD COLUMN file_inline TEXT; -- actual mode when sending file, determined when invitation is accepted ALTER TABLE snd_files ADD COLUMN last_inline_msg_delivery_id INTEGER; CREATE UNIQUE INDEX idx_snd_files_last_inline_msg_delivery_id ON snd_files(last_inline_msg_delivery_id); diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index be95e5f1c..b6f24eed2 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -2859,14 +2859,14 @@ getRcvFileTransferById db fileId = do (user,) <$> getRcvFileTransfer db user fileId getRcvFileTransfer :: DB.Connection -> User -> FileTransferId -> ExceptT StoreError IO RcvFileTransfer -getRcvFileTransfer db user@User {userId} fileId = do +getRcvFileTransfer db User {userId} fileId = do rftRow <- ExceptT . firstRow id (SERcvFileNotFound fileId) $ DB.query db [sql| SELECT r.file_status, r.file_queue_info, r.group_member_id, f.file_name, - f.file_size, f.chunk_size, f.cancelled, cs.contact_id, cs.local_display_name, m.group_id, m.group_member_id, m.local_display_name, + f.file_size, f.chunk_size, f.cancelled, cs.local_display_name, m.local_display_name, f.file_path, r.file_inline, r.rcv_file_inline, c.connection_id, c.agent_conn_id FROM rcv_files r JOIN files f USING (file_id) @@ -2879,11 +2879,11 @@ getRcvFileTransfer db user@User {userId} fileId = do rcvFileTransfer rftRow where rcvFileTransfer :: - (FileStatus, Maybe ConnReqInvitation, Maybe Int64, String, Integer, Integer, Maybe Bool) :. (Maybe Int64, Maybe ContactName, Maybe Int64, Maybe Int64, Maybe ContactName, Maybe FilePath, Maybe InlineFileMode, Maybe InlineFileMode) :. (Maybe Int64, Maybe AgentConnId) -> + (FileStatus, Maybe ConnReqInvitation, Maybe Int64, String, Integer, Integer, Maybe Bool) :. (Maybe ContactName, Maybe ContactName, Maybe FilePath, Maybe InlineFileMode, Maybe InlineFileMode) :. (Maybe Int64, Maybe AgentConnId) -> ExceptT StoreError IO RcvFileTransfer - rcvFileTransfer ((fileStatus', fileConnReq, grpMemberId, fileName, fileSize, chunkSize, cancelled_) :. (contactId_, contactName_, groupId_, groupMemberId_, memberName_, filePath_, fileInline, rcvFileInline) :. (connId_, agentConnId_)) = do + rcvFileTransfer ((fileStatus', fileConnReq, grpMemberId, fileName, fileSize, chunkSize, cancelled_) :. (contactName_, memberName_, filePath_, fileInline, rcvFileInline) :. (connId_, agentConnId_)) = do let fileInv = FileInvitation {fileName, fileSize, fileConnReq, fileInline} - fileInfo = (filePath_, connId_, agentConnId_, contactId_, groupId_, groupMemberId_, isJust fileInline) + fileInfo = (filePath_, connId_, agentConnId_) case contactName_ <|> memberName_ of Nothing -> throwError $ SERcvFileInvalid fileId Just name -> do @@ -2898,22 +2898,14 @@ getRcvFileTransfer db user@User {userId} fileId = do RcvFileTransfer {fileId, fileInvitation, fileStatus, rcvFileInline, senderDisplayName, chunkSize, cancelled, grpMemberId} rfi fileInfo = maybe (throwError $ SERcvFileInvalid fileId) pure =<< rfi_ fileInfo rfi_ = \case - (Just filePath, Just connId, Just agentConnId, _, _, _, _) -> pure $ Just RcvFileInfo {filePath, connId, agentConnId} - (Just filePath, Nothing, Nothing, Just contactId, _, _, True) -> do - Contact {activeConn = Connection {connId, agentConnId}} <- getContact db user contactId - pure $ Just RcvFileInfo {filePath, connId, agentConnId} - (Just filePath, Nothing, Nothing, _, Just groupId, Just groupMemberId, True) -> do - getGroupMember db user groupId groupMemberId >>= \case - GroupMember {activeConn = Just Connection {connId, agentConnId}} -> - pure $ Just RcvFileInfo {filePath, connId, agentConnId} - _ -> pure Nothing + (Just filePath, connId, agentConnId) -> pure $ Just RcvFileInfo {filePath, connId, agentConnId} _ -> pure Nothing cancelled = fromMaybe False cancelled_ acceptRcvFileTransfer :: DB.Connection -> User -> Int64 -> (CommandId, ConnId) -> ConnStatus -> FilePath -> ExceptT StoreError IO AChatItem acceptRcvFileTransfer db user@User {userId} fileId (cmdId, acId) connStatus filePath = ExceptT $ do currentTs <- getCurrentTime - acceptRcvFT_ db user fileId filePath currentTs + acceptRcvFT_ db user fileId filePath Nothing currentTs DB.execute db "INSERT INTO connections (agent_conn_id, conn_status, conn_type, rcv_file_id, user_id, created_at, updated_at) VALUES (?,?,?,?,?,?,?)" @@ -2933,23 +2925,23 @@ getContactByFileId db user@User {userId} fileId = do acceptRcvInlineFT :: DB.Connection -> User -> Int64 -> FilePath -> ExceptT StoreError IO AChatItem acceptRcvInlineFT db user fileId filePath = do - liftIO $ acceptRcvFT_ db user fileId filePath =<< getCurrentTime + liftIO $ acceptRcvFT_ db user fileId filePath (Just IFMOffer) =<< getCurrentTime getChatItemByFileId db user fileId -startRcvInlineFT :: DB.Connection -> User -> RcvFileTransfer -> FilePath -> IO () -startRcvInlineFT db user RcvFileTransfer {fileId} filePath = - acceptRcvFT_ db user fileId filePath =<< getCurrentTime +startRcvInlineFT :: DB.Connection -> User -> RcvFileTransfer -> FilePath -> Maybe InlineFileMode -> IO () +startRcvInlineFT db user RcvFileTransfer {fileId} filePath rcvFileInline = + acceptRcvFT_ db user fileId filePath rcvFileInline =<< getCurrentTime -acceptRcvFT_ :: DB.Connection -> User -> Int64 -> FilePath -> UTCTime -> IO () -acceptRcvFT_ db User {userId} fileId filePath currentTs = do +acceptRcvFT_ :: DB.Connection -> User -> Int64 -> FilePath -> Maybe InlineFileMode -> UTCTime -> IO () +acceptRcvFT_ db User {userId} fileId filePath rcvFileInline currentTs = do DB.execute db "UPDATE files SET file_path = ?, ci_file_status = ?, updated_at = ? WHERE user_id = ? AND file_id = ?" (filePath, CIFSRcvAccepted, currentTs, userId, fileId) DB.execute db - "UPDATE rcv_files SET file_status = ?, updated_at = ? WHERE file_id = ?" - (FSAccepted, currentTs, fileId) + "UPDATE rcv_files SET rcv_file_inline = ?, file_status = ?, updated_at = ? WHERE file_id = ?" + (rcvFileInline, FSAccepted, currentTs, fileId) updateRcvFileStatus :: DB.Connection -> RcvFileTransfer -> FileStatus -> IO () updateRcvFileStatus db RcvFileTransfer {fileId} status = do diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index f79d37211..fb1eb7f55 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -1518,8 +1518,8 @@ instance ToJSON RcvFileStatus where data RcvFileInfo = RcvFileInfo { filePath :: FilePath, - connId :: Int64, - agentConnId :: AgentConnId + connId :: Maybe Int64, + agentConnId :: Maybe AgentConnId } deriving (Eq, Show, Generic) @@ -1531,7 +1531,8 @@ liveRcvFileTransferConnId RcvFileTransfer {fileStatus} = case fileStatus of RFSConnected fi -> acId fi _ -> Nothing where - acId RcvFileInfo {agentConnId = AgentConnId cId} = Just cId + acId RcvFileInfo {agentConnId = Just (AgentConnId cId)} = Just cId + acId _ = Nothing newtype AgentConnId = AgentConnId ConnId deriving (Eq, Show)