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
2023-09-20 12:26:16 +04:00
// c u r r e n t C h a t V e r s i o n i n c o r e
public let CURRENT_CHAT_VERSION : Int = 2
// v e r s i o n r a n g e t h a t s u p p o r t s e s t a b l i s h i n g d i r e c t c o n n e c t i o n w i t h a g r o u p m e m b e r ( x G r p D i r e c t I n v V R a n g e i n c o r e )
public let CREATE_MEMBER_CONTACT_VRANGE = VersionRange ( minVersion : 2 , maxVersion : CURRENT_CHAT_VERSION )
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 ) " )
2023-05-26 17:55:57 +02:00
let start = Date . now
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 ) " )
}
2023-08-17 18:21:05 +01:00
Task {
await TerminalItems . shared . addCommand ( start , cmd . obfuscated , 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
}
}
2023-05-10 09:06:18 +02:00
func apiCreateActiveUser ( _ p : Profile ? , sameServers : Bool = false , pastTimestamp : Bool = false ) throws -> User {
let r = chatSendCmdSync ( . createActiveUser ( profile : p , sameServers : sameServers , pastTimestamp : pastTimestamp ) )
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-03-22 15:58:01 +00:00
return try listUsersResponse ( chatSendCmdSync ( . listUsers ) )
}
func listUsersAsync ( ) async throws -> [ UserInfo ] {
return try listUsersResponse ( await chatSendCmd ( . listUsers ) )
}
private func listUsersResponse ( _ r : ChatResponse ) throws -> [ UserInfo ] {
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
}
2023-03-22 15:58:01 +00:00
func apiSetActiveUser ( _ userId : Int64 , viewPwd : String ? ) throws -> User {
let r = chatSendCmdSync ( . apiSetActiveUser ( userId : userId , viewPwd : viewPwd ) )
if case let . activeUser ( user ) = r { return user }
throw r
}
func apiSetActiveUserAsync ( _ userId : Int64 , viewPwd : String ? ) async throws -> User {
let r = await chatSendCmd ( . apiSetActiveUser ( userId : userId , viewPwd : viewPwd ) )
2023-01-17 17:47:37 +00:00
if case let . activeUser ( user ) = r { return user }
throw r
}
2023-07-16 14:55:31 +04:00
func apiSetAllContactReceipts ( enable : Bool ) async throws {
let r = await chatSendCmd ( . setAllContactReceipts ( enable : enable ) )
if case . cmdOk = r { return }
throw r
}
func apiSetUserContactReceipts ( _ userId : Int64 , userMsgReceiptSettings : UserMsgReceiptSettings ) async throws {
let r = await chatSendCmd ( . apiSetUserContactReceipts ( userId : userId , userMsgReceiptSettings : userMsgReceiptSettings ) )
if case . cmdOk = r { return }
throw r
}
2023-07-28 13:16:52 +04:00
func apiSetUserGroupReceipts ( _ userId : Int64 , userMsgReceiptSettings : UserMsgReceiptSettings ) async throws {
let r = await chatSendCmd ( . apiSetUserGroupReceipts ( userId : userId , userMsgReceiptSettings : userMsgReceiptSettings ) )
if case . cmdOk = r { return }
throw r
}
2023-03-22 15:58:01 +00:00
func apiHideUser ( _ userId : Int64 , viewPwd : String ) async throws -> User {
try await setUserPrivacy_ ( . apiHideUser ( userId : userId , viewPwd : viewPwd ) )
}
2023-03-29 19:28:06 +01:00
func apiUnhideUser ( _ userId : Int64 , viewPwd : String ) async throws -> User {
2023-03-22 15:58:01 +00:00
try await setUserPrivacy_ ( . apiUnhideUser ( userId : userId , viewPwd : viewPwd ) )
}
2023-03-29 19:28:06 +01:00
func apiMuteUser ( _ userId : Int64 ) async throws -> User {
try await setUserPrivacy_ ( . apiMuteUser ( userId : userId ) )
2023-03-22 15:58:01 +00:00
}
2023-03-29 19:28:06 +01:00
func apiUnmuteUser ( _ userId : Int64 ) async throws -> User {
try await setUserPrivacy_ ( . apiUnmuteUser ( userId : userId ) )
2023-03-22 15:58:01 +00:00
}
func setUserPrivacy_ ( _ cmd : ChatCommand ) async throws -> User {
let r = await chatSendCmd ( cmd )
2023-03-30 09:02:57 +01:00
if case let . userPrivacy ( _ , updatedUser ) = r { return updatedUser }
2023-03-22 15:58:01 +00:00
throw r
}
func apiDeleteUser ( _ userId : Int64 , _ delSMPQueues : Bool , viewPwd : String ? ) async throws {
let r = await chatSendCmd ( . apiDeleteUser ( userId : userId , delSMPQueues : delSMPQueues , viewPwd : viewPwd ) )
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 {
2023-04-18 19:43:16 +04:00
let r = chatSendCmdSync ( . startChat ( subscribe : true , expire : true , xftp : 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
}
2023-03-24 15:20:15 +04:00
func apiSetTempFolder ( tempFolder : String ) throws {
let r = chatSendCmdSync ( . setTempFolder ( tempFolder : tempFolder ) )
if case . cmdOk = r { return }
throw r
}
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
}
2023-03-24 15:20:15 +04:00
func setXFTPConfig ( _ cfg : XFTPFileConfig ? ) throws {
let r = chatSendCmdSync ( . apiSetXFTPConfig ( config : cfg ) )
if case . cmdOk = r { return }
throw r
}
2023-10-15 20:58:39 +01:00
func apiSetEncryptLocalFiles ( _ enable : Bool ) throws {
2023-10-15 18:16:12 +01:00
let r = chatSendCmdSync ( . apiSetEncryptLocalFiles ( enable : enable ) )
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 ) )
}
2023-05-24 14:22:12 +04:00
func apiImportArchive ( config : ArchiveConfig ) async throws -> [ ArchiveError ] {
let r = await chatSendCmd ( . apiImportArchive ( config : config ) )
if case let . archiveImported ( archiveErrors ) = r { return archiveErrors }
throw r
2022-06-24 13:52:20 +01:00
}
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-03-22 15:58:01 +00:00
let userId = try currentUserId ( " apiGetChats " )
return try apiChatsResponse ( chatSendCmdSync ( . apiGetChats ( userId : userId ) ) )
}
func apiGetChatsAsync ( ) async throws -> [ ChatData ] {
let userId = try currentUserId ( " apiGetChats " )
return try apiChatsResponse ( await chatSendCmd ( . apiGetChats ( userId : userId ) ) )
}
private func apiChatsResponse ( _ r : ChatResponse ) throws -> [ ChatData ] {
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
2023-10-18 11:23:35 +01:00
m . chatItemStatuses = [ : ]
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 ) ) " )
}
}
2023-05-19 18:50:48 +02:00
func apiGetChatItemInfo ( type : ChatType , id : Int64 , itemId : Int64 ) async throws -> ChatItemInfo {
let r = await chatSendCmd ( . apiGetChatItemInfo ( type : type , id : id , itemId : itemId ) )
2023-05-09 20:43:21 +04:00
if case let . chatItemInfo ( _ , _ , chatItemInfo ) = r { return chatItemInfo }
throw r
}
2023-09-07 11:28:37 +01:00
func apiSendMessage ( type : ChatType , id : Int64 , file : CryptoFile ? , quotedItemId : Int64 ? , msg : MsgContent , live : Bool = false , ttl : Int ? = nil ) async -> ChatItem ? {
2022-02-28 10:44:48 +00:00
let chatModel = ChatModel . shared
2023-05-15 16:07:55 +04:00
let cmd : ChatCommand = . apiSendMessage ( type : type , id : id , file : file , quotedItemId : quotedItemId , msg : msg , live : live , ttl : ttl )
2022-02-28 10:44:48 +00:00
let r : ChatResponse
if type = = . direct {
2023-08-17 18:21:05 +01:00
var cItem : ChatItem ? = nil
let endTask = beginBGTask ( {
if let cItem = cItem {
DispatchQueue . main . async {
chatModel . messageDelivery . removeValue ( forKey : cItem . id )
}
}
} )
2022-02-28 10:44:48 +00:00
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
2023-08-17 18:21:05 +01:00
chatModel . messageDelivery [ aChatItem . chatItem . id ] = endTask
2022-02-28 10:44:48 +00:00
return cItem
}
2023-05-16 15:03:41 +04:00
if let networkErrorAlert = networkErrorAlert ( r ) {
AlertManager . shared . showAlert ( networkErrorAlert )
} else {
2022-09-21 17:18:48 +04:00
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
}
2023-05-17 10:31:27 +02:00
func apiChatItemReaction ( type : ChatType , id : Int64 , itemId : Int64 , add : Bool , reaction : MsgReaction ) async throws -> ChatItem {
let r = await chatSendCmd ( . apiChatItemReaction ( type : type , id : id , itemId : itemId , add : add , reaction : reaction ) , bgDelay : msgDelay )
if case let . chatItemReaction ( _ , _ , reaction ) = r { return reaction . chatReaction . chatItem }
2023-05-16 10:34:25 +02: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-03-06 21:57:58 +00:00
if case let . chatItemDeleted ( _ , deletedChatItem , toChatItem , _ ) = r { return ( deletedChatItem . chatItem , toChatItem ? . chatItem ) }
throw r
}
func apiDeleteMemberChatItem ( groupId : Int64 , groupMemberId : Int64 , itemId : Int64 ) async throws -> ( ChatItem , ChatItem ? ) {
let r = await chatSendCmd ( . apiDeleteMemberChatItem ( groupId : groupId , groupMemberId : groupMemberId , itemId : itemId ) , 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
}
2023-04-06 22:48:32 +01:00
func getUserProtoServers ( _ serverProtocol : ServerProtocol ) throws -> UserProtoServers {
let userId = try currentUserId ( " getUserProtoServers " )
let r = chatSendCmdSync ( . apiGetUserProtoServers ( userId : userId , serverProtocol : serverProtocol ) )
if case let . userProtoServers ( _ , servers ) = r { return servers }
2022-03-10 15:45:40 +04:00
throw r
}
2023-04-06 22:48:32 +01:00
func setUserProtoServers ( _ serverProtocol : ServerProtocol , servers : [ ServerCfg ] ) async throws {
let userId = try currentUserId ( " setUserProtoServers " )
try await sendCommandOkResp ( . apiSetUserProtoServers ( userId : userId , serverProtocol : serverProtocol , servers : servers ) )
2022-03-10 15:45:40 +04:00
}
2023-04-06 22:48:32 +01:00
func testProtoServer ( server : String ) async throws -> Result < ( ) , ProtocolTestFailure > {
let userId = try currentUserId ( " testProtoServer " )
let r = await chatSendCmd ( . apiTestProtoServer ( userId : userId , server : server ) )
if case let . serverTestResult ( _ , _ , 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-03-22 15:58:01 +00:00
let userId = try currentUserId ( " getChatItemTTL " )
return try chatItemTTLResponse ( chatSendCmdSync ( . apiGetChatItemTTL ( userId : userId ) ) )
}
func getChatItemTTLAsync ( ) async throws -> ChatItemTTL {
let userId = try currentUserId ( " getChatItemTTLAsync " )
return try chatItemTTLResponse ( await chatSendCmd ( . apiGetChatItemTTL ( userId : userId ) ) )
}
private func chatItemTTLResponse ( _ r : ChatResponse ) throws -> ChatItemTTL {
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-03-22 15:58:01 +00:00
let userId = try currentUserId ( " setChatItemTTL " )
2023-01-05 20:38:31 +04:00
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
}
2023-07-05 09:09:56 +01:00
func reconnectAllServers ( ) async throws {
try await sendCommandOkResp ( . reconnectAllServers )
}
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 ) )
}
2023-10-31 09:44:57 +00:00
func apiSetMemberSettings ( _ groupId : Int64 , _ groupMemberId : Int64 , _ memberSettings : GroupMemberSettings ) async throws {
try await sendCommandOkResp ( . apiSetMemberSettings ( groupId : groupId , groupMemberId : groupMemberId , memberSettings : memberSettings ) )
}
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
}
2023-07-10 19:01:22 +04:00
func apiGroupMemberInfo ( _ groupId : Int64 , _ groupMemberId : Int64 ) throws -> ( GroupMember , ConnectionStats ? ) {
2022-12-12 08:59:35 +00:00
let r = chatSendCmdSync ( . apiGroupMemberInfo ( groupId : groupId , groupMemberId : groupMemberId ) )
2023-07-10 19:01:22 +04:00
if case let . groupMemberInfo ( _ , _ , member , connStats_ ) = r { return ( member , connStats_ ) }
2022-07-26 07:29:48 +01:00
throw r
}
2023-06-20 10:09:04 +04:00
func apiSwitchContact ( contactId : Int64 ) throws -> ConnectionStats {
let r = chatSendCmdSync ( . apiSwitchContact ( contactId : contactId ) )
if case let . contactSwitchStarted ( _ , _ , connectionStats ) = r { return connectionStats }
throw r
2022-11-01 20:30:53 +00:00
}
2023-06-20 10:09:04 +04:00
func apiSwitchGroupMember ( _ groupId : Int64 , _ groupMemberId : Int64 ) throws -> ConnectionStats {
let r = chatSendCmdSync ( . apiSwitchGroupMember ( groupId : groupId , groupMemberId : groupMemberId ) )
if case let . groupMemberSwitchStarted ( _ , _ , _ , connectionStats ) = r { return connectionStats }
throw r
2022-11-01 20:30:53 +00:00
}
2023-06-19 14:46:08 +04:00
func apiAbortSwitchContact ( _ contactId : Int64 ) throws -> ConnectionStats {
let r = chatSendCmdSync ( . apiAbortSwitchContact ( contactId : contactId ) )
if case let . contactSwitchAborted ( _ , _ , connectionStats ) = r { return connectionStats }
throw r
}
func apiAbortSwitchGroupMember ( _ groupId : Int64 , _ groupMemberId : Int64 ) throws -> ConnectionStats {
let r = chatSendCmdSync ( . apiAbortSwitchGroupMember ( groupId : groupId , groupMemberId : groupMemberId ) )
if case let . groupMemberSwitchAborted ( _ , _ , _ , connectionStats ) = r { return connectionStats }
throw r
}
2023-07-10 19:01:22 +04:00
func apiSyncContactRatchet ( _ contactId : Int64 , _ force : Bool ) throws -> ConnectionStats {
let r = chatSendCmdSync ( . apiSyncContactRatchet ( contactId : contactId , force : force ) )
if case let . contactRatchetSyncStarted ( _ , _ , connectionStats ) = r { return connectionStats }
throw r
}
func apiSyncGroupMemberRatchet ( _ groupId : Int64 , _ groupMemberId : Int64 , _ force : Bool ) throws -> ( GroupMember , ConnectionStats ) {
let r = chatSendCmdSync ( . apiSyncGroupMemberRatchet ( groupId : groupId , groupMemberId : groupMemberId , force : force ) )
if case let . groupMemberRatchetSyncStarted ( _ , _ , member , connectionStats ) = r { return ( member , connectionStats ) }
throw r
}
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
}
2023-08-08 17:26:56 +04:00
func apiAddContact ( incognito : Bool ) async -> ( String , PendingContactConnection ) ? {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else {
logger . error ( " apiAddContact: no current user " )
return nil
}
2023-08-08 17:26:56 +04:00
let r = await chatSendCmd ( . apiAddContact ( userId : userId , incognito : incognito ) , bgTask : false )
if case let . invitation ( _ , connReqInvitation , connection ) = r { return ( connReqInvitation , connection ) }
2023-05-16 15:03:41 +04:00
AlertManager . shared . showAlert ( connectionErrorAlert ( r ) )
2022-09-21 17:18:48 +04:00
return nil
2022-01-29 23:37:02 +00:00
}
2023-08-08 17:26:56 +04:00
func apiSetConnectionIncognito ( connId : Int64 , incognito : Bool ) async throws -> PendingContactConnection ? {
let r = await chatSendCmd ( . apiSetConnectionIncognito ( connId : connId , incognito : incognito ) )
if case let . connectionIncognitoUpdated ( _ , toConnection ) = r { return toConnection }
throw r
}
2023-10-13 19:19:00 +04:00
func apiConnectPlan ( connReq : String ) async throws -> ConnectionPlan {
let userId = try currentUserId ( " apiConnectPlan " )
let r = await chatSendCmd ( . apiConnectPlan ( userId : userId , connReq : connReq ) )
if case let . connectionPlan ( _ , connectionPlan ) = r { return connectionPlan }
logger . error ( " apiConnectPlan error: \( responseError ( r ) ) " )
throw r
}
2023-08-08 17:26:56 +04:00
func apiConnect ( incognito : Bool , connReq : String ) async -> ConnReqType ? {
let ( connReqType , alert ) = await apiConnect_ ( incognito : incognito , connReq : connReq )
2023-05-16 15:03:41 +04:00
if let alert = alert {
AlertManager . shared . showAlert ( alert )
return nil
} else {
return connReqType
}
}
2023-08-08 17:26:56 +04:00
func apiConnect_ ( incognito : Bool , connReq : String ) async -> ( ConnReqType ? , Alert ? ) {
2023-01-05 20:38:31 +04:00
guard let userId = ChatModel . shared . currentUser ? . userId else {
logger . error ( " apiConnect: no current user " )
2023-05-16 15:03:41 +04:00
return ( nil , nil )
2023-01-05 20:38:31 +04:00
}
2023-08-08 17:26:56 +04:00
let r = await chatSendCmd ( . apiConnect ( userId : userId , incognito : incognito , connReq : connReq ) )
2022-01-29 23:37:02 +00:00
switch r {
2023-05-16 15:03:41 +04:00
case . sentConfirmation : return ( . invitation , nil )
case . sentInvitation : return ( . contact , nil )
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 }
}
2023-10-13 19:19:00 +04:00
let alert = contactAlreadyExistsAlert ( contact )
2023-05-16 15:03:41 +04:00
return ( nil , alert )
2023-01-19 16:22:56 +00:00
case . chatCmdError ( _ , . error ( . invalidConnReq ) ) :
2023-05-16 15:03:41 +04:00
let alert = mkAlert (
2022-04-02 14:35:35 +01:00
title : " Invalid connection link " ,
message : " Please check that you used the correct link or ask your contact to send you another one. "
)
2023-05-16 15:03:41 +04:00
return ( nil , alert )
2023-01-19 16:22:56 +00:00
case . chatCmdError ( _ , . errorAgent ( . SMP ( . AUTH ) ) ) :
2023-05-16 15:03:41 +04:00
let alert = mkAlert (
2022-04-21 11:50:24 +04:00
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. "
)
2023-05-16 15:03:41 +04:00
return ( nil , alert )
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 " {
2023-05-16 15:03:41 +04:00
let alert = mkAlert (
2022-05-09 09:52:09 +01:00
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 ) ) ). "
)
2023-05-16 15:03:41 +04:00
return ( nil , alert )
2022-05-09 09:52:09 +01:00
}
2022-09-21 17:18:48 +04:00
default : ( )
}
2023-05-16 15:03:41 +04:00
let alert = connectionErrorAlert ( r )
return ( nil , alert )
2022-09-21 17:18:48 +04:00
}
2023-10-13 19:19:00 +04:00
func contactAlreadyExistsAlert ( _ contact : Contact ) -> Alert {
mkAlert (
title : " Contact already exists " ,
message : " You are already connected to \( contact . displayName ) . "
)
}
2023-05-16 15:03:41 +04:00
private func connectionErrorAlert ( _ r : ChatResponse ) -> Alert {
if let networkErrorAlert = networkErrorAlert ( r ) {
return networkErrorAlert
} else {
return mkAlert (
2022-09-21 17:18:48 +04:00
title : " Connection error " ,
message : " Error: \( String ( describing : r ) ) "
)
2022-01-29 23:37:02 +00:00
}
}
2023-11-10 10:16:06 +04:00
func apiConnectContactViaAddress ( incognito : Bool , contactId : Int64 ) async -> ( Contact ? , Alert ? ) {
guard let userId = ChatModel . shared . currentUser ? . userId else {
logger . error ( " apiConnectContactViaAddress: no current user " )
return ( nil , nil )
}
let r = await chatSendCmd ( . apiConnectContactViaAddress ( userId : userId , incognito : incognito , contactId : contactId ) )
if case let . sentInvitationToContact ( _ , contact , _ ) = r { return ( contact , nil ) }
logger . error ( " apiConnectContactViaAddress error: \( responseError ( r ) ) " )
let alert = connectionErrorAlert ( r )
return ( nil , alert )
}
2023-10-19 19:52:59 +04:00
func apiDeleteChat ( type : ChatType , id : Int64 , notify : Bool ? = nil ) async throws {
let r = await chatSendCmd ( . apiDeleteChat ( type : type , id : id , notify : notify ) , 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
}
2023-10-19 19:52:59 +04:00
func deleteChat ( _ chat : Chat , notify : Bool ? = nil ) async {
2022-07-19 18:21:15 +04:00
do {
let cInfo = chat . chatInfo
2023-10-19 19:52:59 +04:00
try await apiDeleteChat ( type : cInfo . chatType , id : cInfo . apiId , notify : notify )
2022-07-19 18:21:15 +04:00
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-03-22 15:58:01 +00:00
let userId = try currentUserId ( " apiListContacts " )
2023-01-05 20:38:31 +04:00
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
}
2023-08-23 20:43:06 +03:00
func apiUpdateProfile ( profile : Profile ) async throws -> ( Profile , [ Contact ] ) ? {
2023-03-22 15:58:01 +00:00
let userId = try currentUserId ( " apiUpdateProfile " )
2023-01-05 20:38:31 +04:00
let r = await chatSendCmd ( . apiUpdateProfile ( userId : userId , profile : profile ) )
2022-01-31 21:28:07 +00:00
switch r {
2023-10-25 06:01:47 +08:00
case . userProfileNoChange : return ( profile , [ ] )
2023-08-23 20:43:06 +03:00
case let . userProfileUpdated ( _ , _ , toProfile , updateSummary ) : return ( toProfile , updateSummary . changedContacts )
2023-10-25 06:01:47 +08:00
case . chatCmdError ( _ , . errorStore ( . duplicateName ) ) : return nil ;
2022-01-31 21:28:07 +00:00
default : throw r
}
}
2023-04-27 17:19:21 +04:00
func apiSetProfileAddress ( on : Bool ) async throws -> User ? {
let userId = try currentUserId ( " apiSetProfileAddress " )
let r = await chatSendCmd ( . apiSetProfileAddress ( userId : userId , on : on ) )
switch r {
case . userProfileNoChange : return nil
2023-08-23 20:43:06 +03:00
case let . userProfileUpdated ( user , _ , _ , _ ) : return user
2023-04-27 17:19:21 +04: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-03-22 15:58:01 +00:00
let userId = try currentUserId ( " apiCreateUserAddress " )
2023-01-05 20:38:31 +04:00
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
}
2023-04-27 17:19:21 +04:00
func apiDeleteUserAddress ( ) async throws -> User ? {
2023-03-22 15:58:01 +00:00
let userId = try currentUserId ( " apiDeleteUserAddress " )
2023-01-05 20:38:31 +04:00
let r = await chatSendCmd ( . apiDeleteMyAddress ( userId : userId ) )
2023-04-27 17:19:21 +04:00
if case let . userContactLinkDeleted ( user ) = r { return user }
2022-02-01 17:34:06 +00:00
throw r
}
2022-10-23 11:16:56 +01:00
func apiGetUserAddress ( ) throws -> UserContactLink ? {
2023-03-22 15:58:01 +00:00
let userId = try currentUserId ( " apiGetUserAddress " )
return try userAddressResponse ( chatSendCmdSync ( . apiShowMyAddress ( userId : userId ) ) )
}
func apiGetUserAddressAsync ( ) async throws -> UserContactLink ? {
let userId = try currentUserId ( " apiGetUserAddressAsync " )
return try userAddressResponse ( await chatSendCmd ( . apiShowMyAddress ( userId : userId ) ) )
}
private func userAddressResponse ( _ r : ChatResponse ) throws -> UserContactLink ? {
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-03-22 15:58:01 +00:00
let userId = try currentUserId ( " userAddressAutoAccept " )
2023-01-05 20:38:31 +04:00
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
}
}
2023-08-08 17:26:56 +04:00
func apiAcceptContactRequest ( incognito : Bool , contactReqId : Int64 ) async -> Contact ? {
let r = await chatSendCmd ( . apiAcceptContact ( incognito : incognito , 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. "
)
2023-05-16 15:03:41 +04:00
} else if let networkErrorAlert = networkErrorAlert ( r ) {
am . showAlert ( networkErrorAlert )
} else {
2022-09-21 17:18:48 +04:00
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 ) )
}
2023-09-07 11:28:37 +01:00
func receiveFile ( user : any UserLike , fileId : Int64 , encrypted : Bool , auto : Bool = false ) async {
if let chatItem = await apiReceiveFile ( fileId : fileId , encrypted : encrypted , auto : auto ) {
2023-08-17 18:21:05 +01:00
await chatItemSimpleUpdate ( user , chatItem )
2022-05-04 09:10:36 +04:00
}
}
2023-09-07 11:28:37 +01:00
func apiReceiveFile ( fileId : Int64 , encrypted : Bool , inline : Bool ? = nil , auto : Bool = false ) async -> AChatItem ? {
let r = await chatSendCmd ( . receiveFile ( fileId : fileId , encrypted : encrypted , 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 {
2023-08-01 18:18:14 +04:00
logger . debug ( " apiReceiveFile error: sender cancelled file transfer " )
if ! auto {
am . showAlertMsg (
title : " Cannot receive file " ,
message : " Sender cancelled file transfer. "
)
}
2023-05-16 15:03:41 +04:00
} else if let networkErrorAlert = networkErrorAlert ( r ) {
2023-08-01 18:18:14 +04:00
logger . error ( " apiReceiveFile network error: \( String ( describing : r ) ) " )
2023-08-03 14:25:56 +04:00
am . showAlert ( networkErrorAlert )
2023-05-16 15:03:41 +04:00
} else {
2023-08-03 14:25:56 +04:00
switch chatError ( r ) {
case . fileCancelled :
logger . debug ( " apiReceiveFile ignoring fileCancelled error " )
case . fileAlreadyReceiving :
2022-11-26 13:55:36 +04:00
logger . debug ( " apiReceiveFile ignoring fileAlreadyReceiving error " )
default :
2023-08-01 18:18:14 +04:00
logger . error ( " apiReceiveFile error: \( String ( describing : r ) ) " )
2023-08-03 14:25:56 +04:00
am . showAlertMsg (
title : " Error receiving file " ,
message : " Error: \( String ( describing : r ) ) "
)
2022-11-26 13:55:36 +04:00
}
2022-09-21 17:18:48 +04:00
}
return nil
}
2023-03-30 14:10:13 +04:00
func cancelFile ( user : User , fileId : Int64 ) async {
if let chatItem = await apiCancelFile ( fileId : fileId ) {
2023-08-17 18:21:05 +01:00
await chatItemSimpleUpdate ( user , chatItem )
2023-04-07 17:41:07 +04:00
cleanupFile ( chatItem )
2023-03-30 14:10:13 +04:00
}
}
func apiCancelFile ( fileId : Int64 ) async -> AChatItem ? {
let r = await chatSendCmd ( . cancelFile ( fileId : fileId ) )
switch r {
case let . sndFileCancelled ( _ , chatItem , _ , _ ) : return chatItem
case let . rcvFileCancelled ( _ , chatItem , _ ) : return chatItem
default :
logger . error ( " apiCancelFile error: \( String ( describing : r ) ) " )
return nil
}
}
2023-11-16 16:53:44 +00:00
func setLocalDeviceName ( _ displayName : String ) throws {
try sendCommandOkRespSync ( . setLocalDeviceName ( displayName : displayName ) )
2023-10-13 22:35:30 +01:00
}
2023-11-16 16:53:44 +00:00
func connectRemoteCtrl ( desktopAddress : String ) async throws -> ( RemoteCtrlInfo ? , CtrlAppInfo , String ) {
let r = await chatSendCmd ( . connectRemoteCtrl ( xrcpInvitation : desktopAddress ) )
if case let . remoteCtrlConnecting ( rc_ , ctrlAppInfo , v ) = r { return ( rc_ , ctrlAppInfo , v ) }
2023-10-13 22:35:30 +01:00
throw r
}
2023-11-16 16:53:44 +00:00
func findKnownRemoteCtrl ( ) async throws {
try await sendCommandOkResp ( . findKnownRemoteCtrl )
}
func confirmRemoteCtrl ( _ rcId : Int64 ) async throws {
try await sendCommandOkResp ( . confirmRemoteCtrl ( remoteCtrlId : rcId ) )
2023-10-13 22:35:30 +01:00
}
2023-11-16 16:53:44 +00:00
func verifyRemoteCtrlSession ( _ sessCode : String ) async throws -> RemoteCtrlInfo {
let r = await chatSendCmd ( . verifyRemoteCtrlSession ( sessionCode : sessCode ) )
if case let . remoteCtrlConnected ( rc ) = r { return rc }
throw r
2023-10-13 22:35:30 +01:00
}
2023-11-16 16:53:44 +00:00
func listRemoteCtrls ( ) throws -> [ RemoteCtrlInfo ] {
let r = chatSendCmdSync ( . listRemoteCtrls )
if case let . remoteCtrlList ( rcInfo ) = r { return rcInfo }
throw r
2023-10-13 22:35:30 +01:00
}
func stopRemoteCtrl ( ) async throws {
try await sendCommandOkResp ( . stopRemoteCtrl )
}
func deleteRemoteCtrl ( _ rcId : Int64 ) async throws {
try await sendCommandOkResp ( . deleteRemoteCtrl ( remoteCtrlId : rcId ) )
}
2023-05-16 15:03:41 +04:00
func networkErrorAlert ( _ r : ChatResponse ) -> Alert ? {
2022-09-21 17:18:48 +04:00
switch r {
2023-01-19 16:22:56 +00:00
case let . chatCmdError ( _ , . errorAgent ( . BROKER ( addr , . TIMEOUT ) ) ) :
2023-05-16 15:03:41 +04:00
return mkAlert (
2022-09-21 17:18:48 +04:00
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
)
2023-01-19 16:22:56 +00:00
case let . chatCmdError ( _ , . errorAgent ( . BROKER ( addr , . NETWORK ) ) ) :
2023-05-16 15:03:41 +04:00
return mkAlert (
2022-09-21 17:18:48 +04:00
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
)
default :
2023-05-16 15:03:41 +04:00
return nil
2022-09-21 17:18:48 +04:00
}
2022-04-19 12:29:03 +04:00
}
2023-08-08 17:26:56 +04:00
func acceptContactRequest ( incognito : Bool , contactRequest : UserContactRequest ) async {
if let contact = await apiAcceptContactRequest ( incognito : incognito , 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 " )
}
}
2023-10-13 11:51:01 +01:00
func apiGetNetworkStatuses ( ) throws -> [ ConnNetworkStatus ] {
let r = chatSendCmdSync ( . apiGetNetworkStatuses )
if case let . networkStatuses ( _ , statuses ) = r { return statuses }
throw r
}
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 )
2023-06-19 11:13:30 +01:00
await MainActor . run {
withAnimation { ChatModel . shared . markChatItemsRead ( cInfo , aboveItem : aboveItem ) }
}
2022-10-21 12:32:11 +01:00
}
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 )
2023-06-19 11:13:30 +01:00
await MainActor . run {
withAnimation { ChatModel . shared . markChatUnread ( cInfo , unreadChat : unreadChat ) }
}
2022-10-21 12:32:11 +01:00
} 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
}
2023-11-16 16:53:44 +00:00
private func sendCommandOkRespSync ( _ cmd : ChatCommand ) throws {
let r = chatSendCmdSync ( cmd )
if case . cmdOk = r { return }
throw r
}
2023-10-26 18:51:45 +04:00
func apiNewGroup ( incognito : Bool , groupProfile : GroupProfile ) throws -> GroupInfo {
2023-03-22 15:58:01 +00:00
let userId = try currentUserId ( " apiNewGroup " )
2023-10-26 18:51:45 +04:00
let r = chatSendCmdSync ( . apiNewGroup ( userId : userId , incognito : incognito , groupProfile : groupProfile ) )
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-12-12 08:59:35 +00:00
return [ ]
}
2023-10-31 09:44:57 +00:00
func filterMembersToAdd ( _ ms : [ GMember ] ) -> [ Contact ] {
let memberContactIds = ms . compactMap { m in m . wrapped . memberCurrent ? m . wrapped . memberContactId : nil }
2022-07-30 13:03:44 +01:00
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
}
2023-03-06 13:54:43 +00:00
func apiCreateGroupLink ( _ groupId : Int64 , memberRole : GroupMemberRole = . member ) async throws -> ( String , GroupMemberRole ) {
let r = await chatSendCmd ( . apiCreateGroupLink ( groupId : groupId , memberRole : memberRole ) )
if case let . groupLinkCreated ( _ , _ , connReq , memberRole ) = r { return ( connReq , memberRole ) }
throw r
}
func apiGroupLinkMemberRole ( _ groupId : Int64 , memberRole : GroupMemberRole = . member ) async throws -> ( String , GroupMemberRole ) {
let r = await chatSendCmd ( . apiGroupLinkMemberRole ( groupId : groupId , memberRole : memberRole ) )
if case let . groupLink ( _ , _ , connReq , memberRole ) = r { return ( connReq , memberRole ) }
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
}
2023-03-06 13:54:43 +00:00
func apiGetGroupLink ( _ groupId : Int64 ) throws -> ( String , GroupMemberRole ) ? {
2022-10-15 18:09:25 +04:00
let r = chatSendCmdSync ( . apiGetGroupLink ( groupId : groupId ) )
switch r {
2023-03-06 13:54:43 +00:00
case let . groupLink ( _ , _ , connReq , memberRole ) :
return ( connReq , memberRole )
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-09-20 12:26:16 +04:00
func apiCreateMemberContact ( _ groupId : Int64 , _ groupMemberId : Int64 ) async throws -> Contact {
let r = await chatSendCmd ( . apiCreateMemberContact ( groupId : groupId , groupMemberId : groupMemberId ) )
if case let . newMemberContact ( _ , contact , _ , _ ) = r { return contact }
throw r
}
func apiSendMemberContactInvitation ( _ contactId : Int64 , _ msg : MsgContent ) async throws -> Contact {
let r = await chatSendCmd ( . apiSendMemberContactInvitation ( contactId : contactId , msg : msg ) , bgDelay : msgDelay )
if case let . newMemberContactSentInv ( _ , contact , _ , _ ) = r { return contact }
throw r
}
2023-01-22 18:34:01 +00:00
func apiGetVersion ( ) throws -> CoreVersionInfo {
let r = chatSendCmdSync ( . showVersion )
2023-03-27 18:34:48 +01:00
if case let . versionInfo ( info , _ , _ ) = r { return info }
2023-01-22 18:34:01 +00:00
throw r
}
2023-03-22 15:58:01 +00:00
private func currentUserId ( _ funcName : String ) throws -> Int64 {
if let userId = ChatModel . shared . currentUser ? . userId {
return userId
}
throw RuntimeError ( " \( funcName ) : no current user " )
}
2023-03-27 18:34:48 +01:00
func initializeChat ( start : Bool , dbKey : String ? = nil , refreshInvitations : Bool = true , confirmMigrations : MigrationConfirmation ? = 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
2023-03-27 18:34:48 +01:00
( m . chatDbEncrypted , m . chatDbStatus ) = chatMigrateInit ( dbKey , confirmMigrations : confirmMigrations )
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 )
}
2023-03-24 15:20:15 +04:00
try apiSetTempFolder ( tempFolder : getTempFilesDirectory ( ) . path )
2022-09-07 12:49:41 +01:00
try apiSetFilesFolder ( filesFolder : getAppFilesDirectory ( ) . path )
2023-03-24 15:20:15 +04:00
try setXFTPConfig ( getXFTPCfg ( ) )
2023-10-15 20:58:39 +01:00
try apiSetEncryptLocalFiles ( privacyEncryptLocalFilesGroupDefault . 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 {
2023-05-05 12:56:48 +04:00
onboardingStageDefault . set ( . step1_SimpleXInfo )
2023-07-13 23:48:25 +01:00
privacyDeliveryReceiptsSet . set ( true )
2022-09-07 12:49:41 +01:00
m . onboardingStage = . step1_SimpleXInfo
} else if start {
2023-03-16 19:57:43 +00:00
try startChat ( refreshInvitations : refreshInvitations )
2022-09-07 12:49:41 +01:00
} else {
m . chatRunning = false
2022-02-09 22:53:06 +00:00
}
}
2023-03-16 19:57:43 +00:00
func startChat ( refreshInvitations : Bool = true ) 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 ( ) )
2023-03-16 19:57:43 +00:00
if ( refreshInvitations ) {
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-05-05 12:56:48 +04:00
let savedOnboardingStage = onboardingStageDefault . get ( )
m . onboardingStage = [ . step1_SimpleXInfo , . step2_CreateProfile ] . contains ( savedOnboardingStage ) && m . users . count = = 1
2023-05-01 20:36:52 +04:00
? . step3_CreateSimpleXAddress
2023-05-05 12:56:48 +04:00
: savedOnboardingStage
2023-07-13 23:48:25 +01:00
if m . onboardingStage = = . onboardingComplete && ! privacyDeliveryReceiptsSet . get ( ) {
m . setDeliveryReceipts = true
}
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-03-22 15:58:01 +00:00
func changeActiveUser ( _ userId : Int64 , viewPwd : String ? ) {
2023-01-20 12:38:38 +00:00
do {
2023-03-22 15:58:01 +00:00
try changeActiveUser_ ( userId , viewPwd : viewPwd )
2023-01-20 12:38:38 +00:00
} catch let error {
logger . error ( " Unable to set active user: \( responseError ( error ) ) " )
}
}
2023-03-22 15:58:01 +00:00
private func changeActiveUser_ ( _ userId : Int64 , viewPwd : String ? ) throws {
2023-01-24 19:00:30 +00:00
let m = ChatModel . shared
2023-03-22 15:58:01 +00:00
m . currentUser = try apiSetActiveUser ( userId , viewPwd : viewPwd )
2023-01-24 19:00:30 +00:00
m . users = try listUsers ( )
try getUserChatData ( )
}
2023-03-22 15:58:01 +00:00
func changeActiveUserAsync_ ( _ userId : Int64 , viewPwd : String ? ) async throws {
let currentUser = try await apiSetActiveUserAsync ( userId , viewPwd : viewPwd )
let users = try await listUsersAsync ( )
await MainActor . run {
let m = ChatModel . shared
m . currentUser = currentUser
m . users = users
}
try await getUserChatDataAsync ( )
await MainActor . run {
if var ( _ , invitation ) = ChatModel . shared . callInvitations . first ( where : { _ , inv in inv . user . userId = = userId } ) {
invitation . user = currentUser
activateCall ( invitation )
}
}
}
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 . chatItemTTL = try getChatItemTTL ( )
let chats = try apiGetChats ( )
m . chats = chats . map { Chat . init ( $0 ) }
}
2023-03-22 15:58:01 +00:00
private func getUserChatDataAsync ( ) async throws {
let userAddress = try await apiGetUserAddressAsync ( )
let chatItemTTL = try await getChatItemTTLAsync ( )
let chats = try await apiGetChatsAsync ( )
await MainActor . run {
let m = ChatModel . shared
m . userAddress = userAddress
m . chatItemTTL = chatItemTTL
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 {
2023-08-17 18:21:05 +01:00
Task {
await TerminalItems . shared . add ( . resp ( . now , res ) )
}
2022-04-25 10:39:28 +01:00
let m = ChatModel . shared
2023-08-17 18:21:05 +01:00
logger . debug ( " processReceivedMsg: \( res . responseType ) " )
switch res {
case let . newContactConnection ( user , connection ) :
if active ( user ) {
await MainActor . run {
2023-01-19 16:22:56 +00:00
m . updateContactConnection ( connection )
}
2023-08-17 18:21:05 +01:00
}
case let . contactConnectionDeleted ( user , connection ) :
if active ( user ) {
await MainActor . run {
2023-01-19 16:22:56 +00:00
m . removeChat ( connection . id )
}
2023-08-17 18:21:05 +01:00
}
2023-09-27 20:07:32 +04:00
case let . contactDeletedByContact ( user , contact ) :
if active ( user ) && contact . directOrUsed {
await MainActor . run {
m . updateContact ( contact )
}
}
2023-08-17 18:21:05 +01:00
case let . contactConnected ( user , contact , _ ) :
if active ( user ) && contact . directOrUsed {
await MainActor . run {
2022-10-24 00:18:15 +04:00
m . updateContact ( contact )
2023-11-10 10:16:06 +04:00
if let conn = contact . activeConn {
m . dismissConnReqView ( conn . id )
m . removeChat ( conn . id )
}
2023-02-02 16:09:36 +00:00
}
2023-08-17 18:21:05 +01:00
}
if contact . directOrUsed {
NtfManager . shared . notifyContactConnected ( user , contact )
}
await MainActor . run {
2023-01-20 17:35:39 +04:00
m . setContactNetworkStatus ( contact , . connected )
2023-08-17 18:21:05 +01:00
}
case let . contactConnecting ( user , contact ) :
if active ( user ) && contact . directOrUsed {
await MainActor . run {
2022-10-24 00:18:15 +04:00
m . updateContact ( contact )
2023-11-10 10:16:06 +04:00
if let conn = contact . activeConn {
m . dismissConnReqView ( conn . id )
m . removeChat ( conn . id )
}
2022-10-24 00:18:15 +04:00
}
2023-08-17 18:21:05 +01:00
}
case let . receivedContactRequest ( user , contactRequest ) :
if active ( user ) {
let cInfo = ChatInfo . contactRequest ( contactRequest : contactRequest )
await MainActor . run {
2023-03-22 15:58:01 +00:00
if m . hasChat ( contactRequest . id ) {
m . updateChatInfo ( cInfo )
} else {
m . addChat ( Chat (
chatInfo : cInfo ,
chatItems : [ ]
) )
}
2022-07-22 08:10:37 +01:00
}
2023-08-17 18:21:05 +01:00
}
NtfManager . shared . notifyContactRequest ( user , contactRequest )
case let . contactUpdated ( user , toContact ) :
if active ( user ) && m . hasChat ( toContact . id ) {
await MainActor . run {
2023-01-19 16:22:56 +00:00
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-08-17 18:21:05 +01:00
}
2023-10-31 10:51:02 +04:00
case let . groupMemberUpdated ( user , groupInfo , _ , toMember ) :
if active ( user ) {
await MainActor . run {
_ = m . upsertGroupMember ( groupInfo , toMember )
}
}
2023-08-17 18:21:05 +01:00
case let . contactsMerged ( user , intoContact , mergedContact ) :
if active ( user ) && m . hasChat ( mergedContact . id ) {
await MainActor . run {
2022-10-28 20:05:04 +04:00
if m . chatId = = mergedContact . id {
m . chatId = intoContact . id
}
m . removeChat ( mergedContact . id )
}
2023-08-17 18:21:05 +01:00
}
case let . contactsSubscribed ( _ , contactRefs ) :
await updateContactsStatus ( contactRefs , status : . connected )
case let . contactsDisconnected ( _ , contactRefs ) :
await updateContactsStatus ( contactRefs , status : . disconnected )
case let . contactSubSummary ( _ , contactSubscriptions ) :
await MainActor . run {
2022-02-25 20:26:56 +00:00
for sub in contactSubscriptions {
2023-08-17 18:21:05 +01:00
// n o n e e d t o u p d a t e c o n t a c t h e r e , a n d i t i s s l o w
// i f a c t i v e ( u s e r ) {
// m . u p d a t e C o n t a c t ( s u b . c o n t a c t )
// }
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-08-17 18:21:05 +01:00
}
2023-10-13 11:51:01 +01:00
case let . networkStatus ( status , connections ) :
await MainActor . run {
for cId in connections {
m . networkStatuses [ cId ] = status
}
}
2023-10-13 14:29:48 +01:00
case let . networkStatuses ( _ , statuses ) : ( )
2023-10-13 11:51:01 +01:00
await MainActor . run {
for s in statuses {
m . networkStatuses [ s . agentConnId ] = s . networkStatus
}
}
2023-08-17 18:21:05 +01:00
case let . newChatItem ( user , aChatItem ) :
let cInfo = aChatItem . chatInfo
let cItem = aChatItem . chatItem
await MainActor . run {
2023-02-02 16:09:36 +00:00
if active ( user ) {
m . addChatItem ( cInfo , cItem )
} else if cItem . isRcvNew && cInfo . ntfsEnabled {
m . increaseUnreadCounter ( user : user )
}
2023-08-17 18:21:05 +01:00
}
if let file = cItem . autoReceiveFile ( ) {
Task {
2023-09-07 11:28:37 +01:00
await receiveFile ( user : user , fileId : file . fileId , encrypted : cItem . encryptLocalFile , auto : true )
2022-04-19 12:29:03 +04:00
}
2023-08-17 18:21:05 +01:00
}
if cItem . showNotification {
NtfManager . shared . notifyMessageReceived ( user , cInfo , cItem )
}
case let . chatItemStatusUpdated ( user , aChatItem ) :
let cInfo = aChatItem . chatInfo
let cItem = aChatItem . chatItem
2023-10-18 11:23:35 +01:00
if ! cItem . isDeletedContent && active ( user ) {
await MainActor . run { m . updateChatItem ( cInfo , cItem , status : cItem . meta . itemStatus ) }
2023-08-17 18:21:05 +01:00
}
if let endTask = m . messageDelivery [ cItem . id ] {
switch cItem . meta . itemStatus {
case . sndSent : endTask ( )
case . sndErrorAuth : endTask ( )
case . sndError : endTask ( )
default : ( )
2022-02-12 15:59:43 +00:00
}
2023-08-17 18:21:05 +01:00
}
case let . chatItemUpdated ( user , aChatItem ) :
await chatItemSimpleUpdate ( user , aChatItem )
case let . chatItemReaction ( user , _ , r ) :
if active ( user ) {
await MainActor . run {
2023-05-16 10:34:25 +02:00
m . updateChatItem ( r . chatInfo , r . chatReaction . chatItem )
}
2023-08-17 18:21:05 +01:00
}
case let . chatItemDeleted ( user , deletedChatItem , toChatItem , _ ) :
if ! active ( user ) {
if toChatItem = = nil && deletedChatItem . chatItem . isRcvNew && deletedChatItem . chatInfo . ntfsEnabled {
await MainActor . run {
2023-01-19 16:22:56 +00:00
m . decreaseUnreadCounter ( user : user )
}
}
2023-08-17 18:21:05 +01:00
return
}
2023-01-19 16:22:56 +00:00
2023-08-17 18:21:05 +01:00
await MainActor . run {
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-08-17 18:21:05 +01:00
}
case let . receivedGroupInvitation ( user , groupInfo , _ , _ ) :
if active ( user ) {
await MainActor . run {
2023-01-19 16:22:56 +00:00
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 ?
}
2023-08-17 18:21:05 +01:00
}
case let . userAcceptedGroupSent ( user , groupInfo , hostContact ) :
if ! active ( user ) { return }
2023-01-19 16:22:56 +00:00
2023-08-17 18:21:05 +01:00
await MainActor . run {
2022-11-04 15:33:29 +04:00
m . updateGroup ( groupInfo )
2023-11-10 10:16:06 +04:00
if let conn = hostContact ? . activeConn {
m . dismissConnReqView ( conn . id )
m . removeChat ( conn . id )
2022-11-05 17:48:57 +04:00
}
2023-08-17 18:21:05 +01:00
}
2023-10-31 10:51:02 +04:00
case let . groupLinkConnecting ( user , groupInfo , hostMember ) :
if ! active ( user ) { return }
await MainActor . run {
m . updateGroup ( groupInfo )
if let hostConn = hostMember . activeConn {
m . dismissConnReqView ( hostConn . id )
m . removeChat ( hostConn . id )
}
}
2023-08-17 18:21:05 +01:00
case let . joinedGroupMemberConnecting ( user , groupInfo , _ , member ) :
if active ( user ) {
await MainActor . run {
2023-01-19 16:22:56 +00:00
_ = m . upsertGroupMember ( groupInfo , member )
}
2023-08-17 18:21:05 +01:00
}
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 ) {
await MainActor . run {
2023-01-19 16:22:56 +00:00
m . updateGroup ( groupInfo )
}
2023-08-17 18:21:05 +01:00
}
case let . deletedMember ( user , groupInfo , _ , deletedMember ) :
if active ( user ) {
await MainActor . run {
2023-01-19 16:22:56 +00:00
_ = m . upsertGroupMember ( groupInfo , deletedMember )
}
2023-08-17 18:21:05 +01:00
}
case let . leftMember ( user , groupInfo , member ) :
if active ( user ) {
await MainActor . run {
2023-01-19 16:22:56 +00:00
_ = m . upsertGroupMember ( groupInfo , member )
}
2023-08-17 18:21:05 +01:00
}
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 ) {
await MainActor . run {
2023-01-19 16:22:56 +00:00
m . updateGroup ( groupInfo )
}
2023-08-17 18:21:05 +01:00
}
case let . userJoinedGroup ( user , groupInfo ) :
if active ( user ) {
await MainActor . run {
2023-01-19 16:22:56 +00:00
m . updateGroup ( groupInfo )
}
2023-08-17 18:21:05 +01:00
}
case let . joinedGroupMember ( user , groupInfo , member ) :
if active ( user ) {
await MainActor . run {
2023-01-19 16:22:56 +00:00
_ = m . upsertGroupMember ( groupInfo , member )
}
2023-08-17 18:21:05 +01:00
}
case let . connectedToGroupMember ( user , groupInfo , member , memberContact ) :
if active ( user ) {
await MainActor . run {
2023-01-19 16:22:56 +00:00
_ = m . upsertGroupMember ( groupInfo , member )
}
2023-08-17 18:21:05 +01:00
}
if let contact = memberContact {
await MainActor . run {
2023-06-09 16:43:53 +04:00
m . setContactNetworkStatus ( contact , . connected )
}
2023-08-17 18:21:05 +01:00
}
case let . groupUpdated ( user , toGroup ) :
if active ( user ) {
await MainActor . run {
2023-01-19 16:22:56 +00:00
m . updateGroup ( toGroup )
}
2023-08-17 18:21:05 +01:00
}
2023-10-31 09:44:57 +00:00
case let . memberRole ( user , groupInfo , byMember : _ , member : member , fromRole : _ , toRole : _ ) :
2023-08-17 18:21:05 +01:00
if active ( user ) {
await MainActor . run {
2023-03-06 13:54:43 +00:00
m . updateGroup ( groupInfo )
2023-10-31 09:44:57 +00:00
_ = m . upsertGroupMember ( groupInfo , member )
2023-03-06 13:54:43 +00:00
}
2023-08-17 18:21:05 +01:00
}
2023-09-20 12:26:16 +04:00
case let . newMemberContactReceivedInv ( user , contact , _ , _ ) :
if active ( user ) {
await MainActor . run {
m . updateContact ( contact )
}
}
2023-08-17 18:21:05 +01:00
case let . rcvFileAccepted ( user , aChatItem ) : // u s u a l l y r c v F i l e A c c e p t e d i s a r e s p o n s e , b u t i t ' s a l s o a n e v e n t f o r X F T P f i l e s a u t o - a c c e p t e d f r o m N S E
await chatItemSimpleUpdate ( user , aChatItem )
case let . rcvFileStart ( user , aChatItem ) :
await chatItemSimpleUpdate ( user , aChatItem )
case let . rcvFileComplete ( user , aChatItem ) :
await chatItemSimpleUpdate ( user , aChatItem )
case let . rcvFileSndCancelled ( user , aChatItem , _ ) :
await chatItemSimpleUpdate ( user , aChatItem )
Task { cleanupFile ( aChatItem ) }
case let . rcvFileProgressXFTP ( user , aChatItem , _ , _ ) :
await chatItemSimpleUpdate ( user , aChatItem )
case let . rcvFileError ( user , aChatItem ) :
await chatItemSimpleUpdate ( user , aChatItem )
Task { cleanupFile ( aChatItem ) }
case let . sndFileStart ( user , aChatItem , _ ) :
await chatItemSimpleUpdate ( user , aChatItem )
case let . sndFileComplete ( user , aChatItem , _ ) :
await chatItemSimpleUpdate ( user , aChatItem )
Task { cleanupDirectFile ( aChatItem ) }
case let . sndFileRcvCancelled ( user , aChatItem , _ ) :
await chatItemSimpleUpdate ( user , aChatItem )
Task { cleanupDirectFile ( aChatItem ) }
case let . sndFileProgressXFTP ( user , aChatItem , _ , _ , _ ) :
await chatItemSimpleUpdate ( user , aChatItem )
case let . sndFileCompleteXFTP ( user , aChatItem , _ ) :
await chatItemSimpleUpdate ( user , aChatItem )
Task { cleanupFile ( aChatItem ) }
case let . sndFileError ( user , aChatItem ) :
await chatItemSimpleUpdate ( user , aChatItem )
Task { cleanupFile ( aChatItem ) }
case let . callInvitation ( invitation ) :
2023-08-21 13:56:53 +01:00
await MainActor . run {
m . callInvitations [ invitation . contact . id ] = invitation
}
2023-08-17 18:21:05 +01:00
activateCall ( invitation )
case let . callOffer ( _ , contact , callType , offer , sharedKey , _ ) :
await withCall ( contact ) { call in
call . callState = . offerReceived
call . peerMedia = callType . media
call . sharedKey = sharedKey
let useRelay = UserDefaults . standard . bool ( forKey : DEFAULT_WEBRTC_POLICY_RELAY )
let iceServers = getIceServers ( )
logger . debug ( " .callOffer useRelay \( useRelay ) " )
logger . debug ( " .callOffer iceServers \( String ( describing : iceServers ) ) " )
m . callCommand = . offer (
offer : offer . rtcSession ,
iceCandidates : offer . rtcIceCandidates ,
media : callType . media , aesKey : sharedKey ,
iceServers : iceServers ,
relay : useRelay
)
}
case let . callAnswer ( _ , contact , answer ) :
await withCall ( contact ) { call in
call . callState = . answerReceived
m . callCommand = . answer ( answer : answer . rtcSession , iceCandidates : answer . rtcIceCandidates )
}
case let . callExtraInfo ( _ , contact , extraInfo ) :
await withCall ( contact ) { _ in
m . callCommand = . ice ( iceCandidates : extraInfo . rtcIceCandidates )
}
case let . callEnded ( _ , contact ) :
if let invitation = await MainActor . run ( body : { m . callInvitations . removeValue ( forKey : contact . id ) } ) {
CallController . shared . reportCallRemoteEnded ( invitation : invitation )
}
await withCall ( contact ) { call in
m . callCommand = . end
CallController . shared . reportCallRemoteEnded ( call : call )
}
case . chatSuspended :
chatSuspended ( )
case let . contactSwitch ( _ , contact , switchProgress ) :
await MainActor . run {
2023-07-10 19:01:22 +04:00
m . updateContactConnectionStats ( contact , switchProgress . connectionStats )
2023-08-17 18:21:05 +01:00
}
case let . groupMemberSwitch ( _ , groupInfo , member , switchProgress ) :
await MainActor . run {
2023-07-10 19:01:22 +04:00
m . updateGroupMemberConnectionStats ( groupInfo , member , switchProgress . connectionStats )
2023-08-17 18:21:05 +01:00
}
case let . contactRatchetSync ( _ , contact , ratchetSyncProgress ) :
await MainActor . run {
2023-07-10 19:01:22 +04:00
m . updateContactConnectionStats ( contact , ratchetSyncProgress . connectionStats )
2023-08-17 18:21:05 +01:00
}
case let . groupMemberRatchetSync ( _ , groupInfo , member , ratchetSyncProgress ) :
await MainActor . run {
2023-07-10 19:01:22 +04:00
m . updateGroupMemberConnectionStats ( groupInfo , member , ratchetSyncProgress . connectionStats )
2022-01-29 11:10:04 +00:00
}
2023-11-16 16:53:44 +00:00
case let . remoteCtrlFound ( remoteCtrl ) :
// T O D O m u l t i c a s t
logger . debug ( " \( String ( describing : remoteCtrl ) ) " )
case let . remoteCtrlSessionCode ( remoteCtrl_ , sessionCode ) :
await MainActor . run {
let state = UIRemoteCtrlSessionState . pendingConfirmation ( remoteCtrl_ : remoteCtrl_ , sessionCode : sessionCode )
m . remoteCtrlSession = m . remoteCtrlSession ? . updateState ( state )
}
case let . remoteCtrlConnected ( remoteCtrl ) :
// T O D O c u r r e n t l y i t i s r e t u r n e d i n r e s p o n s e t o c o m m a n d , s o i t i s r e d u n d a n t
await MainActor . run {
let state = UIRemoteCtrlSessionState . connected ( remoteCtrl : remoteCtrl , sessionCode : m . remoteCtrlSession ? . sessionCode ? ? " " )
m . remoteCtrlSession = m . remoteCtrlSession ? . updateState ( state )
}
case . remoteCtrlStopped :
2023-11-18 22:20:22 +00:00
// T h i s d e l a y i s n e e d e d t o c a n c e l t h e s e s s i o n t h a t f a i l s o n n e t w o r k f a i l u r e ,
// e . g . w h e n u s e r d i d n o t g r a n t p e r m i s s i o n t o a c c e s s l o c a l n e t w o r k y e t .
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.1 ) {
2023-11-16 16:53:44 +00:00
switchToLocalSession ( )
}
2023-08-17 18:21:05 +01:00
default :
logger . debug ( " unsupported event: \( res . responseType ) " )
}
2022-05-07 06:40:46 +01:00
2023-08-17 18:21:05 +01:00
func withCall ( _ contact : Contact , _ perform : ( Call ) -> Void ) async {
if let call = m . activeCall , call . contact . apiId = = contact . apiId {
await MainActor . run { perform ( call ) }
} else {
logger . debug ( " processReceivedMsg: ignoring \( res . responseType ) , not in call with the contact \( contact . id ) " )
2022-05-07 06:40:46 +01:00
}
2022-01-29 11:10:04 +00:00
}
}
2023-11-16 16:53:44 +00:00
func switchToLocalSession ( ) {
let m = ChatModel . shared
m . remoteCtrlSession = nil
do {
m . users = try listUsers ( )
try getUserChatData ( )
let statuses = ( try apiGetNetworkStatuses ( ) ) . map { s in ( s . agentConnId , s . networkStatus ) }
m . networkStatuses = Dictionary ( uniqueKeysWithValues : statuses )
} catch let error {
logger . debug ( " error updating chat data: \( responseError ( error ) ) " )
}
}
2023-08-22 12:20:21 +01:00
func active ( _ user : any UserLike ) -> Bool {
user . userId = = ChatModel . shared . currentUser ? . id
2023-02-02 16:09:36 +00:00
}
2023-08-22 12:20:21 +01:00
func chatItemSimpleUpdate ( _ user : any UserLike , _ aChatItem : AChatItem ) async {
2022-05-04 09:10:36 +04:00
let m = ChatModel . shared
let cInfo = aChatItem . chatInfo
let cItem = aChatItem . chatItem
2023-08-17 18:21:05 +01:00
if active ( user ) {
if await MainActor . run ( body : { m . upsertChatItem ( cInfo , cItem ) } ) {
NtfManager . shared . notifyMessageReceived ( user , cInfo , cItem )
}
2022-05-04 09:10:36 +04:00
}
}
2023-08-17 18:21:05 +01:00
func updateContactsStatus ( _ contactRefs : [ ContactRef ] , status : NetworkStatus ) async {
2022-02-25 20:26:56 +00:00
let m = ChatModel . shared
2023-08-17 18:21:05 +01:00
await MainActor . run {
for c in contactRefs {
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-10-13 11:51:01 +01:00
m . setContactNetworkStatus ( contact , . error ( connectionError : err ) )
2022-02-25 20:26:56 +00:00
}
2022-07-05 15:15:15 +04:00
func refreshCallInvitations ( ) throws {
let m = ChatModel . shared
2023-03-14 11:12:40 +03:00
let callInvitations = try justRefreshCallInvitations ( )
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 )
2023-03-22 15:58:01 +00:00
} else if let invitation = callInvitations . last ( where : { $0 . user . showNotifications } ) {
2022-07-22 08:10:37 +01:00
activateCall ( invitation )
2022-07-05 15:15:15 +04:00
}
}
2023-03-14 11:12:40 +03:00
func justRefreshCallInvitations ( ) throws -> [ RcvCallInvitation ] {
let m = ChatModel . shared
let callInvitations = try apiGetCallInvitations ( )
m . callInvitations = callInvitations . reduce ( into : [ ChatId : RcvCallInvitation ] ( ) ) { result , inv in result [ inv . contact . id ] = inv }
return callInvitations
}
2022-07-05 15:15:15 +04:00
func activateCall ( _ callInvitation : RcvCallInvitation ) {
2023-03-22 15:58:01 +00:00
if ! callInvitation . user . showNotifications { return }
2022-07-05 15:15:15 +04:00
let m = ChatModel . shared
CallController . shared . reportNewIncomingCall ( invitation : callInvitation ) { error in
if let error = error {
2023-08-17 18:21:05 +01:00
DispatchQueue . main . async {
m . callInvitations [ callInvitation . contact . id ] ? . callkitUUID = nil
}
2022-07-05 15:15:15 +04:00
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 ?
}