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-01-29 11:10:04 +00:00
private var chatController : chat_ctrl ?
2022-01-29 23:37:02 +00:00
private let jsonDecoder = getJSONDecoder ( )
2022-04-08 18:17:10 +01:00
let jsonEncoder = getJSONEncoder ( )
2022-01-29 11:10:04 +00:00
enum ChatCommand {
2022-02-06 16:18:01 +00:00
case showActiveUser
case createActiveUser ( profile : Profile )
case startChat
2022-04-19 12:29:03 +04:00
case setFilesFolder ( filesFolder : String )
2022-01-29 11:10:04 +00:00
case apiGetChats
2022-01-30 18:27:20 +00:00
case apiGetChat ( type : ChatType , id : Int64 )
2022-04-19 12:29:03 +04:00
case apiSendMessage ( type : ChatType , id : Int64 , file : String ? , quotedItemId : Int64 ? , msg : MsgContent )
2022-03-30 20:37:47 +04:00
case apiUpdateChatItem ( type : ChatType , id : Int64 , itemId : Int64 , msg : MsgContent )
case apiDeleteChatItem ( type : ChatType , id : Int64 , itemId : Int64 , mode : CIDeleteMode )
2022-04-21 20:04:22 +01:00
case apiRegisterToken ( token : String )
2022-04-22 13:46:05 +01:00
case apiVerifyToken ( token : String , code : String , nonce : String )
case apiIntervalNofication ( token : String , interval : Int )
case apiDeleteToken ( token : String )
2022-03-10 15:45:40 +04:00
case getUserSMPServers
case setUserSMPServers ( smpServers : [ String ] )
2022-01-30 18:27:20 +00:00
case addContact
case connect ( connReq : String )
2022-01-31 21:28:07 +00:00
case apiDeleteChat ( type : ChatType , id : Int64 )
2022-03-25 22:13:01 +04:00
case apiUpdateProfile ( profile : Profile )
2022-04-08 18:17:10 +01:00
case apiParseMarkdown ( text : String )
2022-02-01 17:34:06 +00:00
case createMyAddress
case deleteMyAddress
case showMyAddress
case apiAcceptContact ( contactReqId : Int64 )
case apiRejectContact ( contactReqId : Int64 )
2022-02-12 15:59:43 +00:00
case apiChatRead ( type : ChatType , id : Int64 , itemRange : ( Int64 , Int64 ) )
2022-04-19 12:29:03 +04:00
case receiveFile ( fileId : Int64 )
2022-01-29 11:10:04 +00:00
case string ( String )
var cmdString : String {
get {
switch self {
2022-02-06 16:18:01 +00:00
case . showActiveUser : return " /u "
case let . createActiveUser ( profile ) : return " /u \( profile . displayName ) \( profile . fullName ) "
case . startChat : return " /_start "
2022-04-19 12:29:03 +04:00
case let . setFilesFolder ( filesFolder ) : return " /_files_folder \( filesFolder ) "
2022-02-06 16:18:01 +00:00
case . apiGetChats : return " /_get chats "
2022-02-12 15:59:43 +00:00
case let . apiGetChat ( type , id ) : return " /_get chat \( ref ( type , id ) ) count=100 "
2022-04-19 12:29:03 +04:00
case let . apiSendMessage ( type , id , file , quotedItemId , mc ) :
switch ( file , quotedItemId ) {
case ( nil , nil ) : return " /_send \( ref ( type , id ) ) \( mc . cmdString ) "
case let ( . some ( file ) , nil ) : return " /_send \( ref ( type , id ) ) file \( file ) \( mc . cmdString ) "
case let ( nil , . some ( quotedItemId ) ) : return " /_send \( ref ( type , id ) ) quoted \( quotedItemId ) \( mc . cmdString ) "
case let ( . some ( file ) , . some ( quotedItemId ) ) : return " /_send \( ref ( type , id ) ) file \( file ) quoted \( quotedItemId ) \( mc . cmdString ) "
}
2022-03-30 20:37:47 +04:00
case let . apiUpdateChatItem ( type , id , itemId , mc ) : return " /_update item \( ref ( type , id ) ) \( itemId ) \( mc . cmdString ) "
case let . apiDeleteChatItem ( type , id , itemId , mode ) : return " /_delete item \( ref ( type , id ) ) \( itemId ) \( mode . rawValue ) "
2022-04-22 13:46:05 +01:00
case let . apiRegisterToken ( token ) : return " /_ntf register apns \( token ) "
case let . apiVerifyToken ( token , code , nonce ) : return " /_ntf verify apns \( token ) \( code ) \( nonce ) "
case let . apiIntervalNofication ( token , interval ) : return " /_ntf interval apns \( token ) \( interval ) "
case let . apiDeleteToken ( token ) : return " /_ntf delete apns \( token ) "
2022-03-10 15:45:40 +04:00
case . getUserSMPServers : return " /smp_servers "
case let . setUserSMPServers ( smpServers ) : return " /smp_servers \( smpServersStr ( smpServers : smpServers ) ) "
2022-02-06 16:18:01 +00:00
case . addContact : return " /connect "
case let . connect ( connReq ) : return " /connect \( connReq ) "
2022-02-12 15:59:43 +00:00
case let . apiDeleteChat ( type , id ) : return " /_delete \( ref ( type , id ) ) "
2022-03-25 22:13:01 +04:00
case let . apiUpdateProfile ( profile ) : return " /_profile \( encodeJSON ( profile ) ) "
2022-04-08 18:17:10 +01:00
case let . apiParseMarkdown ( text ) : return " /_parse \( text ) "
2022-02-06 16:18:01 +00:00
case . createMyAddress : return " /address "
case . deleteMyAddress : return " /delete_address "
case . showMyAddress : return " /show_address "
case let . apiAcceptContact ( contactReqId ) : return " /_accept \( contactReqId ) "
case let . apiRejectContact ( contactReqId ) : return " /_reject \( contactReqId ) "
2022-02-12 15:59:43 +00:00
case let . apiChatRead ( type , id , itemRange : ( from , to ) ) : return " /_read chat \( ref ( type , id ) ) from= \( from ) to= \( to ) "
2022-04-19 12:29:03 +04:00
case let . receiveFile ( fileId ) : return " /freceive \( fileId ) "
2022-02-06 16:18:01 +00:00
case let . string ( str ) : return str
2022-01-29 11:10:04 +00:00
}
}
}
2022-02-12 15:59:43 +00:00
var cmdType : String {
get {
switch self {
case . showActiveUser : return " showActiveUser "
case . createActiveUser : return " createActiveUser "
case . startChat : return " startChat "
2022-04-19 12:29:03 +04:00
case . setFilesFolder : return " setFilesFolder "
2022-02-12 15:59:43 +00:00
case . apiGetChats : return " apiGetChats "
case . apiGetChat : return " apiGetChat "
case . apiSendMessage : return " apiSendMessage "
2022-03-30 20:37:47 +04:00
case . apiUpdateChatItem : return " apiUpdateChatItem "
case . apiDeleteChatItem : return " apiDeleteChatItem "
2022-04-21 20:04:22 +01:00
case . apiRegisterToken : return " apiRegisterToken "
2022-04-22 17:53:09 +01:00
case . apiVerifyToken : return " apiVerifyToken "
2022-04-22 13:46:05 +01:00
case . apiIntervalNofication : return " apiIntervalNofication "
case . apiDeleteToken : return " apiDeleteToken "
2022-03-10 15:45:40 +04:00
case . getUserSMPServers : return " getUserSMPServers "
case . setUserSMPServers : return " setUserSMPServers "
2022-02-12 15:59:43 +00:00
case . addContact : return " addContact "
case . connect : return " connect "
case . apiDeleteChat : return " apiDeleteChat "
2022-03-25 22:13:01 +04:00
case . apiUpdateProfile : return " apiUpdateProfile "
2022-04-08 18:17:10 +01:00
case . apiParseMarkdown : return " apiParseMarkdown "
2022-02-12 15:59:43 +00:00
case . createMyAddress : return " createMyAddress "
case . deleteMyAddress : return " deleteMyAddress "
case . showMyAddress : return " showMyAddress "
case . apiAcceptContact : return " apiAcceptContact "
case . apiRejectContact : return " apiRejectContact "
case . apiChatRead : return " apiChatRead "
2022-04-19 12:29:03 +04:00
case . receiveFile : return " receiveFile "
2022-02-12 15:59:43 +00:00
case . string : return " console command "
}
}
}
func ref ( _ type : ChatType , _ id : Int64 ) -> String {
" \( type . rawValue ) \( id ) "
}
2022-04-19 12:29:03 +04:00
2022-03-10 15:45:40 +04:00
func smpServersStr ( smpServers : [ String ] ) -> String {
smpServers . isEmpty ? " default " : smpServers . joined ( separator : " , " )
}
2022-01-29 11:10:04 +00:00
}
2022-01-29 23:37:02 +00:00
struct APIResponse : Decodable {
2022-01-29 11:10:04 +00:00
var resp : ChatResponse
}
2022-01-29 23:37:02 +00:00
enum ChatResponse : Decodable , Error {
2022-01-29 11:10:04 +00:00
case response ( type : String , json : String )
2022-02-06 16:18:01 +00:00
case activeUser ( user : User )
case chatStarted
2022-02-26 20:21:32 +00:00
case chatRunning
2022-02-02 12:51:39 +00:00
case apiChats ( chats : [ ChatData ] )
case apiChat ( chat : ChatData )
2022-03-10 15:45:40 +04:00
case userSMPServers ( smpServers : [ String ] )
2022-01-30 18:27:20 +00:00
case invitation ( connReqInvitation : String )
case sentConfirmation
case sentInvitation
2022-04-02 14:35:35 +01:00
case contactAlreadyExists ( contact : Contact )
2022-01-31 21:28:07 +00:00
case contactDeleted ( contact : Contact )
case userProfileNoChange
case userProfileUpdated ( fromProfile : Profile , toProfile : Profile )
2022-04-08 18:17:10 +01:00
case apiParsedMarkdown ( formattedText : [ FormattedText ] ? )
2022-02-01 17:34:06 +00:00
case userContactLink ( connReqContact : String )
case userContactLinkCreated ( connReqContact : String )
case userContactLinkDeleted
2022-01-29 11:10:04 +00:00
case contactConnected ( contact : Contact )
2022-04-22 17:26:17 +01:00
case contactConnecting ( contact : Contact )
2022-02-01 17:34:06 +00:00
case receivedContactRequest ( contactRequest : UserContactRequest )
case acceptingContactRequest ( contact : Contact )
case contactRequestRejected
2022-02-04 22:13:52 +00:00
case contactUpdated ( toContact : Contact )
2022-02-05 20:10:47 +00:00
case contactSubscribed ( contact : Contact )
case contactDisconnected ( contact : Contact )
case contactSubError ( contact : Contact , chatError : ChatError )
2022-02-25 20:26:56 +00:00
case contactSubSummary ( contactSubscriptions : [ ContactSubStatus ] )
2022-02-09 22:53:06 +00:00
case groupSubscribed ( groupInfo : GroupInfo )
2022-02-25 20:26:56 +00:00
case memberSubErrors ( memberSubErrors : [ MemberSubError ] )
2022-02-09 22:53:06 +00:00
case groupEmpty ( groupInfo : GroupInfo )
case userContactLinkSubscribed
2022-01-29 23:37:02 +00:00
case newChatItem ( chatItem : AChatItem )
2022-03-25 22:26:05 +04:00
case chatItemStatusUpdated ( chatItem : AChatItem )
2022-02-12 15:59:43 +00:00
case chatItemUpdated ( chatItem : AChatItem )
2022-03-30 20:37:47 +04:00
case chatItemDeleted ( deletedChatItem : AChatItem , toChatItem : AChatItem )
2022-04-19 12:29:03 +04:00
case rcvFileAccepted
case rcvFileComplete ( chatItem : AChatItem )
2022-02-12 15:59:43 +00:00
case cmdOk
2022-02-01 17:34:06 +00:00
case chatCmdError ( chatError : ChatError )
2022-02-06 16:18:01 +00:00
case chatError ( chatError : ChatError )
2022-01-29 11:10:04 +00:00
var responseType : String {
get {
switch self {
case let . response ( type , _ ) : return " * \( type ) "
2022-02-06 16:18:01 +00:00
case . activeUser : return " activeUser "
case . chatStarted : return " chatStarted "
2022-02-26 20:21:32 +00:00
case . chatRunning : return " chatRunning "
2022-01-29 23:37:02 +00:00
case . apiChats : return " apiChats "
2022-01-30 18:27:20 +00:00
case . apiChat : return " apiChat "
2022-03-10 15:45:40 +04:00
case . userSMPServers : return " userSMPServers "
2022-01-30 18:27:20 +00:00
case . invitation : return " invitation "
case . sentConfirmation : return " sentConfirmation "
case . sentInvitation : return " sentInvitation "
2022-04-02 14:35:35 +01:00
case . contactAlreadyExists : return " contactAlreadyExists "
2022-01-31 21:28:07 +00:00
case . contactDeleted : return " contactDeleted "
case . userProfileNoChange : return " userProfileNoChange "
2022-03-25 22:13:01 +04:00
case . userProfileUpdated : return " userProfileUpdated "
2022-04-08 18:17:10 +01:00
case . apiParsedMarkdown : return " apiParsedMarkdown "
2022-02-01 17:34:06 +00:00
case . userContactLink : return " userContactLink "
case . userContactLinkCreated : return " userContactLinkCreated "
case . userContactLinkDeleted : return " userContactLinkDeleted "
2022-01-29 23:37:02 +00:00
case . contactConnected : return " contactConnected "
2022-04-22 17:26:17 +01:00
case . contactConnecting : return " contactConnecting "
2022-02-01 17:34:06 +00:00
case . receivedContactRequest : return " receivedContactRequest "
case . acceptingContactRequest : return " acceptingContactRequest "
case . contactRequestRejected : return " contactRequestRejected "
2022-02-04 22:13:52 +00:00
case . contactUpdated : return " contactUpdated "
2022-02-05 20:10:47 +00:00
case . contactSubscribed : return " contactSubscribed "
case . contactDisconnected : return " contactDisconnected "
case . contactSubError : return " contactSubError "
2022-02-25 20:26:56 +00:00
case . contactSubSummary : return " contactSubSummary "
2022-02-09 22:53:06 +00:00
case . groupSubscribed : return " groupSubscribed "
2022-02-25 20:26:56 +00:00
case . memberSubErrors : return " memberSubErrors "
2022-02-09 22:53:06 +00:00
case . groupEmpty : return " groupEmpty "
case . userContactLinkSubscribed : return " userContactLinkSubscribed "
2022-01-29 23:37:02 +00:00
case . newChatItem : return " newChatItem "
2022-03-25 22:26:05 +04:00
case . chatItemStatusUpdated : return " chatItemStatusUpdated "
2022-02-12 15:59:43 +00:00
case . chatItemUpdated : return " chatItemUpdated "
2022-03-25 22:26:05 +04:00
case . chatItemDeleted : return " chatItemDeleted "
2022-04-19 12:29:03 +04:00
case . rcvFileAccepted : return " rcvFileAccepted "
case . rcvFileComplete : return " rcvFileComplete "
2022-02-12 15:59:43 +00:00
case . cmdOk : return " cmdOk "
2022-02-01 17:34:06 +00:00
case . chatCmdError : return " chatCmdError "
2022-02-06 16:18:01 +00:00
case . chatError : return " chatError "
2022-01-29 11:10:04 +00:00
}
}
}
2022-04-19 12:29:03 +04:00
2022-01-29 11:10:04 +00:00
var details : String {
get {
switch self {
case let . response ( _ , json ) : return json
2022-02-06 16:18:01 +00:00
case let . activeUser ( user ) : return String ( describing : user )
case . chatStarted : return noDetails
2022-02-26 20:21:32 +00:00
case . chatRunning : return noDetails
2022-01-29 11:10:04 +00:00
case let . apiChats ( chats ) : return String ( describing : chats )
2022-01-30 18:27:20 +00:00
case let . apiChat ( chat ) : return String ( describing : chat )
2022-03-10 15:45:40 +04:00
case let . userSMPServers ( smpServers ) : return String ( describing : smpServers )
2022-01-30 18:27:20 +00:00
case let . invitation ( connReqInvitation ) : return connReqInvitation
2022-02-01 17:34:06 +00:00
case . sentConfirmation : return noDetails
case . sentInvitation : return noDetails
2022-04-02 14:35:35 +01:00
case let . contactAlreadyExists ( contact ) : return String ( describing : contact )
2022-01-31 21:28:07 +00:00
case let . contactDeleted ( contact ) : return String ( describing : contact )
2022-02-01 17:34:06 +00:00
case . userProfileNoChange : return noDetails
2022-01-31 21:28:07 +00:00
case let . userProfileUpdated ( _ , toProfile ) : return String ( describing : toProfile )
2022-04-08 18:17:10 +01:00
case let . apiParsedMarkdown ( formattedText ) : return String ( describing : formattedText )
2022-02-01 17:34:06 +00:00
case let . userContactLink ( connReq ) : return connReq
case let . userContactLinkCreated ( connReq ) : return connReq
case . userContactLinkDeleted : return noDetails
2022-01-29 11:10:04 +00:00
case let . contactConnected ( contact ) : return String ( describing : contact )
2022-04-22 17:26:17 +01:00
case let . contactConnecting ( contact ) : return String ( describing : contact )
2022-02-01 17:34:06 +00:00
case let . receivedContactRequest ( contactRequest ) : return String ( describing : contactRequest )
case let . acceptingContactRequest ( contact ) : return String ( describing : contact )
case . contactRequestRejected : return noDetails
2022-02-04 22:13:52 +00:00
case let . contactUpdated ( toContact ) : return String ( describing : toContact )
2022-02-05 20:10:47 +00:00
case let . contactSubscribed ( contact ) : return String ( describing : contact )
case let . contactDisconnected ( contact ) : return String ( describing : contact )
case let . contactSubError ( contact , chatError ) : return " contact: \n \( String ( describing : contact ) ) \n error: \n \( String ( describing : chatError ) ) "
2022-02-25 20:26:56 +00:00
case let . contactSubSummary ( contactSubscriptions ) : return String ( describing : contactSubscriptions )
2022-02-09 22:53:06 +00:00
case let . groupSubscribed ( groupInfo ) : return String ( describing : groupInfo )
2022-02-25 20:26:56 +00:00
case let . memberSubErrors ( memberSubErrors ) : return String ( describing : memberSubErrors )
2022-02-09 22:53:06 +00:00
case let . groupEmpty ( groupInfo ) : return String ( describing : groupInfo )
case . userContactLinkSubscribed : return noDetails
2022-01-29 23:37:02 +00:00
case let . newChatItem ( chatItem ) : return String ( describing : chatItem )
2022-03-25 22:26:05 +04:00
case let . chatItemStatusUpdated ( chatItem ) : return String ( describing : chatItem )
2022-02-12 15:59:43 +00:00
case let . chatItemUpdated ( chatItem ) : return String ( describing : chatItem )
2022-03-30 20:37:47 +04:00
case let . chatItemDeleted ( deletedChatItem , toChatItem ) : return " deletedChatItem: \n \( String ( describing : deletedChatItem ) ) \n toChatItem: \n \( String ( describing : toChatItem ) ) "
2022-04-19 12:29:03 +04:00
case . rcvFileAccepted : return noDetails
case let . rcvFileComplete ( chatItem ) : return String ( describing : chatItem )
2022-02-12 15:59:43 +00:00
case . cmdOk : return noDetails
2022-02-01 17:34:06 +00:00
case let . chatCmdError ( chatError ) : return String ( describing : chatError )
2022-02-06 16:18:01 +00:00
case let . chatError ( chatError ) : return String ( describing : chatError )
2022-01-29 23:37:02 +00:00
}
}
}
2022-02-01 17:34:06 +00:00
private var noDetails : String { get { " \( responseType ) : no details " } }
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-02-28 10:44:48 +00:00
private func _sendCmd ( _ cmd : ChatCommand ) -> ChatResponse {
2022-01-29 11:10:04 +00:00
var c = cmd . cmdString . cString ( using : . utf8 ) !
2022-02-28 10:44:48 +00:00
return chatResponse ( chat_send_cmd ( getChatCtrl ( ) , & c ) )
}
private func beginBGTask ( _ handler : ( ( ) -> Void ) ? = nil ) -> ( ( ) -> Void ) {
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
private func withBGTask ( bgDelay : Double ? = nil , f : @ escaping ( ) -> ChatResponse ) -> ChatResponse {
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
? withBGTask ( bgDelay : bgDelay ) { _sendCmd ( cmd ) }
: _sendCmd ( 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-04-08 18:58:09 +01:00
if case . apiParseMarkdown = cmd { } else {
DispatchQueue . main . async {
ChatModel . shared . terminalItems . append ( . cmd ( . now , cmd ) )
ChatModel . shared . terminalItems . append ( . 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
}
}
func chatRecvMsg ( ) async -> ChatResponse {
await withCheckedContinuation { cont in
2022-02-28 10:44:48 +00:00
_ = withBGTask ( bgDelay : msgDelay ) {
let resp = chatResponse ( chat_recv_msg ( getChatCtrl ( ) ) ! )
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-06 21:06:02 +00:00
let _ = getChatCtrl ( )
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
case . chatCmdError ( . error ( . noActiveUser ) ) : return nil
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
}
func apiStartChat ( ) throws {
2022-02-24 17:16:41 +00:00
let r = chatSendCmdSync ( . startChat )
2022-02-06 18:26:22 +00:00
if case . chatStarted = 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
}
2022-01-31 21:28:07 +00:00
func apiGetChats ( ) throws -> [ Chat ] {
2022-02-24 17:16:41 +00:00
let r = chatSendCmdSync ( . apiGetChats )
2022-02-02 12:51:39 +00:00
if case let . apiChats ( chats ) = r { return chats . map { Chat . init ( $0 ) } }
2022-01-30 18:27:20 +00:00
throw r
}
2022-03-19 17:20:27 +00:00
func apiGetChat ( type : ChatType , id : Int64 ) throws -> Chat {
let r = chatSendCmdSync ( . apiGetChat ( type : type , id : id ) )
2022-02-02 12:51:39 +00:00
if case let . apiChat ( chat ) = r { return Chat . init ( chat ) }
2022-01-30 18:27:20 +00:00
throw r
}
2022-04-19 12:29:03 +04:00
func apiSendMessage ( type : ChatType , id : Int64 , file : String ? , quotedItemId : Int64 ? , msg : MsgContent ) async throws -> ChatItem {
2022-02-28 10:44:48 +00:00
let chatModel = ChatModel . shared
2022-04-19 12:29:03 +04:00
let cmd : ChatCommand = . apiSendMessage ( type : type , id : id , file : file , quotedItemId : quotedItemId , msg : msg )
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 )
if case let . newChatItem ( aChatItem ) = r {
cItem = aChatItem . chatItem
chatModel . messageDelivery [ cItem . id ] = endTask
return cItem
}
endTask ( )
} else {
r = await chatSendCmd ( cmd , bgDelay : msgDelay )
if case let . newChatItem ( aChatItem ) = r {
return aChatItem . chatItem
}
}
2022-01-30 18:27:20 +00:00
throw r
}
2022-03-30 20:37:47 +04:00
func apiUpdateChatItem ( type : ChatType , id : Int64 , itemId : Int64 , msg : MsgContent ) async throws -> ChatItem {
let r = await chatSendCmd ( . apiUpdateChatItem ( type : type , id : id , itemId : itemId , msg : msg ) , bgDelay : msgDelay )
2022-03-25 22:26:05 +04:00
if case let . chatItemUpdated ( aChatItem ) = r { return aChatItem . chatItem }
throw r
}
2022-03-30 20:37:47 +04:00
func apiDeleteChatItem ( type : ChatType , id : Int64 , itemId : Int64 , mode : CIDeleteMode ) async throws -> ChatItem {
let r = await chatSendCmd ( . apiDeleteChatItem ( type : type , id : id , itemId : itemId , mode : mode ) , bgDelay : msgDelay )
if case let . chatItemDeleted ( _ , toChatItem ) = r { return toChatItem . chatItem }
2022-03-25 22:26:05 +04:00
throw r
}
2022-04-21 20:04:22 +01:00
func apiRegisterToken ( token : String ) async throws {
2022-04-22 13:46:05 +01:00
try await sendCommandOkResp ( . apiRegisterToken ( token : token ) )
2022-04-21 20:04:22 +01:00
}
2022-04-22 13:46:05 +01:00
func apiVerifyToken ( token : String , code : String , nonce : String ) async throws {
try await sendCommandOkResp ( . apiVerifyToken ( token : token , code : code , nonce : nonce ) )
}
func apiIntervalNofication ( token : String , interval : Int ) async throws {
try await sendCommandOkResp ( . apiIntervalNofication ( token : token , interval : interval ) )
}
func apiDeleteToken ( token : String ) async throws {
try await sendCommandOkResp ( . apiDeleteToken ( token : token ) )
2022-04-21 20:04:22 +01:00
}
2022-03-10 15:45:40 +04:00
func getUserSMPServers ( ) throws -> [ String ] {
let r = chatSendCmdSync ( . getUserSMPServers )
if case let . userSMPServers ( smpServers ) = r { return smpServers }
throw r
}
func setUserSMPServers ( smpServers : [ String ] ) async throws {
2022-04-22 13:46:05 +01:00
try await sendCommandOkResp ( . setUserSMPServers ( smpServers : smpServers ) )
2022-03-10 15:45:40 +04:00
}
2022-02-26 20:21:32 +00:00
func apiAddContact ( ) throws -> String {
2022-02-28 10:44:48 +00:00
let r = chatSendCmdSync ( . addContact , bgTask : false )
2022-01-30 18:27:20 +00:00
if case let . invitation ( connReqInvitation ) = r { return connReqInvitation }
throw r
2022-01-29 23:37:02 +00:00
}
2022-04-02 14:35:35 +01:00
func apiConnect ( connReq : String ) async throws -> Bool {
2022-02-24 17:16:41 +00:00
let r = await chatSendCmd ( . connect ( 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-02 14:35:35 +01:00
case . sentConfirmation : return true
case . sentInvitation : return true
case let . contactAlreadyExists ( contact ) :
am . showAlertMsg (
title : " Contact already exists " ,
message : " You are already connected to \( contact . displayName ) via this link. "
)
return false
case . chatCmdError ( . error ( . invalidConnReq ) ) :
am . showAlertMsg (
title : " Invalid connection link " ,
message : " Please check that you used the correct link or ask your contact to send you another one. "
)
return false
case . chatCmdError ( . errorAgent ( . BROKER ( . TIMEOUT ) ) ) :
am . showAlertMsg (
title : " Connection timeout " ,
2022-04-16 09:37:01 +01:00
message : " Please check your network connection and try again. "
2022-04-02 14:35:35 +01:00
)
return false
case . chatCmdError ( . errorAgent ( . BROKER ( . NETWORK ) ) ) :
am . showAlertMsg (
title : " Connection error " ,
2022-04-16 09:37:01 +01:00
message : " Please check your network connection and try again. "
2022-04-02 14:35:35 +01:00
)
return false
2022-04-21 11:50:24 +04:00
case . chatCmdError ( . errorAgent ( . SMP ( . AUTH ) ) ) :
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. "
)
return false
2022-01-29 23:37:02 +00:00
default : throw r
}
}
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-01-31 21:28:07 +00:00
if case . contactDeleted = r { return }
throw r
}
2022-02-24 17:16:41 +00:00
func apiUpdateProfile ( profile : Profile ) async throws -> Profile ? {
2022-03-25 22:13:01 +04:00
let r = await chatSendCmd ( . apiUpdateProfile ( profile : profile ) )
2022-01-31 21:28:07 +00:00
switch r {
case . userProfileNoChange : return nil
case let . userProfileUpdated ( _ , toProfile ) : return toProfile
default : throw r
}
}
2022-04-08 18:17:10 +01:00
func apiParseMarkdown ( text : String ) throws -> [ FormattedText ] ? {
let r = chatSendCmdSync ( . apiParseMarkdown ( text : text ) )
if case let . apiParsedMarkdown ( formattedText ) = r { return formattedText }
throw r
}
2022-02-24 17:16:41 +00:00
func apiCreateUserAddress ( ) async throws -> String {
let r = await chatSendCmd ( . createMyAddress )
2022-02-01 17:34:06 +00:00
if case let . userContactLinkCreated ( connReq ) = r { return connReq }
throw r
}
2022-02-24 17:16:41 +00:00
func apiDeleteUserAddress ( ) async throws {
let r = await chatSendCmd ( . deleteMyAddress )
2022-02-01 17:34:06 +00:00
if case . userContactLinkDeleted = r { return }
throw r
}
2022-04-19 12:29:03 +04:00
func apiGetUserAddress ( ) throws -> String ? {
let r = chatSendCmdSync ( . showMyAddress )
2022-02-01 17:34:06 +00:00
switch r {
case let . userContactLink ( connReq ) :
return connReq
case . chatCmdError ( chatError : . errorStore ( storeError : . userContactLinkNotFound ) ) :
return nil
default : throw r
}
}
2022-02-24 17:16:41 +00:00
func apiAcceptContactRequest ( contactReqId : Int64 ) async throws -> Contact {
let r = await chatSendCmd ( . apiAcceptContact ( contactReqId : contactReqId ) )
2022-02-01 17:34:06 +00:00
if case let . acceptingContactRequest ( contact ) = r { return contact }
throw r
}
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-04-19 12:29:03 +04:00
func receiveFile ( fileId : Int64 ) async throws {
let r = await chatSendCmd ( . receiveFile ( fileId : fileId ) )
if case . rcvFileAccepted = r { return }
throw r
}
2022-02-24 17:16:41 +00:00
func acceptContactRequest ( _ contactRequest : UserContactRequest ) async {
2022-02-09 22:53:06 +00:00
do {
2022-02-24 17:16:41 +00:00
let contact = try 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
} catch let error {
logger . error ( " acceptContactRequest error: \( error . localizedDescription ) " )
}
}
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 {
logger . error ( " rejectContactRequest: \( error . localizedDescription ) " )
}
}
2022-02-24 17:16:41 +00:00
func markChatRead ( _ chat : Chat ) async {
2022-02-12 15:59:43 +00:00
do {
let minItemId = chat . chatStats . minUnreadItemId
let itemRange = ( minItemId , chat . chatItems . last ? . id ? ? minItemId )
let cInfo = chat . chatInfo
2022-02-24 17:16:41 +00:00
try await apiChatRead ( type : cInfo . chatType , id : cInfo . apiId , itemRange : itemRange )
DispatchQueue . main . async { ChatModel . shared . markChatItemsRead ( cInfo ) }
2022-02-12 15:59:43 +00:00
} catch {
logger . error ( " markChatRead apiChatRead error: \( error . localizedDescription ) " )
}
}
2022-02-24 17:16:41 +00:00
func markChatItemRead ( _ cInfo : ChatInfo , _ cItem : ChatItem ) async {
2022-02-12 15:59:43 +00:00
do {
2022-02-24 17:16:41 +00:00
try await apiChatRead ( type : cInfo . chatType , id : cInfo . apiId , itemRange : ( cItem . id , cItem . id ) )
DispatchQueue . main . async { ChatModel . shared . markChatItemRead ( cInfo , cItem ) }
2022-02-12 15:59:43 +00:00
} catch {
logger . error ( " markChatItemRead apiChatRead error: \( error . localizedDescription ) " )
}
}
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
}
func responseError ( _ err : Error ) -> String {
if let r = err as ? ChatResponse {
return String ( describing : r )
} else {
return err . localizedDescription
}
}
2022-02-09 22:53:06 +00:00
func initializeChat ( ) {
do {
ChatModel . shared . currentUser = try apiGetActiveUser ( )
} catch {
fatalError ( " Failed to initialize chat controller or database: \( error ) " )
}
}
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 {
let msg = await chatRecvMsg ( )
self . _lastMsgTime = . now
processReceivedMsg ( msg )
if self . receiveMessages {
2022-02-28 10:44:48 +00:00
do { try await Task . sleep ( nanoseconds : 7_500_000 ) }
catch { logger . error ( " receiveMsgLoop: Task.sleep error: \( error . localizedDescription ) " ) }
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
}
}
func processReceivedMsg ( _ res : ChatResponse ) {
let chatModel = ChatModel . shared
2022-01-29 23:37:02 +00:00
DispatchQueue . main . async {
2022-02-04 22:13:52 +00:00
chatModel . terminalItems . append ( . 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 {
case let . contactConnected ( contact ) :
2022-02-05 20:10:47 +00:00
chatModel . updateContact ( contact )
chatModel . updateNetworkStatus ( contact , . connected )
2022-02-09 22:53:06 +00:00
NtfManager . shared . notifyContactConnected ( contact )
2022-04-22 17:26:17 +01:00
case let . contactConnecting ( contact ) :
chatModel . updateContact ( contact )
2022-02-01 17:34:06 +00:00
case let . receivedContactRequest ( contactRequest ) :
2022-02-02 12:51:39 +00:00
chatModel . addChat ( Chat (
chatInfo : ChatInfo . contactRequest ( contactRequest : contactRequest ) ,
chatItems : [ ]
) )
2022-02-09 22:53:06 +00:00
NtfManager . shared . notifyContactRequest ( contactRequest )
2022-02-04 22:13:52 +00:00
case let . contactUpdated ( toContact ) :
let cInfo = ChatInfo . direct ( contact : toContact )
if chatModel . hasChat ( toContact . id ) {
chatModel . updateChatInfo ( cInfo )
}
2022-02-05 20:10:47 +00:00
case let . contactSubscribed ( contact ) :
2022-02-25 20:26:56 +00:00
processContactSubscribed ( contact )
2022-02-05 20:10:47 +00:00
case let . contactDisconnected ( contact ) :
chatModel . updateContact ( contact )
chatModel . updateNetworkStatus ( contact , . disconnected )
case let . contactSubError ( contact , chatError ) :
2022-02-25 20:26:56 +00:00
processContactSubError ( contact , chatError )
case let . contactSubSummary ( contactSubscriptions ) :
for sub in contactSubscriptions {
if let err = sub . contactError {
processContactSubError ( sub . contact , err )
} else {
processContactSubscribed ( sub . contact )
}
2022-02-05 20:10:47 +00:00
}
2022-01-29 23:37:02 +00:00
case let . newChatItem ( aChatItem ) :
2022-02-09 22:53:06 +00:00
let cInfo = aChatItem . chatInfo
let cItem = aChatItem . chatItem
chatModel . addChatItem ( cInfo , cItem )
2022-04-19 12:29:03 +04:00
if let file = cItem . file ,
2022-04-19 13:24:26 +04:00
file . fileSize <= 236700 {
// f i l e . f i l e S i z e < = 3 9 4 5 0 0 {
2022-04-19 12:29:03 +04:00
Task {
do {
try await receiveFile ( fileId : file . fileId )
} catch {
logger . error ( " receiveFile error: \( error . localizedDescription ) " )
}
}
}
2022-02-09 22:53:06 +00:00
NtfManager . shared . notifyMessageReceived ( cInfo , cItem )
2022-03-25 22:26:05 +04:00
case let . chatItemStatusUpdated ( aChatItem ) :
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
if ! cItem . isDeletedContent ( ) {
res = chatModel . upsertChatItem ( cInfo , cItem )
}
if res {
2022-02-12 15:59:43 +00:00
NtfManager . shared . notifyMessageReceived ( cInfo , cItem )
2022-02-28 10:44:48 +00:00
} else if let endTask = chatModel . messageDelivery [ cItem . id ] {
switch cItem . meta . itemStatus {
case . sndSent : endTask ( )
case . sndErrorAuth : endTask ( )
case . sndError : endTask ( )
default : break
}
2022-02-12 15:59:43 +00:00
}
2022-03-25 22:26:05 +04:00
case let . chatItemUpdated ( aChatItem ) :
let cInfo = aChatItem . chatInfo
let cItem = aChatItem . chatItem
if chatModel . upsertChatItem ( cInfo , cItem ) {
NtfManager . shared . notifyMessageReceived ( cInfo , cItem )
}
2022-03-30 20:37:47 +04:00
case let . chatItemDeleted ( _ , toChatItem ) :
let cInfo = toChatItem . chatInfo
let cItem = toChatItem . chatItem
if cItem . meta . itemDeleted {
chatModel . removeChatItem ( cInfo , cItem )
} else {
// c u r r e n t l y o n l y b r o a d c a s t d e l e t i o n o f r c v m e s s a g e c a n b e r e c e i v e d , a n d o n l y t h i s c a s e s h o u l d h a p p e n
_ = chatModel . upsertChatItem ( cInfo , cItem )
}
2022-04-19 12:29:03 +04:00
case let . rcvFileComplete ( aChatItem ) :
let cInfo = aChatItem . chatInfo
let cItem = aChatItem . chatItem
if chatModel . upsertChatItem ( cInfo , cItem ) {
NtfManager . shared . notifyMessageReceived ( cInfo , cItem )
}
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-02-25 20:26:56 +00:00
func processContactSubscribed ( _ contact : Contact ) {
let m = ChatModel . shared
m . updateContact ( contact )
m . updateNetworkStatus ( contact , . connected )
}
func processContactSubError ( _ contact : Contact , _ chatError : ChatError ) {
let m = ChatModel . shared
m . updateContact ( contact )
var err : String
switch chatError {
case . errorAgent ( agentError : . BROKER ( brokerErr : . NETWORK ) ) : err = " network "
case . errorAgent ( agentError : . SMP ( smpErr : . AUTH ) ) : err = " contact deleted "
default : err = String ( describing : chatError )
}
m . updateNetworkStatus ( contact , . error ( err ) )
}
2022-01-29 11:10:04 +00:00
private struct UserResponse : Decodable {
var user : User ?
var error : String ?
}
2022-02-25 21:07:36 +00:00
private func chatResponse ( _ cjson : UnsafeMutablePointer < CChar > ) -> ChatResponse {
2022-01-29 11:10:04 +00:00
let s = String . init ( cString : cjson )
let d = s . data ( using : . utf8 ) !
2022-01-29 23:37:02 +00:00
// T O D O i s t h e r e a w a y t o d o i t w i t h o u t c o p y i n g t h e d a t a ? e . g :
2022-01-29 11:10:04 +00:00
// l e t p = U n s a f e M u t a b l e R a w P o i n t e r . i n i t ( m u t a t i n g : U n s a f e R a w P o i n t e r ( c j s o n ) )
// l e t d = D a t a . i n i t ( b y t e s N o C o p y : p , c o u n t : s t r l e n ( c j s o n ) , d e a l l o c a t o r : . f r e e )
2022-01-29 23:37:02 +00:00
// T O D O s o m e m e c h a n i s m t o u p d a t e m o d e l w i t h o u t p a s s i n g i t - m a y b e P u b l i s h e r / S u b s c r i b e r ?
2022-01-29 11:10:04 +00:00
do {
2022-01-29 23:37:02 +00:00
let r = try jsonDecoder . decode ( APIResponse . self , from : d )
return r . resp
2022-01-29 11:10:04 +00:00
} catch {
2022-02-09 22:53:06 +00:00
logger . error ( " chatResponse jsonDecoder.decode error: \( error . localizedDescription ) " )
2022-01-29 11:10:04 +00:00
}
2022-04-19 12:29:03 +04:00
2022-01-29 11:10:04 +00:00
var type : String ?
var json : String ?
if let j = try ? JSONSerialization . jsonObject ( with : d ) as ? NSDictionary {
if let j1 = j [ " resp " ] as ? NSDictionary , j1 . count = = 1 {
type = j1 . allKeys [ 0 ] as ? String
}
json = prettyJSON ( j )
}
2022-02-25 21:07:36 +00:00
free ( cjson )
2022-01-29 23:37:02 +00:00
return ChatResponse . response ( type : type ? ? " invalid " , json : json ? ? s )
2022-01-29 11:10:04 +00:00
}
func prettyJSON ( _ obj : NSDictionary ) -> String ? {
if let d = try ? JSONSerialization . data ( withJSONObject : obj , options : . prettyPrinted ) {
return String ( decoding : d , as : UTF8 . self )
}
return nil
}
private func getChatCtrl ( ) -> chat_ctrl {
if let controller = chatController { return controller }
2022-04-19 12:29:03 +04:00
let dataDir = getDocumentsDirectory ( ) . path + " /mobile_v1 "
2022-02-06 18:26:22 +00:00
var cstr = dataDir . cString ( using : . utf8 ) !
2022-02-26 20:21:32 +00:00
logger . debug ( " getChatCtrl: chat_init " )
ChatModel . shared . terminalItems . append ( . cmd ( . now , . string ( " chat_init " ) ) )
2022-02-06 18:26:22 +00:00
chatController = chat_init ( & cstr )
2022-02-26 20:21:32 +00:00
ChatModel . shared . terminalItems . append ( . resp ( . now , . response ( type : " chat_controller " , json : " chat_controller: no details " ) ) )
2022-02-06 18:26:22 +00:00
return chatController !
2022-01-29 11:10:04 +00:00
}
private func decodeCJSON < T : Decodable > ( _ cjson : UnsafePointer < CChar > ) -> T ? {
let s = String . init ( cString : cjson )
let d = s . data ( using : . utf8 ) !
// l e t p = U n s a f e M u t a b l e R a w P o i n t e r . i n i t ( m u t a t i n g : U n s a f e R a w P o i n t e r ( c j s o n ) )
// l e t d = D a t a . i n i t ( b y t e s N o C o p y : p , c o u n t : s t r l e n ( c j s o n ) , d e a l l o c a t o r : . f r e e )
return try ? jsonDecoder . decode ( T . self , from : d )
}
private func getJSONObject ( _ cjson : UnsafePointer < CChar > ) -> NSDictionary ? {
let s = String . init ( cString : cjson )
let d = s . data ( using : . utf8 ) !
return try ? JSONSerialization . jsonObject ( with : d ) as ? NSDictionary
}
2022-04-08 18:17:10 +01:00
func encodeJSON < T : Encodable > ( _ value : T ) -> String {
2022-01-29 11:10:04 +00:00
let data = try ! jsonEncoder . encode ( value )
2022-03-25 22:13:01 +04:00
return String ( decoding : data , as : UTF8 . self )
}
private func encodeCJSON < T : Encodable > ( _ value : T ) -> [ CChar ] {
encodeJSON ( value ) . cString ( using : . utf8 ) !
2022-01-29 11:10:04 +00:00
}
2022-02-01 17:34:06 +00:00
enum ChatError : Decodable {
2022-02-01 20:30:33 +00:00
case error ( errorType : ChatErrorType )
2022-02-05 20:10:47 +00:00
case errorAgent ( agentError : AgentErrorType )
2022-02-01 17:34:06 +00:00
case errorStore ( storeError : StoreError )
}
2022-02-01 20:30:33 +00:00
enum ChatErrorType : Decodable {
2022-02-06 16:18:01 +00:00
case noActiveUser
case activeUserExists
case chatNotStarted
2022-02-01 20:30:33 +00:00
case invalidConnReq
2022-02-06 16:18:01 +00:00
case invalidChatMessage ( message : String )
2022-02-14 21:52:01 +04:00
case contactNotReady ( contact : Contact )
2022-02-05 20:10:47 +00:00
case contactGroups ( contact : Contact , groupNames : [ GroupName ] )
2022-02-06 16:18:01 +00:00
case groupUserRole
2022-02-05 20:10:47 +00:00
case groupContactRole ( contactName : ContactName )
case groupDuplicateMember ( contactName : ContactName )
case groupDuplicateMemberId
case groupNotJoined ( groupInfo : GroupInfo )
case groupMemberNotActive
case groupMemberUserRemoved
case groupMemberNotFound ( contactName : ContactName )
case groupMemberIntroNotFound ( contactName : ContactName )
case groupCantResendInvitation ( groupInfo : GroupInfo , contactName : ContactName )
case groupInternal ( message : String )
case fileNotFound ( message : String )
case fileAlreadyReceiving ( message : String )
case fileAlreadyExists ( filePath : String )
case fileRead ( filePath : String , message : String )
case fileWrite ( filePath : String , message : String )
case fileSend ( fileId : Int64 , agentError : String )
case fileRcvChunk ( message : String )
case fileInternal ( message : String )
2022-03-25 22:26:05 +04:00
case invalidQuote
2022-03-30 20:37:47 +04:00
case invalidChatItemUpdate
case invalidChatItemDelete
2022-02-05 20:10:47 +00:00
case agentVersion
case commandError ( message : String )
2022-02-01 20:30:33 +00:00
}
2022-02-01 17:34:06 +00:00
enum StoreError : Decodable {
2022-02-05 20:10:47 +00:00
case duplicateName
case contactNotFound ( contactId : Int64 )
case contactNotFoundByName ( contactName : ContactName )
case contactNotReady ( contactName : ContactName )
case duplicateContactLink
2022-02-01 17:34:06 +00:00
case userContactLinkNotFound
2022-02-05 20:10:47 +00:00
case contactRequestNotFound ( contactRequestId : Int64 )
case contactRequestNotFoundByName ( contactName : ContactName )
case groupNotFound ( groupId : Int64 )
case groupNotFoundByName ( groupName : GroupName )
case groupWithoutUser
case duplicateGroupMember
case groupAlreadyJoined
case groupInvitationNotFound
case sndFileNotFound ( fileId : Int64 )
case sndFileInvalid ( fileId : Int64 )
case rcvFileNotFound ( fileId : Int64 )
case fileNotFound ( fileId : Int64 )
case rcvFileInvalid ( fileId : Int64 )
case connectionNotFound ( agentConnId : String )
case introNotFound
case uniqueID
case internalError ( message : String )
case noMsgDelivery ( connId : Int64 , agentMsgId : String )
case badChatItem ( itemId : Int64 )
case chatItemNotFound ( itemId : Int64 )
2022-03-25 22:26:05 +04:00
case quotedChatItemNotFound
case chatItemSharedMsgIdNotFound ( sharedMsgId : String )
2022-02-05 20:10:47 +00:00
}
enum AgentErrorType : Decodable {
case CMD ( cmdErr : CommandErrorType )
case CONN ( connErr : ConnectionErrorType )
case SMP ( smpErr : SMPErrorType )
case BROKER ( brokerErr : BrokerErrorType )
case AGENT ( agentErr : SMPAgentError )
case INTERNAL ( internalErr : String )
}
enum CommandErrorType : Decodable {
case PROHIBITED
case SYNTAX
case NO_CONN
case SIZE
case LARGE
}
enum ConnectionErrorType : Decodable {
case NOT_FOUND
case DUPLICATE
case SIMPLEX
case NOT_ACCEPTED
case NOT_AVAILABLE
}
enum BrokerErrorType : Decodable {
case RESPONSE ( smpErr : SMPErrorType )
case UNEXPECTED
case NETWORK
case TRANSPORT ( transportErr : SMPTransportError )
case TIMEOUT
}
enum SMPErrorType : Decodable {
case BLOCK
case SESSION
case CMD ( cmdErr : SMPCommandError )
case AUTH
case QUOTA
case NO_MSG
case LARGE_MSG
case INTERNAL
}
enum SMPCommandError : Decodable {
case UNKNOWN
case SYNTAX
case NO_AUTH
case HAS_AUTH
case NO_QUEUE
}
enum SMPTransportError : Decodable {
2022-02-20 21:17:24 +00:00
case badBlock
case largeMsg
case badSession
case handshake ( handshakeErr : SMPHandshakeError )
2022-02-05 20:10:47 +00:00
}
enum SMPHandshakeError : Decodable {
case PARSE
case VERSION
case IDENTITY
}
enum SMPAgentError : Decodable {
case A_MESSAGE
case A_PROHIBITED
case A_VERSION
case A_ENCRYPTION
2022-02-01 17:34:06 +00:00
}