2022-01-29 11:10:04 +00:00
//
// C h a t A P I . s w i f t
// S i m p l e X
//
// C r e a t e d b y E v g e n y P o b e r e z k i n o n 2 7 / 0 1 / 2 0 2 2 .
// C o p y r i g h t © 2 0 2 2 S i m p l e X C h a t . A l l r i g h t s r e s e r v e d .
//
import Foundation
2022-01-29 23:37:02 +00:00
import UIKit
2022-02-09 22:53:06 +00:00
import Dispatch
import BackgroundTasks
2022-05-09 09:52:09 +01:00
import SwiftUI
2022-05-31 07:55:13 +01:00
import SimpleXChat
2022-01-29 11:10:04 +00:00
private var chatController : chat_ctrl ?
2022-01-29 23:37:02 +00:00
enum TerminalItem : Identifiable {
case cmd ( Date , ChatCommand )
case resp ( Date , ChatResponse )
var id : Date {
get {
switch self {
case let . cmd ( id , _ ) : return id
case let . resp ( id , _ ) : return id
}
}
}
var label : String {
get {
switch self {
case let . cmd ( _ , cmd ) : return " > \( cmd . cmdString . prefix ( 30 ) ) "
case let . resp ( _ , resp ) : return " < \( resp . responseType ) "
}
}
}
var details : String {
get {
switch self {
case let . cmd ( _ , cmd ) : return cmd . cmdString
case let . resp ( _ , resp ) : return resp . details
2022-01-29 11:10:04 +00:00
}
}
}
}
2022-06-19 19:49:39 +01:00
func beginBGTask ( _ handler : ( ( ) -> Void ) ? = nil ) -> ( ( ) -> Void ) {
2022-02-28 10:44:48 +00:00
var id : UIBackgroundTaskIdentifier !
var running = true
let endTask = {
// l o g g e r . d e b u g ( " b e g i n B G T a s k : e n d T a s k \ ( i d . r a w V a l u e ) " )
if running {
running = false
if let h = handler {
// l o g g e r . d e b u g ( " b e g i n B G T a s k : u s e r h a n d l e r " )
h ( )
}
if id != . invalid {
UIApplication . shared . endBackgroundTask ( id )
id = . invalid
}
}
}
id = UIApplication . shared . beginBackgroundTask ( expirationHandler : endTask )
// l o g g e r . d e b u g ( " b e g i n B G T a s k : \ ( i d . r a w V a l u e ) " )
return endTask
}
let msgDelay : Double = 7.5
let maxTaskDuration : Double = 15
2022-06-24 13:52:20 +01:00
private func withBGTask < T > ( bgDelay : Double ? = nil , f : @ escaping ( ) -> T ) -> T {
2022-02-28 10:44:48 +00:00
let endTask = beginBGTask ( )
DispatchQueue . global ( ) . asyncAfter ( deadline : . now ( ) + maxTaskDuration , execute : endTask )
let r = f ( )
if let d = bgDelay {
DispatchQueue . global ( ) . asyncAfter ( deadline : . now ( ) + d , execute : endTask )
} else {
endTask ( )
}
return r
}
func chatSendCmdSync ( _ cmd : ChatCommand , bgTask : Bool = true , bgDelay : Double ? = nil ) -> ChatResponse {
2022-02-12 15:59:43 +00:00
logger . debug ( " chatSendCmd \( cmd . cmdType ) " )
2022-02-28 10:44:48 +00:00
let resp = bgTask
2022-05-03 08:20:19 +01:00
? withBGTask ( bgDelay : bgDelay ) { sendSimpleXCmd ( cmd ) }
: sendSimpleXCmd ( cmd )
2022-02-12 15:59:43 +00:00
logger . debug ( " chatSendCmd \( cmd . cmdType ) : \( resp . responseType ) " )
2022-02-18 14:33:55 +00:00
if case let . response ( _ , json ) = resp {
logger . debug ( " chatSendCmd \( cmd . cmdType ) response: \( json ) " )
}
2022-06-24 13:52:20 +01:00
DispatchQueue . main . async {
2023-01-30 12:07:06 +00:00
ChatModel . shared . addTerminalItem ( . cmd ( . now , cmd . obfuscated ) )
ChatModel . shared . addTerminalItem ( . resp ( . now , resp ) )
2022-02-09 22:53:06 +00:00
}
return resp
2022-01-29 11:10:04 +00:00
}
2022-02-28 10:44:48 +00:00
func chatSendCmd ( _ cmd : ChatCommand , bgTask : Bool = true , bgDelay : Double ? = nil ) async -> ChatResponse {
2022-02-24 17:16:41 +00:00
await withCheckedContinuation { cont in
2022-02-28 10:44:48 +00:00
cont . resume ( returning : chatSendCmdSync ( cmd , bgTask : bgTask , bgDelay : bgDelay ) )
2022-02-24 17:16:41 +00:00
}
}
2022-06-24 13:52:20 +01:00
func chatRecvMsg ( ) async -> ChatResponse ? {
2022-02-24 17:16:41 +00:00
await withCheckedContinuation { cont in
2022-06-24 13:52:20 +01:00
_ = withBGTask ( bgDelay : msgDelay ) { ( ) -> ChatResponse ? in
let resp = recvSimpleXMsg ( )
2022-02-28 10:44:48 +00:00
cont . resume ( returning : resp )
return resp
}
2022-02-24 17:16:41 +00:00
}
2022-01-29 11:10:04 +00:00
}
2022-02-06 18:26:22 +00:00
func apiGetActiveUser ( ) throws -> User ? {
2022-02-24 17:16:41 +00:00
let r = chatSendCmdSync ( . showActiveUser )
2022-02-06 18:26:22 +00:00
switch r {
case let . activeUser ( user ) : return user
2023-01-19 16:22:56 +00:00
case . chatCmdError ( _ , . error ( . noActiveUser ) ) : return nil
2022-02-06 18:26:22 +00:00
default : throw r
}
}
func apiCreateActiveUser ( _ p : Profile ) throws -> User {
2022-02-24 17:16:41 +00:00
let r = chatSendCmdSync ( . createActiveUser ( profile : p ) )
2022-02-06 18:26:22 +00:00
if case let . activeUser ( user ) = r { return user }
throw r
}
2023-01-20 12:38:38 +00:00
func listUsers ( ) throws -> [ UserInfo ] {
2023-01-17 17:47:37 +00:00
let r = chatSendCmdSync ( . listUsers )
2023-01-20 12:38:38 +00:00
if case let . usersList ( users ) = r {
return users . sorted { $0 . user . chatViewName . compare ( $1 . user . chatViewName ) = = . orderedAscending }
}
throw r
2023-01-17 17:47:37 +00:00
}
func apiSetActiveUser ( _ userId : Int64 ) throws -> User {
let r = chatSendCmdSync ( . apiSetActiveUser ( userId : userId ) )
if case let . activeUser ( user ) = r { return user }
throw r
}
2023-01-24 16:24:34 +04:00
func apiDeleteUser ( _ userId : Int64 , _ delSMPQueues : Bool ) throws {
let r = chatSendCmdSync ( . apiDeleteUser ( userId : userId , delSMPQueues : delSMPQueues ) )
2023-01-17 17:47:37 +00:00
if case . cmdOk = r { return }
throw r
}
2022-05-09 17:40:39 +01:00
func apiStartChat ( ) throws -> Bool {
2022-10-03 16:42:43 +04:00
let r = chatSendCmdSync ( . startChat ( subscribe : true , expire : true ) )
2022-05-09 17:40:39 +01:00
switch r {
case . chatStarted : return true
case . chatRunning : return false
default : throw r
}
2022-02-06 18:26:22 +00:00
}
2022-06-24 13:52:20 +01:00
func apiStopChat ( ) async throws {
let r = await chatSendCmd ( . apiStopChat )
2022-06-19 19:49:39 +01:00
switch r {
case . chatStopped : return
default : throw r
}
}
2022-06-27 10:28:30 +01:00
func apiActivateChat ( ) {
let r = chatSendCmdSync ( . apiActivateChat )
2022-06-19 19:49:39 +01:00
if case . cmdOk = r { return }
2022-06-27 10:28:30 +01:00
logger . error ( " apiActivateChat error: \( String ( describing : r ) ) " )
}
func apiSuspendChat ( timeoutMicroseconds : Int ) {
let r = chatSendCmdSync ( . apiSuspendChat ( timeoutMicroseconds : timeoutMicroseconds ) )
if case . cmdOk = r { return }
logger . error ( " apiSuspendChat error: \( String ( describing : r ) ) " )
2022-06-19 19:49:39 +01:00
}
2022-04-19 12:29:03 +04:00
func apiSetFilesFolder ( filesFolder : String ) throws {
let r = chatSendCmdSync ( . setFilesFolder ( filesFolder : filesFolder ) )
if case . cmdOk = r { return }
throw r
}
2022-08-23 18:18:12 +04:00
func apiSetIncognito ( incognito : Bool ) throws {
let r = chatSendCmdSync ( . setIncognito ( incognito : incognito ) )
if case . cmdOk = r { return }
throw r
}
2022-06-24 13:52:20 +01:00
func apiExportArchive ( config : ArchiveConfig ) async throws {
try await sendCommandOkResp ( . apiExportArchive ( config : config ) )
}
func apiImportArchive ( config : ArchiveConfig ) async throws {
try await sendCommandOkResp ( . apiImportArchive ( config : config ) )
}
func apiDeleteStorage ( ) async throws {
try await sendCommandOkResp ( . apiDeleteStorage )
}
2022-09-07 12:49:41 +01:00
func apiStorageEncryption ( currentKey : String = " " , newKey : String = " " ) async throws {
try await sendCommandOkResp ( . apiStorageEncryption ( config : DBEncryptionConfig ( currentKey : currentKey , newKey : newKey ) ) )
}
2022-07-02 17:18:45 +01:00
func apiGetChats ( ) throws -> [ ChatData ] {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else { throw RuntimeError ( " apiGetChats: no current user " ) }
let r = chatSendCmdSync ( . apiGetChats ( userId : userId ) )
2023-01-19 16:22:56 +00:00
if case let . apiChats ( _ , chats ) = r { return chats }
2022-01-30 18:27:20 +00:00
throw r
}
2022-08-17 11:43:18 +01:00
func apiGetChat ( type : ChatType , id : Int64 , search : String = " " ) throws -> Chat {
let r = chatSendCmdSync ( . apiGetChat ( type : type , id : id , pagination : . last ( count : 50 ) , search : search ) )
2023-01-19 16:22:56 +00:00
if case let . apiChat ( _ , chat ) = r { return Chat . init ( chat ) }
2022-01-30 18:27:20 +00:00
throw r
}
2022-08-17 11:43:18 +01:00
func apiGetChatItems ( type : ChatType , id : Int64 , pagination : ChatPagination , search : String = " " ) async throws -> [ ChatItem ] {
let r = await chatSendCmd ( . apiGetChat ( type : type , id : id , pagination : pagination , search : search ) )
2023-01-19 16:22:56 +00:00
if case let . apiChat ( _ , chat ) = r { return chat . chatItems }
2022-08-15 21:07:11 +01:00
throw r
}
2022-08-17 11:43:18 +01:00
func loadChat ( chat : Chat , search : String = " " ) {
2022-07-02 17:18:45 +01:00
do {
let cInfo = chat . chatInfo
let m = ChatModel . shared
2022-08-29 14:08:46 +01:00
m . reversedChatItems = [ ]
let chat = try apiGetChat ( type : cInfo . chatType , id : cInfo . apiId , search : search )
2022-07-02 17:18:45 +01:00
m . updateChatInfo ( chat . chatInfo )
2022-08-15 21:07:11 +01:00
m . reversedChatItems = chat . chatItems . reversed ( )
2022-07-02 17:18:45 +01:00
} catch let error {
logger . error ( " loadChat error: \( responseError ( error ) ) " )
}
}
2022-12-17 14:02:07 +00:00
func apiSendMessage ( type : ChatType , id : Int64 , file : String ? , quotedItemId : Int64 ? , msg : MsgContent , live : Bool = false ) async -> ChatItem ? {
2022-02-28 10:44:48 +00:00
let chatModel = ChatModel . shared
2022-12-17 14:02:07 +00:00
let cmd : ChatCommand = . apiSendMessage ( type : type , id : id , file : file , quotedItemId : quotedItemId , msg : msg , live : live )
2022-02-28 10:44:48 +00:00
let r : ChatResponse
if type = = . direct {
var cItem : ChatItem !
let endTask = beginBGTask ( { if cItem != nil { chatModel . messageDelivery . removeValue ( forKey : cItem . id ) } } )
r = await chatSendCmd ( cmd , bgTask : false )
2023-01-19 16:22:56 +00:00
if case let . newChatItem ( _ , aChatItem ) = r {
2022-02-28 10:44:48 +00:00
cItem = aChatItem . chatItem
chatModel . messageDelivery [ cItem . id ] = endTask
return cItem
}
2022-09-21 17:18:48 +04:00
if ! networkErrorAlert ( r ) {
sendMessageErrorAlert ( r )
}
2022-02-28 10:44:48 +00:00
endTask ( )
2022-09-21 17:18:48 +04:00
return nil
2022-02-28 10:44:48 +00:00
} else {
r = await chatSendCmd ( cmd , bgDelay : msgDelay )
2023-01-19 16:22:56 +00:00
if case let . newChatItem ( _ , aChatItem ) = r {
2022-02-28 10:44:48 +00:00
return aChatItem . chatItem
}
2022-09-21 17:18:48 +04:00
sendMessageErrorAlert ( r )
return nil
2022-02-28 10:44:48 +00:00
}
2022-09-21 17:18:48 +04:00
}
private func sendMessageErrorAlert ( _ r : ChatResponse ) {
logger . error ( " apiSendMessage error: \( String ( describing : r ) ) " )
AlertManager . shared . showAlertMsg (
title : " Error sending message " ,
message : " Error: \( String ( describing : r ) ) "
)
2022-01-30 18:27:20 +00:00
}
2022-12-17 14:02:07 +00:00
func apiUpdateChatItem ( type : ChatType , id : Int64 , itemId : Int64 , msg : MsgContent , live : Bool = false ) async throws -> ChatItem {
let r = await chatSendCmd ( . apiUpdateChatItem ( type : type , id : id , itemId : itemId , msg : msg , live : live ) , bgDelay : msgDelay )
2023-01-19 16:22:56 +00:00
if case let . chatItemUpdated ( _ , aChatItem ) = r { return aChatItem . chatItem }
2022-03-25 22:26:05 +04:00
throw r
}
2022-12-03 15:40:31 +04:00
func apiDeleteChatItem ( type : ChatType , id : Int64 , itemId : Int64 , mode : CIDeleteMode ) async throws -> ( ChatItem , ChatItem ? ) {
2022-03-30 20:37:47 +04:00
let r = await chatSendCmd ( . apiDeleteChatItem ( type : type , id : id , itemId : itemId , mode : mode ) , bgDelay : msgDelay )
2023-01-19 16:22:56 +00:00
if case let . chatItemDeleted ( _ , deletedChatItem , toChatItem , _ ) = r { return ( deletedChatItem . chatItem , toChatItem ? . chatItem ) }
2022-03-25 22:26:05 +04:00
throw r
}
2022-07-09 09:29:56 +01:00
func apiGetNtfToken ( ) -> ( DeviceToken ? , NtfTknStatus ? , NotificationsMode ) {
2022-07-01 09:49:30 +01:00
let r = chatSendCmdSync ( . apiGetNtfToken )
switch r {
case let . ntfToken ( token , status , ntfMode ) : return ( token , status , ntfMode )
2023-01-19 16:22:56 +00:00
case . chatCmdError ( _ , . errorAgent ( . CMD ( . PROHIBITED ) ) ) : return ( nil , nil , . off )
2022-07-09 09:29:56 +01:00
default :
logger . debug ( " apiGetNtfToken response: \( String ( describing : r ) , privacy : . public ) " )
return ( nil , nil , . off )
2022-07-01 09:49:30 +01:00
}
}
func apiRegisterToken ( token : DeviceToken , notificationMode : NotificationsMode ) async throws -> NtfTknStatus {
2022-06-27 10:28:30 +01:00
let r = await chatSendCmd ( . apiRegisterToken ( token : token , notificationMode : notificationMode ) )
2022-04-23 06:32:16 +01:00
if case let . ntfTokenStatus ( status ) = r { return status }
throw r
2022-04-21 20:04:22 +01:00
}
2022-07-01 09:49:30 +01:00
func registerToken ( token : DeviceToken ) {
let m = ChatModel . shared
let mode = m . notificationMode
2022-07-09 09:29:56 +01:00
if mode != . off && ! m . tokenRegistered {
m . tokenRegistered = true
2022-07-01 09:49:30 +01:00
logger . debug ( " registerToken \( mode . rawValue ) " )
Task {
do {
let status = try await apiRegisterToken ( token : token , notificationMode : mode )
await MainActor . run { m . tokenStatus = status }
} catch let error {
logger . error ( " registerToken apiRegisterToken error: \( responseError ( error ) ) " )
}
}
}
}
2022-06-27 23:03:27 +01:00
func apiVerifyToken ( token : DeviceToken , nonce : String , code : String ) async throws {
try await sendCommandOkResp ( . apiVerifyToken ( token : token , nonce : nonce , code : code ) )
2022-04-22 13:46:05 +01:00
}
2022-06-27 23:03:27 +01:00
func apiDeleteToken ( token : DeviceToken ) async throws {
2022-04-22 13:46:05 +01:00
try await sendCommandOkResp ( . apiDeleteToken ( token : token ) )
2022-04-21 20:04:22 +01:00
}
2022-11-21 08:37:13 +00:00
func getUserSMPServers ( ) throws -> ( [ ServerCfg ] , [ String ] ) {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else { throw RuntimeError ( " getUserSMPServers: no current user " ) }
let r = chatSendCmdSync ( . apiGetUserSMPServers ( userId : userId ) )
2023-01-19 16:22:56 +00:00
if case let . userSMPServers ( _ , smpServers , presetServers ) = r { return ( smpServers , presetServers ) }
2022-03-10 15:45:40 +04:00
throw r
}
2022-11-21 08:37:13 +00:00
func setUserSMPServers ( smpServers : [ ServerCfg ] ) async throws {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else { throw RuntimeError ( " setUserSMPServers: no current user " ) }
try await sendCommandOkResp ( . apiSetUserSMPServers ( userId : userId , smpServers : smpServers ) )
2022-03-10 15:45:40 +04:00
}
2022-11-16 15:37:20 +00:00
func testSMPServer ( smpServer : String ) async throws -> Result < ( ) , SMPTestFailure > {
2023-01-13 13:54:07 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else { throw RuntimeError ( " testSMPServer: no current user " ) }
let r = await chatSendCmd ( . testSMPServer ( userId : userId , smpServer : smpServer ) )
2023-01-19 16:22:56 +00:00
if case let . smpTestResult ( _ , testFailure ) = r {
2022-11-16 15:37:20 +00:00
if let t = testFailure {
return . failure ( t )
}
return . success ( ( ) )
}
throw r
}
2022-10-03 16:42:43 +04:00
func getChatItemTTL ( ) throws -> ChatItemTTL {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else { throw RuntimeError ( " getChatItemTTL: no current user " ) }
let r = chatSendCmdSync ( . apiGetChatItemTTL ( userId : userId ) )
2023-01-19 16:22:56 +00:00
if case let . chatItemTTL ( _ , chatItemTTL ) = r { return ChatItemTTL ( chatItemTTL ) }
2022-10-03 16:42:43 +04:00
throw r
}
func setChatItemTTL ( _ chatItemTTL : ChatItemTTL ) async throws {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else { throw RuntimeError ( " setChatItemTTL: no current user " ) }
try await sendCommandOkResp ( . apiSetChatItemTTL ( userId : userId , seconds : chatItemTTL . seconds ) )
2022-10-03 16:42:43 +04:00
}
2022-07-26 07:29:48 +01:00
func getNetworkConfig ( ) async throws -> NetCfg ? {
let r = await chatSendCmd ( . apiGetNetworkConfig )
if case let . networkConfig ( cfg ) = r { return cfg }
throw r
}
2022-08-03 12:36:51 +01:00
func setNetworkConfig ( _ cfg : NetCfg ) throws {
let r = chatSendCmdSync ( . apiSetNetworkConfig ( networkConfig : cfg ) )
if case . cmdOk = r { return }
throw r
2022-07-26 07:29:48 +01:00
}
2022-08-20 12:47:48 +01:00
func apiSetChatSettings ( type : ChatType , id : Int64 , chatSettings : ChatSettings ) async throws {
try await sendCommandOkResp ( . apiSetChatSettings ( type : type , id : id , chatSettings : chatSettings ) )
}
2022-12-12 08:59:35 +00:00
func apiContactInfo ( _ contactId : Int64 ) async throws -> ( ConnectionStats ? , Profile ? ) {
2022-07-26 07:29:48 +01:00
let r = await chatSendCmd ( . apiContactInfo ( contactId : contactId ) )
2023-01-19 16:22:56 +00:00
if case let . contactInfo ( _ , _ , connStats , customUserProfile ) = r { return ( connStats , customUserProfile ) }
2022-07-26 07:29:48 +01:00
throw r
}
2022-12-12 08:59:35 +00:00
func apiGroupMemberInfo ( _ groupId : Int64 , _ groupMemberId : Int64 ) throws -> ( ConnectionStats ? ) {
let r = chatSendCmdSync ( . apiGroupMemberInfo ( groupId : groupId , groupMemberId : groupMemberId ) )
2023-01-19 16:22:56 +00:00
if case let . groupMemberInfo ( _ , _ , _ , connStats_ ) = r { return ( connStats_ ) }
2022-07-26 07:29:48 +01:00
throw r
}
2022-11-01 20:30:53 +00:00
func apiSwitchContact ( contactId : Int64 ) async throws {
try await sendCommandOkResp ( . apiSwitchContact ( contactId : contactId ) )
}
func apiSwitchGroupMember ( _ groupId : Int64 , _ groupMemberId : Int64 ) async throws {
try await sendCommandOkResp ( . apiSwitchGroupMember ( groupId : groupId , groupMemberId : groupMemberId ) )
}
2022-12-12 08:59:35 +00:00
func apiGetContactCode ( _ contactId : Int64 ) async throws -> ( Contact , String ) {
let r = await chatSendCmd ( . apiGetContactCode ( contactId : contactId ) )
2023-01-19 16:22:56 +00:00
if case let . contactCode ( _ , contact , connectionCode ) = r { return ( contact , connectionCode ) }
2022-12-12 08:59:35 +00:00
throw r
}
func apiGetGroupMemberCode ( _ groupId : Int64 , _ groupMemberId : Int64 ) throws -> ( GroupMember , String ) {
let r = chatSendCmdSync ( . apiGetGroupMemberCode ( groupId : groupId , groupMemberId : groupMemberId ) )
2023-01-19 16:22:56 +00:00
if case let . groupMemberCode ( _ , _ , member , connectionCode ) = r { return ( member , connectionCode ) }
2022-12-12 08:59:35 +00:00
throw r
}
func apiVerifyContact ( _ contactId : Int64 , connectionCode : String ? ) -> ( Bool , String ) ? {
let r = chatSendCmdSync ( . apiVerifyContact ( contactId : contactId , connectionCode : connectionCode ) )
2023-01-19 16:22:56 +00:00
if case let . connectionVerified ( _ , verified , expectedCode ) = r { return ( verified , expectedCode ) }
2022-12-12 08:59:35 +00:00
logger . error ( " apiVerifyContact error: \( String ( describing : r ) ) " )
return nil
}
func apiVerifyGroupMember ( _ groupId : Int64 , _ groupMemberId : Int64 , connectionCode : String ? ) -> ( Bool , String ) ? {
let r = chatSendCmdSync ( . apiVerifyGroupMember ( groupId : groupId , groupMemberId : groupMemberId , connectionCode : connectionCode ) )
2023-01-19 16:22:56 +00:00
if case let . connectionVerified ( _ , verified , expectedCode ) = r { return ( verified , expectedCode ) }
2022-12-12 08:59:35 +00:00
logger . error ( " apiVerifyGroupMember error: \( String ( describing : r ) ) " )
return nil
}
2022-09-21 16:28:01 +01:00
func apiAddContact ( ) async -> String ? {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else {
logger . error ( " apiAddContact: no current user " )
return nil
}
let r = await chatSendCmd ( . apiAddContact ( userId : userId ) , bgTask : false )
2023-01-19 16:22:56 +00:00
if case let . invitation ( _ , connReqInvitation ) = r { return connReqInvitation }
2022-09-21 17:18:48 +04:00
connectionErrorAlert ( r )
return nil
2022-01-29 23:37:02 +00:00
}
2022-09-21 17:18:48 +04:00
func apiConnect ( connReq : String ) async -> ConnReqType ? {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else {
logger . error ( " apiConnect: no current user " )
return nil
}
let r = await chatSendCmd ( . apiConnect ( userId : userId , connReq : connReq ) )
2022-04-02 14:35:35 +01:00
let am = AlertManager . shared
2022-01-29 23:37:02 +00:00
switch r {
2022-04-25 07:54:07 +01:00
case . sentConfirmation : return . invitation
case . sentInvitation : return . contact
2023-01-19 16:22:56 +00:00
case let . contactAlreadyExists ( _ , contact ) :
2022-11-11 12:30:10 +04:00
let m = ChatModel . shared
if let c = m . getContactChat ( contact . contactId ) {
await MainActor . run { m . chatId = c . id }
}
2022-04-02 14:35:35 +01:00
am . showAlertMsg (
title : " Contact already exists " ,
2022-11-11 12:30:10 +04:00
message : " You are already connected to \( contact . displayName ) . "
2022-04-02 14:35:35 +01:00
)
2022-04-25 07:54:07 +01:00
return nil
2023-01-19 16:22:56 +00:00
case . chatCmdError ( _ , . error ( . invalidConnReq ) ) :
2022-04-02 14:35:35 +01:00
am . showAlertMsg (
title : " Invalid connection link " ,
message : " Please check that you used the correct link or ask your contact to send you another one. "
)
2022-04-25 07:54:07 +01:00
return nil
2023-01-19 16:22:56 +00:00
case . chatCmdError ( _ , . errorAgent ( . SMP ( . AUTH ) ) ) :
2022-04-21 11:50:24 +04:00
am . showAlertMsg (
title : " Connection error (AUTH) " ,
message : " Unless your contact deleted the connection or this link was already used, it might be a bug - please report it. \n To connect, please ask your contact to create another connection link and check that you have a stable network connection. "
)
2022-04-25 07:54:07 +01:00
return nil
2023-01-19 16:22:56 +00:00
case let . chatCmdError ( _ , . errorAgent ( . INTERNAL ( internalErr ) ) ) :
2022-05-09 09:52:09 +01:00
if internalErr = = " SEUniqueID " {
am . showAlertMsg (
title : " Already connected? " ,
message : " It seems like you are already connected via this link. If it is not the case, there was an error ( \( responseError ( r ) ) ). "
)
return nil
}
2022-09-21 17:18:48 +04:00
default : ( )
}
connectionErrorAlert ( r )
return nil
}
private func connectionErrorAlert ( _ r : ChatResponse ) {
if ! networkErrorAlert ( r ) {
AlertManager . shared . showAlertMsg (
title : " Connection error " ,
message : " Error: \( String ( describing : r ) ) "
)
2022-01-29 23:37:02 +00:00
}
}
2022-02-24 17:16:41 +00:00
func apiDeleteChat ( type : ChatType , id : Int64 ) async throws {
2022-02-28 10:44:48 +00:00
let r = await chatSendCmd ( . apiDeleteChat ( type : type , id : id ) , bgTask : false )
2022-04-25 10:39:28 +01:00
if case . direct = type , case . contactDeleted = r { return }
if case . contactConnection = type , case . contactConnectionDeleted = r { return }
2022-07-19 18:21:15 +04:00
if case . group = type , case . groupDeletedUser = r { return }
2022-01-31 21:28:07 +00:00
throw r
}
2022-07-19 18:21:15 +04:00
func deleteChat ( _ chat : Chat ) async {
do {
let cInfo = chat . chatInfo
try await apiDeleteChat ( type : cInfo . chatType , id : cInfo . apiId )
DispatchQueue . main . async { ChatModel . shared . removeChat ( cInfo . id ) }
2022-09-21 17:18:48 +04:00
} catch let error {
2022-07-19 18:21:15 +04:00
logger . error ( " deleteChat apiDeleteChat error: \( responseError ( error ) ) " )
2022-10-20 19:27:00 +04:00
AlertManager . shared . showAlertMsg (
title : " Error deleting chat! " ,
message : " Error: \( responseError ( error ) ) "
)
2022-07-19 18:21:15 +04:00
}
}
2022-05-20 12:00:58 +04:00
func apiClearChat ( type : ChatType , id : Int64 ) async throws -> ChatInfo {
2022-05-17 22:48:54 +04:00
let r = await chatSendCmd ( . apiClearChat ( type : type , id : id ) , bgTask : false )
2023-01-19 16:22:56 +00:00
if case let . chatCleared ( _ , updatedChatInfo ) = r { return updatedChatInfo }
2022-05-17 22:48:54 +04:00
throw r
}
func clearChat ( _ chat : Chat ) async {
do {
let cInfo = chat . chatInfo
2022-05-20 12:00:58 +04:00
let updatedChatInfo = try await apiClearChat ( type : cInfo . chatType , id : cInfo . apiId )
DispatchQueue . main . async { ChatModel . shared . clearChat ( updatedChatInfo ) }
2022-05-17 22:48:54 +04:00
} catch {
logger . error ( " clearChat apiClearChat error: \( responseError ( error ) ) " )
}
}
2022-07-26 10:55:58 +04:00
func apiListContacts ( ) throws -> [ Contact ] {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else { throw RuntimeError ( " apiListContacts: no current user " ) }
let r = chatSendCmdSync ( . apiListContacts ( userId : userId ) )
2023-01-19 16:22:56 +00:00
if case let . contactsList ( _ , contacts ) = r { return contacts }
2022-07-26 10:55:58 +04:00
throw r
}
2022-02-24 17:16:41 +00:00
func apiUpdateProfile ( profile : Profile ) async throws -> Profile ? {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else { throw RuntimeError ( " apiUpdateProfile: no current user " ) }
let r = await chatSendCmd ( . apiUpdateProfile ( userId : userId , profile : profile ) )
2022-01-31 21:28:07 +00:00
switch r {
case . userProfileNoChange : return nil
2023-01-19 16:22:56 +00:00
case let . userProfileUpdated ( _ , _ , toProfile ) : return toProfile
2022-01-31 21:28:07 +00:00
default : throw r
}
}
2022-11-16 20:26:43 +04:00
func apiSetContactPrefs ( contactId : Int64 , preferences : Preferences ) async throws -> Contact ? {
let r = await chatSendCmd ( . apiSetContactPrefs ( contactId : contactId , preferences : preferences ) )
2023-01-19 16:22:56 +00:00
if case let . contactPrefsUpdated ( _ , _ , toContact ) = r { return toContact }
2022-11-16 20:26:43 +04:00
throw r
}
2022-08-25 17:36:26 +04:00
func apiSetContactAlias ( contactId : Int64 , localAlias : String ) async throws -> Contact ? {
let r = await chatSendCmd ( . apiSetContactAlias ( contactId : contactId , localAlias : localAlias ) )
2023-01-19 16:22:56 +00:00
if case let . contactAliasUpdated ( _ , toContact ) = r { return toContact }
2022-08-25 17:36:26 +04:00
throw r
}
2022-10-01 10:57:18 +01:00
func apiSetConnectionAlias ( connId : Int64 , localAlias : String ) async throws -> PendingContactConnection ? {
let r = await chatSendCmd ( . apiSetConnectionAlias ( connId : connId , localAlias : localAlias ) )
2023-01-19 16:22:56 +00:00
if case let . connectionAliasUpdated ( _ , toConnection ) = r { return toConnection }
2022-10-01 10:57:18 +01:00
throw r
}
2022-02-24 17:16:41 +00:00
func apiCreateUserAddress ( ) async throws -> String {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else { throw RuntimeError ( " apiCreateUserAddress: no current user " ) }
let r = await chatSendCmd ( . apiCreateMyAddress ( userId : userId ) )
2023-01-19 16:22:56 +00:00
if case let . userContactLinkCreated ( _ , connReq ) = r { return connReq }
2022-02-01 17:34:06 +00:00
throw r
}
2022-02-24 17:16:41 +00:00
func apiDeleteUserAddress ( ) async throws {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else { throw RuntimeError ( " apiDeleteUserAddress: no current user " ) }
let r = await chatSendCmd ( . apiDeleteMyAddress ( userId : userId ) )
2022-02-01 17:34:06 +00:00
if case . userContactLinkDeleted = r { return }
throw r
}
2022-10-23 11:16:56 +01:00
func apiGetUserAddress ( ) throws -> UserContactLink ? {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else { throw RuntimeError ( " apiGetUserAddress: no current user " ) }
let r = chatSendCmdSync ( . apiShowMyAddress ( userId : userId ) )
2022-02-01 17:34:06 +00:00
switch r {
2023-01-19 16:22:56 +00:00
case let . userContactLink ( _ , contactLink ) : return contactLink
case . chatCmdError ( _ , chatError : . errorStore ( storeError : . userContactLinkNotFound ) ) : return nil
2022-10-23 11:16:56 +01:00
default : throw r
}
}
func userAddressAutoAccept ( _ autoAccept : AutoAccept ? ) async throws -> UserContactLink ? {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else { throw RuntimeError ( " userAddressAutoAccept: no current user " ) }
let r = await chatSendCmd ( . apiAddressAutoAccept ( userId : userId , autoAccept : autoAccept ) )
2022-10-23 11:16:56 +01:00
switch r {
2023-01-19 16:22:56 +00:00
case let . userContactLinkUpdated ( _ , contactLink ) : return contactLink
case . chatCmdError ( _ , chatError : . errorStore ( storeError : . userContactLinkNotFound ) ) : return nil
2022-02-01 17:34:06 +00:00
default : throw r
}
}
2022-09-21 17:18:48 +04:00
func apiAcceptContactRequest ( contactReqId : Int64 ) async -> Contact ? {
2022-02-24 17:16:41 +00:00
let r = await chatSendCmd ( . apiAcceptContact ( contactReqId : contactReqId ) )
2022-09-21 17:18:48 +04:00
let am = AlertManager . shared
2023-01-19 16:22:56 +00:00
if case let . acceptingContactRequest ( _ , contact ) = r { return contact }
if case . chatCmdError ( _ , . errorAgent ( . SMP ( . AUTH ) ) ) = r {
2022-09-21 17:18:48 +04:00
am . showAlertMsg (
title : " Connection error (AUTH) " ,
message : " Sender may have deleted the connection request. "
)
} else if ! networkErrorAlert ( r ) {
logger . error ( " apiAcceptContactRequest error: \( String ( describing : r ) ) " )
am . showAlertMsg (
title : " Error accepting contact request " ,
message : " Error: \( String ( describing : r ) ) "
)
}
return nil
2022-02-01 17:34:06 +00:00
}
2022-02-24 17:16:41 +00:00
func apiRejectContactRequest ( contactReqId : Int64 ) async throws {
let r = await chatSendCmd ( . apiRejectContact ( contactReqId : contactReqId ) )
2022-02-01 17:34:06 +00:00
if case . contactRequestRejected = r { return }
throw r
}
2022-02-24 17:16:41 +00:00
func apiChatRead ( type : ChatType , id : Int64 , itemRange : ( Int64 , Int64 ) ) async throws {
2022-04-22 13:46:05 +01:00
try await sendCommandOkResp ( . apiChatRead ( type : type , id : id , itemRange : itemRange ) )
2022-02-12 15:59:43 +00:00
}
2022-10-21 12:32:11 +01:00
func apiChatUnread ( type : ChatType , id : Int64 , unreadChat : Bool ) async throws {
try await sendCommandOkResp ( . apiChatUnread ( type : type , id : id , unreadChat : unreadChat ) )
}
2022-05-04 09:10:36 +04:00
func receiveFile ( fileId : Int64 ) async {
2022-11-02 10:32:08 +00:00
let inline = privacyTransferImagesInlineGroupDefault . get ( )
if let chatItem = await apiReceiveFile ( fileId : fileId , inline : inline ) {
2022-05-04 09:10:36 +04:00
DispatchQueue . main . async { chatItemSimpleUpdate ( chatItem ) }
}
}
2022-11-02 10:32:08 +00:00
func apiReceiveFile ( fileId : Int64 , inline : Bool ) async -> AChatItem ? {
let r = await chatSendCmd ( . receiveFile ( fileId : fileId , inline : inline ) )
2022-09-21 17:18:48 +04:00
let am = AlertManager . shared
2023-01-19 16:22:56 +00:00
if case let . rcvFileAccepted ( _ , chatItem ) = r { return chatItem }
2022-09-21 17:18:48 +04:00
if case . rcvFileAcceptedSndCancelled = r {
am . showAlertMsg (
title : " Cannot receive file " ,
message : " Sender cancelled file transfer. "
)
} else if ! networkErrorAlert ( r ) {
logger . error ( " apiReceiveFile error: \( String ( describing : r ) ) " )
2022-11-26 13:55:36 +04:00
switch r {
2023-01-19 16:22:56 +00:00
case . chatCmdError ( _ , . error ( . fileAlreadyReceiving ) ) :
2022-11-26 13:55:36 +04:00
logger . debug ( " apiReceiveFile ignoring fileAlreadyReceiving error " )
default :
am . showAlertMsg (
title : " Error receiving file " ,
message : " Error: \( String ( describing : r ) ) "
)
}
2022-09-21 17:18:48 +04:00
}
return nil
}
func networkErrorAlert ( _ r : ChatResponse ) -> Bool {
let am = AlertManager . shared
switch r {
2023-01-19 16:22:56 +00:00
case let . chatCmdError ( _ , . errorAgent ( . BROKER ( addr , . TIMEOUT ) ) ) :
2022-09-21 17:18:48 +04:00
am . showAlertMsg (
title : " Connection timeout " ,
2022-12-03 18:05:32 +00:00
message : " Please check your network connection with \( serverHostname ( addr ) ) and try again. "
2022-09-21 17:18:48 +04:00
)
return true
2023-01-19 16:22:56 +00:00
case let . chatCmdError ( _ , . errorAgent ( . BROKER ( addr , . NETWORK ) ) ) :
2022-09-21 17:18:48 +04:00
am . showAlertMsg (
title : " Connection error " ,
2022-12-03 18:05:32 +00:00
message : " Please check your network connection with \( serverHostname ( addr ) ) and try again. "
2022-09-21 17:18:48 +04:00
)
return true
default :
return false
}
2022-04-19 12:29:03 +04:00
}
2022-02-24 17:16:41 +00:00
func acceptContactRequest ( _ contactRequest : UserContactRequest ) async {
2022-09-21 17:18:48 +04:00
if let contact = await apiAcceptContactRequest ( contactReqId : contactRequest . apiId ) {
2022-02-09 22:53:06 +00:00
let chat = Chat ( chatInfo : ChatInfo . direct ( contact : contact ) , chatItems : [ ] )
2022-02-24 17:16:41 +00:00
DispatchQueue . main . async { ChatModel . shared . replaceChat ( contactRequest . id , chat ) }
2022-02-09 22:53:06 +00:00
}
}
2022-02-24 17:16:41 +00:00
func rejectContactRequest ( _ contactRequest : UserContactRequest ) async {
2022-02-09 22:53:06 +00:00
do {
2022-02-24 17:16:41 +00:00
try await apiRejectContactRequest ( contactReqId : contactRequest . apiId )
DispatchQueue . main . async { ChatModel . shared . removeChat ( contactRequest . id ) }
2022-02-09 22:53:06 +00:00
} catch let error {
2022-04-25 10:39:28 +01:00
logger . error ( " rejectContactRequest: \( responseError ( error ) ) " )
2022-02-09 22:53:06 +00:00
}
}
2022-05-07 06:40:46 +01:00
func apiSendCallInvitation ( _ contact : Contact , _ callType : CallType ) async throws {
try await sendCommandOkResp ( . apiSendCallInvitation ( contact : contact , callType : callType ) )
}
func apiRejectCall ( _ contact : Contact ) async throws {
try await sendCommandOkResp ( . apiRejectCall ( contact : contact ) )
}
2022-05-16 19:27:58 +01:00
func apiSendCallOffer ( _ contact : Contact , _ rtcSession : String , _ rtcIceCandidates : String , media : CallMediaType , capabilities : CallCapabilities ) async throws {
2022-05-07 06:40:46 +01:00
let webRtcSession = WebRTCSession ( rtcSession : rtcSession , rtcIceCandidates : rtcIceCandidates )
let callOffer = WebRTCCallOffer ( callType : CallType ( media : media , capabilities : capabilities ) , rtcSession : webRtcSession )
try await sendCommandOkResp ( . apiSendCallOffer ( contact : contact , callOffer : callOffer ) )
}
2022-05-16 19:27:58 +01:00
func apiSendCallAnswer ( _ contact : Contact , _ rtcSession : String , _ rtcIceCandidates : String ) async throws {
2022-05-07 06:40:46 +01:00
let answer = WebRTCSession ( rtcSession : rtcSession , rtcIceCandidates : rtcIceCandidates )
try await sendCommandOkResp ( . apiSendCallAnswer ( contact : contact , answer : answer ) )
}
2022-05-16 19:27:58 +01:00
func apiSendCallExtraInfo ( _ contact : Contact , _ rtcIceCandidates : String ) async throws {
2022-05-07 06:40:46 +01:00
let extraInfo = WebRTCExtraInfo ( rtcIceCandidates : rtcIceCandidates )
try await sendCommandOkResp ( . apiSendCallExtraInfo ( contact : contact , extraInfo : extraInfo ) )
}
func apiEndCall ( _ contact : Contact ) async throws {
try await sendCommandOkResp ( . apiEndCall ( contact : contact ) )
}
2022-07-05 15:15:15 +04:00
func apiGetCallInvitations ( ) throws -> [ RcvCallInvitation ] {
2023-01-16 15:06:03 +04:00
let r = chatSendCmdSync ( . apiGetCallInvitations )
2022-07-05 15:15:15 +04:00
if case let . callInvitations ( invs ) = r { return invs }
throw r
}
2022-05-07 06:40:46 +01:00
func apiCallStatus ( _ contact : Contact , _ status : String ) async throws {
if let callStatus = WebRTCCallStatus . init ( rawValue : status ) {
try await sendCommandOkResp ( . apiCallStatus ( contact : contact , callStatus : callStatus ) )
} else {
logger . debug ( " apiCallStatus: call status \( status ) not used " )
}
}
2022-08-16 13:13:29 +01:00
func markChatRead ( _ chat : Chat , aboveItem : ChatItem ? = nil ) async {
2022-02-12 15:59:43 +00:00
do {
2022-10-21 12:32:11 +01:00
if chat . chatStats . unreadCount > 0 {
let minItemId = chat . chatStats . minUnreadItemId
let itemRange = ( minItemId , aboveItem ? . id ? ? chat . chatItems . last ? . id ? ? minItemId )
let cInfo = chat . chatInfo
try await apiChatRead ( type : cInfo . chatType , id : cInfo . apiId , itemRange : itemRange )
await MainActor . run { ChatModel . shared . markChatItemsRead ( cInfo , aboveItem : aboveItem ) }
}
if chat . chatStats . unreadChat {
await markChatUnread ( chat , unreadChat : false )
}
2022-02-12 15:59:43 +00:00
} catch {
2022-04-25 10:39:28 +01:00
logger . error ( " markChatRead apiChatRead error: \( responseError ( error ) ) " )
2022-02-12 15:59:43 +00:00
}
}
2022-10-21 12:32:11 +01:00
func markChatUnread ( _ chat : Chat , unreadChat : Bool = true ) async {
do {
let cInfo = chat . chatInfo
try await apiChatUnread ( type : cInfo . chatType , id : cInfo . apiId , unreadChat : unreadChat )
await MainActor . run { ChatModel . shared . markChatUnread ( cInfo , unreadChat : unreadChat ) }
} catch {
logger . error ( " markChatUnread apiChatUnread error: \( responseError ( error ) ) " )
}
}
2022-05-03 08:20:19 +01:00
func apiMarkChatItemRead ( _ cInfo : ChatInfo , _ cItem : ChatItem ) async {
2022-02-12 15:59:43 +00:00
do {
2022-08-15 21:07:11 +01:00
logger . debug ( " apiMarkChatItemRead: \( cItem . id ) " )
2022-02-24 17:16:41 +00:00
try await apiChatRead ( type : cInfo . chatType , id : cInfo . apiId , itemRange : ( cItem . id , cItem . id ) )
2022-08-15 21:07:11 +01:00
await MainActor . run { ChatModel . shared . markChatItemRead ( cInfo , cItem ) }
2022-02-12 15:59:43 +00:00
} catch {
2022-08-15 21:07:11 +01:00
logger . error ( " apiMarkChatItemRead apiChatRead error: \( responseError ( error ) ) " )
2022-02-12 15:59:43 +00:00
}
}
2022-04-22 13:46:05 +01:00
private func sendCommandOkResp ( _ cmd : ChatCommand ) async throws {
let r = await chatSendCmd ( cmd )
if case . cmdOk = r { return }
throw r
}
2022-07-28 11:49:36 +01:00
func apiNewGroup ( _ p : GroupProfile ) throws -> GroupInfo {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else { throw RuntimeError ( " apiNewGroup: no current user " ) }
let r = chatSendCmdSync ( . apiNewGroup ( userId : userId , groupProfile : p ) )
2023-01-19 16:22:56 +00:00
if case let . groupCreated ( _ , groupInfo ) = r { return groupInfo }
2022-07-14 16:40:32 +04:00
throw r
}
2022-08-10 14:54:15 +04:00
func apiAddMember ( _ groupId : Int64 , _ contactId : Int64 , _ memberRole : GroupMemberRole ) async throws -> GroupMember {
2022-07-26 10:55:58 +04:00
let r = await chatSendCmd ( . apiAddMember ( groupId : groupId , contactId : contactId , memberRole : memberRole ) )
2023-01-19 16:22:56 +00:00
if case let . sentGroupInvitation ( _ , _ , _ , member ) = r { return member }
2022-07-26 10:55:58 +04:00
throw r
}
2022-08-01 08:34:07 +01:00
enum JoinGroupResult {
case joined ( groupInfo : GroupInfo )
case invitationRemoved
case groupNotFound
2022-07-18 21:58:32 +04:00
}
2022-08-01 08:34:07 +01:00
func apiJoinGroup ( _ groupId : Int64 ) async throws -> JoinGroupResult {
2022-07-18 21:58:32 +04:00
let r = await chatSendCmd ( . apiJoinGroup ( groupId : groupId ) )
2022-08-01 08:34:07 +01:00
switch r {
2023-01-19 16:22:56 +00:00
case let . userAcceptedGroupSent ( _ , groupInfo , _ ) : return . joined ( groupInfo : groupInfo )
case . chatCmdError ( _ , . errorAgent ( . SMP ( . AUTH ) ) ) : return . invitationRemoved
case . chatCmdError ( _ , . errorStore ( . groupNotFound ) ) : return . groupNotFound
2022-08-01 08:34:07 +01:00
default : throw r
}
2022-07-18 21:58:32 +04:00
}
2022-08-09 13:43:19 +04:00
func apiRemoveMember ( _ groupId : Int64 , _ memberId : Int64 ) async throws -> GroupMember {
2022-07-26 12:33:10 +04:00
let r = await chatSendCmd ( . apiRemoveMember ( groupId : groupId , memberId : memberId ) , bgTask : false )
2023-01-19 16:22:56 +00:00
if case let . userDeletedMember ( _ , _ , member ) = r { return member }
2022-07-26 12:33:10 +04:00
throw r
}
2022-10-04 09:53:43 +01:00
func apiMemberRole ( _ groupId : Int64 , _ memberId : Int64 , _ memberRole : GroupMemberRole ) async throws -> GroupMember {
let r = await chatSendCmd ( . apiMemberRole ( groupId : groupId , memberId : memberId , memberRole : memberRole ) , bgTask : false )
2023-01-19 16:22:56 +00:00
if case let . memberRoleUser ( _ , _ , member , _ , _ ) = r { return member }
2022-10-04 09:53:43 +01:00
throw r
}
2022-07-26 10:55:58 +04:00
func leaveGroup ( _ groupId : Int64 ) async {
2022-07-19 18:21:15 +04:00
do {
2022-07-26 10:55:58 +04:00
let groupInfo = try await apiLeaveGroup ( groupId )
2022-07-19 18:21:15 +04:00
DispatchQueue . main . async { ChatModel . shared . updateGroup ( groupInfo ) }
} catch let error {
logger . error ( " leaveGroup error: \( responseError ( error ) ) " )
}
}
2022-07-26 10:55:58 +04:00
func apiLeaveGroup ( _ groupId : Int64 ) async throws -> GroupInfo {
2022-07-19 18:21:15 +04:00
let r = await chatSendCmd ( . apiLeaveGroup ( groupId : groupId ) , bgTask : false )
2023-01-19 16:22:56 +00:00
if case let . leftMemberUser ( _ , groupInfo ) = r { return groupInfo }
2022-07-19 18:21:15 +04:00
throw r
}
2022-07-26 10:55:58 +04:00
func apiListMembers ( _ groupId : Int64 ) async -> [ GroupMember ] {
let r = await chatSendCmd ( . apiListMembers ( groupId : groupId ) )
2023-01-19 16:22:56 +00:00
if case let . groupMembers ( _ , group ) = r { return group . members }
2022-07-26 10:55:58 +04:00
return [ ]
}
2022-12-12 08:59:35 +00:00
func apiListMembersSync ( _ groupId : Int64 ) -> [ GroupMember ] {
let r = chatSendCmdSync ( . apiListMembers ( groupId : groupId ) )
2023-01-19 16:22:56 +00:00
if case let . groupMembers ( _ , group ) = r { return group . members }
2022-12-12 08:59:35 +00:00
return [ ]
}
2022-07-30 13:03:44 +01:00
func filterMembersToAdd ( _ ms : [ GroupMember ] ) -> [ Contact ] {
let memberContactIds = ms . compactMap { m in m . memberCurrent ? m . memberContactId : nil }
return ChatModel . shared . chats
. compactMap { $0 . chatInfo . contact }
. filter { ! memberContactIds . contains ( $0 . apiId ) }
. sorted { $0 . displayName . lowercased ( ) < $1 . displayName . lowercased ( ) }
}
func apiUpdateGroup ( _ groupId : Int64 , _ groupProfile : GroupProfile ) async throws -> GroupInfo {
let r = await chatSendCmd ( . apiUpdateGroupProfile ( groupId : groupId , groupProfile : groupProfile ) )
2023-01-19 16:22:56 +00:00
if case let . groupUpdated ( _ , toGroup ) = r { return toGroup }
2022-07-30 13:03:44 +01:00
throw r
}
2022-10-15 18:09:25 +04:00
func apiCreateGroupLink ( _ groupId : Int64 ) async throws -> String {
let r = await chatSendCmd ( . apiCreateGroupLink ( groupId : groupId ) )
2023-01-19 16:22:56 +00:00
if case let . groupLinkCreated ( _ , _ , connReq ) = r { return connReq }
2022-10-15 18:09:25 +04:00
throw r
}
func apiDeleteGroupLink ( _ groupId : Int64 ) async throws {
let r = await chatSendCmd ( . apiDeleteGroupLink ( groupId : groupId ) )
if case . groupLinkDeleted = r { return }
throw r
}
func apiGetGroupLink ( _ groupId : Int64 ) throws -> String ? {
let r = chatSendCmdSync ( . apiGetGroupLink ( groupId : groupId ) )
switch r {
2023-01-19 16:22:56 +00:00
case let . groupLink ( _ , _ , connReq ) :
2022-10-15 18:09:25 +04:00
return connReq
2023-01-19 16:22:56 +00:00
case . chatCmdError ( _ , chatError : . errorStore ( storeError : . groupLinkNotFound ) ) :
2022-10-15 18:09:25 +04:00
return nil
default : throw r
}
}
2023-01-22 18:34:01 +00:00
func apiGetVersion ( ) throws -> CoreVersionInfo {
let r = chatSendCmdSync ( . showVersion )
if case let . versionInfo ( info ) = r { return info }
throw r
}
2022-09-07 12:49:41 +01:00
func initializeChat ( start : Bool , dbKey : String ? = nil ) throws {
2022-05-09 09:52:09 +01:00
logger . debug ( " initializeChat " )
2022-09-07 12:49:41 +01:00
let m = ChatModel . shared
2022-09-24 09:28:22 +01:00
( m . chatDbEncrypted , m . chatDbStatus ) = chatMigrateInit ( dbKey )
2022-09-07 12:49:41 +01:00
if m . chatDbStatus != . ok { return }
2022-09-17 16:41:20 +04:00
// I f w e m i g r a t e d s u c c e s s f u l l y m e a n s p r e v i o u s r e - e n c r y p t i o n p r o c e s s o n d a t a b a s e l e v e l f i n i s h e d s u c c e s s f u l l y t o o
if encryptionStartedDefault . get ( ) {
encryptionStartedDefault . set ( false )
}
2022-09-07 12:49:41 +01:00
try apiSetFilesFolder ( filesFolder : getAppFilesDirectory ( ) . path )
try apiSetIncognito ( incognito : incognitoGroupDefault . get ( ) )
2022-09-23 12:51:40 +01:00
m . chatInitialized = true
2022-09-07 12:49:41 +01:00
m . currentUser = try apiGetActiveUser ( )
if m . currentUser = = nil {
m . onboardingStage = . step1_SimpleXInfo
} else if start {
try startChat ( )
} else {
m . chatRunning = false
2022-02-09 22:53:06 +00:00
}
}
2022-06-24 13:52:20 +01:00
func startChat ( ) throws {
2022-05-09 09:52:09 +01:00
logger . debug ( " startChat " )
2022-06-24 13:52:20 +01:00
let m = ChatModel . shared
2022-08-03 12:36:51 +01:00
try setNetworkConfig ( getNetCfg ( ) )
2022-06-24 13:52:20 +01:00
let justStarted = try apiStartChat ( )
2023-01-20 12:38:38 +00:00
m . users = try listUsers ( )
2022-06-24 13:52:20 +01:00
if justStarted {
2023-01-20 12:38:38 +00:00
try getUserChatData ( )
2023-01-23 13:20:58 +00:00
NtfManager . shared . setNtfBadgeCount ( m . totalUnreadCountForAllUsers ( ) )
2022-07-05 15:15:15 +04:00
try refreshCallInvitations ( )
2022-07-09 09:29:56 +01:00
( m . savedToken , m . tokenStatus , m . notificationMode ) = apiGetNtfToken ( )
2022-07-01 09:49:30 +01:00
if let token = m . deviceToken {
registerToken ( token : token )
}
2022-06-24 13:52:20 +01:00
withAnimation {
2023-01-19 16:22:56 +00:00
m . onboardingStage = m . onboardingStage = = . step2_CreateProfile && m . users . count = = 1
2022-07-03 19:53:07 +01:00
? . step3_SetNotificationsMode
2022-06-24 13:52:20 +01:00
: . onboardingComplete
2022-05-09 09:52:09 +01:00
}
}
2022-06-24 13:52:20 +01:00
ChatReceiver . shared . start ( )
m . chatRunning = true
chatLastStartGroupDefault . set ( Date . now )
2022-05-09 09:52:09 +01:00
}
2023-01-24 19:00:30 +00:00
func changeActiveUser ( _ userId : Int64 ) {
2023-01-20 12:38:38 +00:00
do {
2023-01-24 19:00:30 +00:00
try changeActiveUser_ ( userId )
2023-01-20 12:38:38 +00:00
} catch let error {
logger . error ( " Unable to set active user: \( responseError ( error ) ) " )
}
}
2023-01-24 19:00:30 +00:00
func changeActiveUser_ ( _ userId : Int64 ) throws {
let m = ChatModel . shared
m . currentUser = try apiSetActiveUser ( userId )
m . users = try listUsers ( )
try getUserChatData ( )
}
2023-01-20 12:38:38 +00:00
func getUserChatData ( ) throws {
let m = ChatModel . shared
2023-01-17 17:47:37 +00:00
m . userAddress = try apiGetUserAddress ( )
( m . userSMPServers , m . presetSMPServers ) = try getUserSMPServers ( )
m . chatItemTTL = try getChatItemTTL ( )
let chats = try apiGetChats ( )
m . chats = chats . map { Chat . init ( $0 ) }
}
2022-02-09 22:53:06 +00:00
class ChatReceiver {
2022-02-24 17:16:41 +00:00
private var receiveLoop : Task < Void , Never > ?
2022-02-09 22:53:06 +00:00
private var receiveMessages = true
private var _lastMsgTime = Date . now
static let shared = ChatReceiver ( )
var lastMsgTime : Date { get { _lastMsgTime } }
2022-02-10 15:52:11 +00:00
func start ( ) {
2022-02-09 22:53:06 +00:00
logger . debug ( " ChatReceiver.start " )
receiveMessages = true
_lastMsgTime = . now
if receiveLoop != nil { return }
2022-02-24 17:16:41 +00:00
receiveLoop = Task { await receiveMsgLoop ( ) }
}
func receiveMsgLoop ( ) async {
2022-06-24 13:52:20 +01:00
// T O D O u s e f u n c t i o n t h a t h a s t i m e o u t
if let msg = await chatRecvMsg ( ) {
self . _lastMsgTime = . now
await processReceivedMsg ( msg )
}
2022-02-24 17:16:41 +00:00
if self . receiveMessages {
2022-07-02 08:50:25 +01:00
_ = try ? await Task . sleep ( nanoseconds : 7_500_000 )
2022-02-24 17:16:41 +00:00
await receiveMsgLoop ( )
2022-02-09 22:53:06 +00:00
}
}
func stop ( ) {
2022-02-10 15:52:11 +00:00
logger . debug ( " ChatReceiver.stop " )
2022-02-09 22:53:06 +00:00
receiveMessages = false
receiveLoop ? . cancel ( )
receiveLoop = nil
}
}
2022-06-19 19:49:39 +01:00
func processReceivedMsg ( _ res : ChatResponse ) async {
2022-04-25 10:39:28 +01:00
let m = ChatModel . shared
2022-06-19 19:49:39 +01:00
await MainActor . run {
2023-01-30 12:07:06 +00:00
m . addTerminalItem ( . resp ( . now , res ) )
2022-02-09 22:53:06 +00:00
logger . debug ( " processReceivedMsg: \( res . responseType ) " )
2022-01-29 23:37:02 +00:00
switch res {
2023-01-19 16:22:56 +00:00
case let . newContactConnection ( user , connection ) :
if active ( user ) {
m . updateContactConnection ( connection )
}
case let . contactConnectionDeleted ( user , connection ) :
if active ( user ) {
m . removeChat ( connection . id )
}
case let . contactConnected ( user , contact , _ ) :
if active ( user ) && contact . directOrUsed {
2022-10-24 00:18:15 +04:00
m . updateContact ( contact )
2022-11-05 17:48:57 +04:00
m . dismissConnReqView ( contact . activeConn . id )
m . removeChat ( contact . activeConn . id )
2023-01-19 16:22:56 +00:00
NtfManager . shared . notifyContactConnected ( user , contact )
2022-10-24 00:18:15 +04:00
}
2023-01-20 17:35:39 +04:00
m . setContactNetworkStatus ( contact , . connected )
2023-01-19 16:22:56 +00:00
case let . contactConnecting ( user , contact ) :
if active ( user ) && contact . directOrUsed {
2022-10-24 00:18:15 +04:00
m . updateContact ( contact )
m . dismissConnReqView ( contact . activeConn . id )
m . removeChat ( contact . activeConn . id )
}
2023-01-19 16:22:56 +00:00
case let . receivedContactRequest ( user , contactRequest ) :
if ! active ( user ) { return }
2022-07-22 08:10:37 +01:00
let cInfo = ChatInfo . contactRequest ( contactRequest : contactRequest )
if m . hasChat ( contactRequest . id ) {
m . updateChatInfo ( cInfo )
} else {
m . addChat ( Chat (
chatInfo : cInfo ,
chatItems : [ ]
) )
2023-01-19 16:22:56 +00:00
NtfManager . shared . notifyContactRequest ( user , contactRequest )
2022-07-22 08:10:37 +01:00
}
2023-01-19 16:22:56 +00:00
case let . contactUpdated ( user , toContact ) :
if active ( user ) && m . hasChat ( toContact . id ) {
let cInfo = ChatInfo . direct ( contact : toContact )
2022-04-25 10:39:28 +01:00
m . updateChatInfo ( cInfo )
2022-02-04 22:13:52 +00:00
}
2023-01-19 16:22:56 +00:00
case let . contactsMerged ( user , intoContact , mergedContact ) :
if active ( user ) && m . hasChat ( mergedContact . id ) {
2022-10-28 20:05:04 +04:00
if m . chatId = = mergedContact . id {
m . chatId = intoContact . id
}
m . removeChat ( mergedContact . id )
}
2023-01-20 17:35:39 +04:00
case let . contactsSubscribed ( _ , contactRefs ) :
2023-01-20 14:56:05 +04:00
updateContactsStatus ( contactRefs , status : . connected )
2023-01-20 17:35:39 +04:00
case let . contactsDisconnected ( _ , contactRefs ) :
2023-01-20 14:56:05 +04:00
updateContactsStatus ( contactRefs , status : . disconnected )
2023-01-19 16:22:56 +00:00
case let . contactSubError ( user , contact , chatError ) :
if active ( user ) {
2023-01-20 14:56:05 +04:00
m . updateContact ( contact )
2023-01-19 16:22:56 +00:00
}
2023-01-20 14:56:05 +04:00
processContactSubError ( contact , chatError )
2023-01-19 16:22:56 +00:00
case let . contactSubSummary ( user , contactSubscriptions ) :
2022-02-25 20:26:56 +00:00
for sub in contactSubscriptions {
2023-01-20 14:56:05 +04:00
if active ( user ) {
m . updateContact ( sub . contact )
}
2022-02-25 20:26:56 +00:00
if let err = sub . contactError {
processContactSubError ( sub . contact , err )
} else {
2023-01-20 17:35:39 +04:00
m . setContactNetworkStatus ( sub . contact , . connected )
2022-02-25 20:26:56 +00:00
}
2022-02-05 20:10:47 +00:00
}
2023-01-19 16:22:56 +00:00
case let . newChatItem ( user , aChatItem ) :
if ! active ( user ) {
2023-01-23 13:20:58 +00:00
if case . rcvNew = aChatItem . chatItem . meta . itemStatus , aChatItem . chatInfo . ntfsEnabled {
2023-01-19 16:22:56 +00:00
m . increaseUnreadCounter ( user : user )
}
return
}
2022-02-09 22:53:06 +00:00
let cInfo = aChatItem . chatInfo
let cItem = aChatItem . chatItem
2022-04-25 10:39:28 +01:00
m . addChatItem ( cInfo , cItem )
2022-12-07 22:18:22 +00:00
if let file = cItem . file ,
let mc = cItem . content . msgContent ,
2022-12-24 00:22:12 +03:00
file . fileSize <= MAX_IMAGE_SIZE_AUTO_RCV {
2022-12-07 22:18:22 +00:00
let acceptImages = UserDefaults . standard . bool ( forKey : DEFAULT_PRIVACY_ACCEPT_IMAGES )
if ( mc . isImage && acceptImages )
|| ( mc . isVoice && ( ( file . fileSize > MAX_VOICE_MESSAGE_SIZE_INLINE_SEND && acceptImages ) || cInfo . chatType = = . group ) ) {
Task {
await receiveFile ( fileId : file . fileId ) // T O D O c h e c k i n l i n e F i l e M o d e ! = I F M S e n t
}
2022-11-24 21:18:28 +04:00
}
2022-04-19 12:29:03 +04:00
}
2022-11-28 19:11:26 +04:00
if cItem . showNotification {
2023-01-19 16:22:56 +00:00
NtfManager . shared . notifyMessageReceived ( user , cInfo , cItem )
2022-05-07 06:40:46 +01:00
}
2023-01-19 16:22:56 +00:00
case let . chatItemStatusUpdated ( user , aChatItem ) :
if ! active ( user ) { return }
2022-02-12 15:59:43 +00:00
let cInfo = aChatItem . chatInfo
let cItem = aChatItem . chatItem
2022-03-30 20:37:47 +04:00
var res = false
2022-11-24 21:18:28 +04:00
if ! cItem . isDeletedContent {
2022-04-25 10:39:28 +01:00
res = m . upsertChatItem ( cInfo , cItem )
2022-03-30 20:37:47 +04:00
}
if res {
2023-01-19 16:22:56 +00:00
NtfManager . shared . notifyMessageReceived ( user , cInfo , cItem )
2022-04-25 10:39:28 +01:00
} else if let endTask = m . messageDelivery [ cItem . id ] {
2022-02-28 10:44:48 +00:00
switch cItem . meta . itemStatus {
case . sndSent : endTask ( )
case . sndErrorAuth : endTask ( )
case . sndError : endTask ( )
default : break
}
2022-02-12 15:59:43 +00:00
}
2023-01-19 16:22:56 +00:00
case let . chatItemUpdated ( user , aChatItem ) :
if active ( user ) {
chatItemSimpleUpdate ( aChatItem )
}
case let . chatItemDeleted ( user , deletedChatItem , toChatItem , _ ) :
if ! active ( user ) {
2023-01-23 13:20:58 +00:00
if toChatItem = = nil && deletedChatItem . chatItem . isRcvNew && deletedChatItem . chatInfo . ntfsEnabled {
2023-01-19 16:22:56 +00:00
m . decreaseUnreadCounter ( user : user )
}
return
}
2022-12-03 15:40:31 +04:00
if let toChatItem = toChatItem {
_ = m . upsertChatItem ( toChatItem . chatInfo , toChatItem . chatItem )
2022-03-30 20:37:47 +04:00
} else {
2022-12-03 15:40:31 +04:00
m . removeChatItem ( deletedChatItem . chatInfo , deletedChatItem . chatItem )
2022-03-30 20:37:47 +04:00
}
2023-01-19 16:22:56 +00:00
case let . receivedGroupInvitation ( user , groupInfo , _ , _ ) :
if active ( user ) {
m . updateGroup ( groupInfo ) // u p d a t e s o t h a t r e p e a t g r o u p i n v i t a t i o n s a r e n o t d u p l i c a t e d
// N t f M a n a g e r . s h a r e d . n o t i f y C o n t a c t R e q u e s t ( c o n t a c t R e q u e s t ) / / T O D O n o t i f y G r o u p I n v i t a t i o n ?
}
case let . userAcceptedGroupSent ( user , groupInfo , hostContact ) :
if ! active ( user ) { return }
2022-11-04 15:33:29 +04:00
m . updateGroup ( groupInfo )
2022-11-05 17:48:57 +04:00
if let hostContact = hostContact {
m . dismissConnReqView ( hostContact . activeConn . id )
m . removeChat ( hostContact . activeConn . id )
}
2023-01-19 16:22:56 +00:00
case let . joinedGroupMemberConnecting ( user , groupInfo , _ , member ) :
if active ( user ) {
_ = m . upsertGroupMember ( groupInfo , member )
}
case let . deletedMemberUser ( user , groupInfo , _ ) : // T O D O u p d a t e u s e r m e m b e r
if active ( user ) {
m . updateGroup ( groupInfo )
}
case let . deletedMember ( user , groupInfo , _ , deletedMember ) :
if active ( user ) {
_ = m . upsertGroupMember ( groupInfo , deletedMember )
}
case let . leftMember ( user , groupInfo , member ) :
if active ( user ) {
_ = m . upsertGroupMember ( groupInfo , member )
}
case let . groupDeleted ( user , groupInfo , _ ) : // T O D O u p d a t e u s e r m e m b e r
if active ( user ) {
m . updateGroup ( groupInfo )
}
case let . userJoinedGroup ( user , groupInfo ) :
if active ( user ) {
m . updateGroup ( groupInfo )
}
case let . joinedGroupMember ( user , groupInfo , member ) :
if active ( user ) {
_ = m . upsertGroupMember ( groupInfo , member )
}
case let . connectedToGroupMember ( user , groupInfo , member ) :
if active ( user ) {
_ = m . upsertGroupMember ( groupInfo , member )
}
case let . groupUpdated ( user , toGroup ) :
if active ( user ) {
m . updateGroup ( toGroup )
}
case let . rcvFileStart ( user , aChatItem ) :
if active ( user ) {
chatItemSimpleUpdate ( aChatItem )
}
case let . rcvFileComplete ( user , aChatItem ) :
if active ( user ) {
chatItemSimpleUpdate ( aChatItem )
}
case let . sndFileStart ( user , aChatItem , _ ) :
if active ( user ) {
chatItemSimpleUpdate ( aChatItem )
}
case let . sndFileComplete ( user , aChatItem , _ ) :
if ! active ( user ) { return }
2022-05-06 21:10:32 +04:00
chatItemSimpleUpdate ( aChatItem )
let cItem = aChatItem . chatItem
2022-11-21 07:42:36 +00:00
let mc = cItem . content . msgContent
2022-05-06 21:10:32 +04:00
if aChatItem . chatInfo . chatType = = . direct ,
2022-11-21 07:42:36 +00:00
case . file = mc ,
2022-05-06 21:10:32 +04:00
let fileName = cItem . file ? . filePath {
removeFile ( fileName )
}
2022-07-05 15:15:15 +04:00
case let . callInvitation ( invitation ) :
m . callInvitations [ invitation . contact . id ] = invitation
activateCall ( invitation )
2022-05-24 19:34:27 +01:00
// T h i s w i l l b e c a l l e d f r o m n o t i f i c a t i o n s e r v i c e e x t e n s i o n
// C X P r o v i d e r . r e p o r t N e w I n c o m i n g V o I P P u s h P a y l o a d ( [
// " d i s p l a y N a m e " : c o n t a c t . d i s p l a y N a m e ,
// " c o n t a c t I d " : c o n t a c t . i d ,
// " u u i d " : i n v i t a t i o n . c a l l k i t U U I D
// ] ) { e r r o r i n
// i f l e t e r r o r = e r r o r {
// l o g g e r . e r r o r ( " r e p o r t N e w I n c o m i n g V o I P P u s h P a y l o a d e r r o r \ ( e r r o r . l o c a l i z e d D e s c r i p t i o n ) " )
// } e l s e {
// l o g g e r . d e b u g ( " r e p o r t N e w I n c o m i n g V o I P P u s h P a y l o a d s u c c e s s f o r \ ( c o n t a c t . i d ) " )
// }
// }
2023-01-19 16:22:56 +00:00
case let . callOffer ( _ , contact , callType , offer , sharedKey , _ ) :
2022-05-07 06:40:46 +01:00
withCall ( contact ) { call in
2022-05-24 19:34:27 +01:00
call . callState = . offerReceived
call . peerMedia = callType . media
call . sharedKey = sharedKey
2022-05-27 16:36:33 +01:00
let useRelay = UserDefaults . standard . bool ( forKey : DEFAULT_WEBRTC_POLICY_RELAY )
2022-09-21 10:19:13 +01:00
let iceServers = getIceServers ( )
2022-05-27 16:36:33 +01:00
logger . debug ( " .callOffer useRelay \( useRelay ) " )
2022-09-21 10:19:13 +01:00
logger . debug ( " .callOffer iceServers \( String ( describing : iceServers ) ) " )
m . callCommand = . offer (
offer : offer . rtcSession ,
iceCandidates : offer . rtcIceCandidates ,
media : callType . media , aesKey : sharedKey ,
useWorker : true ,
iceServers : iceServers ,
relay : useRelay
)
2022-05-07 06:40:46 +01:00
}
2023-01-19 16:22:56 +00:00
case let . callAnswer ( _ , contact , answer ) :
2022-05-07 06:40:46 +01:00
withCall ( contact ) { call in
2022-05-24 19:34:27 +01:00
call . callState = . answerReceived
2022-05-07 06:40:46 +01:00
m . callCommand = . answer ( answer : answer . rtcSession , iceCandidates : answer . rtcIceCandidates )
}
2023-01-19 16:22:56 +00:00
case let . callExtraInfo ( _ , contact , extraInfo ) :
2022-05-07 06:40:46 +01:00
withCall ( contact ) { _ in
m . callCommand = . ice ( iceCandidates : extraInfo . rtcIceCandidates )
}
2023-01-19 16:22:56 +00:00
case let . callEnded ( _ , contact ) :
2022-05-24 19:34:27 +01:00
if let invitation = m . callInvitations . removeValue ( forKey : contact . id ) {
CallController . shared . reportCallRemoteEnded ( invitation : invitation )
}
withCall ( contact ) { call in
2022-05-07 06:40:46 +01:00
m . callCommand = . end
2022-06-02 12:10:41 +01:00
// C a l l C o n t r o l l e r . s h a r e d . r e p o r t C a l l R e m o t e E n d e d ( c a l l : c a l l )
2022-05-07 06:40:46 +01:00
}
2022-06-27 10:28:30 +01:00
case . chatSuspended :
chatSuspended ( )
2022-01-29 23:37:02 +00:00
default :
2022-02-09 22:53:06 +00:00
logger . debug ( " unsupported event: \( res . responseType ) " )
2022-01-29 11:10:04 +00:00
}
2022-05-07 06:40:46 +01:00
2023-01-19 16:22:56 +00:00
func active ( _ user : User ) -> Bool {
user . id = = m . currentUser ? . id
}
2022-05-07 06:40:46 +01:00
func withCall ( _ contact : Contact , _ perform : ( Call ) -> Void ) {
if let call = m . activeCall , call . contact . apiId = = contact . apiId {
perform ( call )
} else {
logger . debug ( " processReceivedMsg: ignoring \( res . responseType ) , not in call with the contact \( contact . id ) " )
}
}
2022-01-29 11:10:04 +00:00
}
}
2022-05-04 09:10:36 +04:00
func chatItemSimpleUpdate ( _ aChatItem : AChatItem ) {
let m = ChatModel . shared
let cInfo = aChatItem . chatInfo
let cItem = aChatItem . chatItem
if m . upsertChatItem ( cInfo , cItem ) {
2023-01-19 16:22:56 +00:00
NtfManager . shared . notifyMessageReceived ( m . currentUser ! , cInfo , cItem )
2022-05-04 09:10:36 +04:00
}
}
2023-01-20 14:56:05 +04:00
func updateContactsStatus ( _ contactRefs : [ ContactRef ] , status : NetworkStatus ) {
2022-02-25 20:26:56 +00:00
let m = ChatModel . shared
2022-04-26 07:51:06 +01:00
for c in contactRefs {
2023-01-20 17:35:39 +04:00
m . networkStatuses [ c . agentConnId ] = status
2022-04-26 07:51:06 +01:00
}
2022-02-25 20:26:56 +00:00
}
func processContactSubError ( _ contact : Contact , _ chatError : ChatError ) {
let m = ChatModel . shared
var err : String
switch chatError {
2022-12-03 18:05:32 +00:00
case . errorAgent ( agentError : . BROKER ( _ , . NETWORK ) ) : err = " network "
2022-02-25 20:26:56 +00:00
case . errorAgent ( agentError : . SMP ( smpErr : . AUTH ) ) : err = " contact deleted "
default : err = String ( describing : chatError )
}
2023-01-20 17:35:39 +04:00
m . setContactNetworkStatus ( contact , . error ( err ) )
2022-02-25 20:26:56 +00:00
}
2022-07-05 15:15:15 +04:00
func refreshCallInvitations ( ) throws {
let m = ChatModel . shared
let callInvitations = try apiGetCallInvitations ( )
m . callInvitations = callInvitations . reduce ( into : [ ChatId : RcvCallInvitation ] ( ) ) { result , inv in result [ inv . contact . id ] = inv }
2022-07-22 08:10:37 +01:00
if let ( chatId , ntfAction ) = m . ntfCallInvitationAction ,
let invitation = m . callInvitations . removeValue ( forKey : chatId ) {
m . ntfCallInvitationAction = nil
CallController . shared . callAction ( invitation : invitation , action : ntfAction )
} else if let invitation = callInvitations . last {
activateCall ( invitation )
2022-07-05 15:15:15 +04:00
}
}
func activateCall ( _ callInvitation : RcvCallInvitation ) {
let m = ChatModel . shared
CallController . shared . reportNewIncomingCall ( invitation : callInvitation ) { error in
if let error = error {
m . callInvitations [ callInvitation . contact . id ] ? . callkitUUID = nil
logger . error ( " reportNewIncomingCall error: \( error . localizedDescription ) " )
} else {
logger . debug ( " reportNewIncomingCall success " )
}
}
}
2022-01-29 11:10:04 +00:00
private struct UserResponse : Decodable {
var user : User ?
var error : String ?
}
2023-01-05 20:38:31 +04:00
struct RuntimeError : Error {
let message : String
init ( _ message : String ) {
self . message = message
}
public var localizedDescription : String {
return message
}
}