mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-29 04:39:53 +00:00
ios: remove types used only in the app from the framework (#5866)
* ios: remove types used only in the app from the framework * move more types * comment
This commit is contained in:
parent
e7a4611be9
commit
f5c706f2dd
12 changed files with 2775 additions and 2467 deletions
2327
apps/ios/Shared/Model/AppAPITypes.swift
Normal file
2327
apps/ios/Shared/Model/AppAPITypes.swift
Normal file
File diff suppressed because it is too large
Load diff
|
@ -11,7 +11,7 @@ import UIKit
|
||||||
import Dispatch
|
import Dispatch
|
||||||
import BackgroundTasks
|
import BackgroundTasks
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SimpleXChat
|
@preconcurrency import SimpleXChat
|
||||||
|
|
||||||
private var chatController: chat_ctrl?
|
private var chatController: chat_ctrl?
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ func chatSendCmdSync(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? =
|
||||||
logger.debug("chatSendCmd \(cmd.cmdType)")
|
logger.debug("chatSendCmd \(cmd.cmdType)")
|
||||||
}
|
}
|
||||||
let start = Date.now
|
let start = Date.now
|
||||||
let resp = bgTask
|
let resp: ChatResponse = bgTask
|
||||||
? withBGTask(bgDelay: bgDelay) { sendSimpleXCmd(cmd, ctrl) }
|
? withBGTask(bgDelay: bgDelay) { sendSimpleXCmd(cmd, ctrl) }
|
||||||
: sendSimpleXCmd(cmd, ctrl)
|
: sendSimpleXCmd(cmd, ctrl)
|
||||||
if log {
|
if log {
|
||||||
|
@ -115,7 +115,7 @@ func chatSendCmd(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? = nil
|
||||||
func chatRecvMsg(_ ctrl: chat_ctrl? = nil) async -> ChatResponse? {
|
func chatRecvMsg(_ ctrl: chat_ctrl? = nil) async -> ChatResponse? {
|
||||||
await withCheckedContinuation { cont in
|
await withCheckedContinuation { cont in
|
||||||
_ = withBGTask(bgDelay: msgDelay) { () -> ChatResponse? in
|
_ = withBGTask(bgDelay: msgDelay) { () -> ChatResponse? in
|
||||||
let resp = recvSimpleXMsg(ctrl)
|
let resp: ChatResponse? = recvSimpleXMsg(ctrl)
|
||||||
cont.resume(returning: resp)
|
cont.resume(returning: resp)
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ func chatRecvMsg(_ ctrl: chat_ctrl? = nil) async -> ChatResponse? {
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiGetActiveUser(ctrl: chat_ctrl? = nil) throws -> User? {
|
func apiGetActiveUser(ctrl: chat_ctrl? = nil) throws -> User? {
|
||||||
let r = chatSendCmdSync(.showActiveUser, ctrl)
|
let r: ChatResponse = chatSendCmdSync(.showActiveUser, ctrl)
|
||||||
switch r {
|
switch r {
|
||||||
case let .activeUser(user): return user
|
case let .activeUser(user): return user
|
||||||
case .chatCmdError(_, .error(.noActiveUser)): return nil
|
case .chatCmdError(_, .error(.noActiveUser)): return nil
|
||||||
|
@ -132,7 +132,7 @@ func apiGetActiveUser(ctrl: chat_ctrl? = nil) throws -> User? {
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiCreateActiveUser(_ p: Profile?, pastTimestamp: Bool = false, ctrl: chat_ctrl? = nil) throws -> User {
|
func apiCreateActiveUser(_ p: Profile?, pastTimestamp: Bool = false, ctrl: chat_ctrl? = nil) throws -> User {
|
||||||
let r = chatSendCmdSync(.createActiveUser(profile: p, pastTimestamp: pastTimestamp), ctrl)
|
let r: ChatResponse = chatSendCmdSync(.createActiveUser(profile: p, pastTimestamp: pastTimestamp), ctrl)
|
||||||
if case let .activeUser(user) = r { return user }
|
if case let .activeUser(user) = r { return user }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
@ -199,19 +199,19 @@ func apiUnmuteUser(_ userId: Int64) async throws -> User {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setUserPrivacy_(_ cmd: ChatCommand) async throws -> User {
|
func setUserPrivacy_(_ cmd: ChatCommand) async throws -> User {
|
||||||
let r = await chatSendCmd(cmd)
|
let r: ChatResponse = await chatSendCmd(cmd)
|
||||||
if case let .userPrivacy(_, updatedUser) = r { return updatedUser }
|
if case let .userPrivacy(_, updatedUser) = r { return updatedUser }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiDeleteUser(_ userId: Int64, _ delSMPQueues: Bool, viewPwd: String?) async throws {
|
func apiDeleteUser(_ userId: Int64, _ delSMPQueues: Bool, viewPwd: String?) async throws {
|
||||||
let r = await chatSendCmd(.apiDeleteUser(userId: userId, delSMPQueues: delSMPQueues, viewPwd: viewPwd))
|
let r: ChatResponse = await chatSendCmd(.apiDeleteUser(userId: userId, delSMPQueues: delSMPQueues, viewPwd: viewPwd))
|
||||||
if case .cmdOk = r { return }
|
if case .cmdOk = r { return }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiStartChat(ctrl: chat_ctrl? = nil) throws -> Bool {
|
func apiStartChat(ctrl: chat_ctrl? = nil) throws -> Bool {
|
||||||
let r = chatSendCmdSync(.startChat(mainApp: true, enableSndFiles: true), ctrl)
|
let r: ChatResponse = chatSendCmdSync(.startChat(mainApp: true, enableSndFiles: true), ctrl)
|
||||||
switch r {
|
switch r {
|
||||||
case .chatStarted: return true
|
case .chatStarted: return true
|
||||||
case .chatRunning: return false
|
case .chatRunning: return false
|
||||||
|
@ -890,7 +890,7 @@ func apiConnect_(incognito: Bool, connLink: CreatedConnLink) async -> ((ConnReqT
|
||||||
logger.error("apiConnect: no current user")
|
logger.error("apiConnect: no current user")
|
||||||
return (nil, nil)
|
return (nil, nil)
|
||||||
}
|
}
|
||||||
let r = await chatSendCmd(.apiConnect(userId: userId, incognito: incognito, connLink: connLink))
|
let r: ChatResponse = await chatSendCmd(.apiConnect(userId: userId, incognito: incognito, connLink: connLink))
|
||||||
let m = ChatModel.shared
|
let m = ChatModel.shared
|
||||||
switch r {
|
switch r {
|
||||||
case let .sentConfirmation(_, connection):
|
case let .sentConfirmation(_, connection):
|
||||||
|
@ -1281,7 +1281,7 @@ func receiveFiles(user: any UserLike, fileIds: [Int64], userApprovedRelays: Bool
|
||||||
case let .rcvFileAccepted(_, chatItem):
|
case let .rcvFileAccepted(_, chatItem):
|
||||||
await chatItemSimpleUpdate(user, chatItem)
|
await chatItemSimpleUpdate(user, chatItem)
|
||||||
default:
|
default:
|
||||||
if let chatError = chatError(r) {
|
if let chatError = r.chatErrorType {
|
||||||
switch chatError {
|
switch chatError {
|
||||||
case let .fileNotApproved(fileId, unknownServers):
|
case let .fileNotApproved(fileId, unknownServers):
|
||||||
fileIdsToApprove.append(fileId)
|
fileIdsToApprove.append(fileId)
|
||||||
|
@ -1348,7 +1348,7 @@ func receiveFiles(user: any UserLike, fileIds: [Int64], userApprovedRelays: Bool
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if let chatError = chatError(errorResponse) {
|
if let chatError = errorResponse.chatErrorType {
|
||||||
switch chatError {
|
switch chatError {
|
||||||
case .fileCancelled, .fileAlreadyReceiving:
|
case .fileCancelled, .fileAlreadyReceiving:
|
||||||
logger.debug("receiveFiles ignoring FileCancelled or FileAlreadyReceiving error")
|
logger.debug("receiveFiles ignoring FileCancelled or FileAlreadyReceiving error")
|
||||||
|
@ -1635,7 +1635,7 @@ func apiLeaveGroup(_ groupId: Int64) async throws -> GroupInfo {
|
||||||
|
|
||||||
// use ChatModel's loadGroupMembers from views
|
// use ChatModel's loadGroupMembers from views
|
||||||
func apiListMembers(_ groupId: Int64) async -> [GroupMember] {
|
func apiListMembers(_ groupId: Int64) async -> [GroupMember] {
|
||||||
let r = await chatSendCmd(.apiListMembers(groupId: groupId))
|
let r: ChatResponse = await chatSendCmd(.apiListMembers(groupId: groupId))
|
||||||
if case let .groupMembers(_, group) = r { return group.members }
|
if case let .groupMembers(_, group) = r { return group.members }
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
|
@ -752,7 +752,7 @@ private class MigrationChatReceiver {
|
||||||
|
|
||||||
func receiveMsgLoop() async {
|
func receiveMsgLoop() async {
|
||||||
// TODO use function that has timeout
|
// TODO use function that has timeout
|
||||||
if let msg = await chatRecvMsg(ctrl) {
|
if let msg: ChatResponse = await chatRecvMsg(ctrl) {
|
||||||
Task {
|
Task {
|
||||||
await TerminalItems.shared.add(.resp(.now, msg))
|
await TerminalItems.shared.add(.resp(.now, msg))
|
||||||
}
|
}
|
||||||
|
|
173
apps/ios/SimpleX NSE/NSEAPITypes.swift
Normal file
173
apps/ios/SimpleX NSE/NSEAPITypes.swift
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
//
|
||||||
|
// APITypes.swift
|
||||||
|
// SimpleX
|
||||||
|
//
|
||||||
|
// Created by EP on 01/05/2025.
|
||||||
|
// Copyright © 2025 SimpleX Chat. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SimpleXChat
|
||||||
|
|
||||||
|
enum NSEChatCommand: ChatCmdProtocol {
|
||||||
|
case showActiveUser
|
||||||
|
case startChat(mainApp: Bool, enableSndFiles: Bool)
|
||||||
|
case apiActivateChat(restoreChat: Bool)
|
||||||
|
case apiSuspendChat(timeoutMicroseconds: Int)
|
||||||
|
case apiSetNetworkConfig(networkConfig: NetCfg)
|
||||||
|
case apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String)
|
||||||
|
case apiSetEncryptLocalFiles(enable: Bool)
|
||||||
|
case apiGetNtfConns(nonce: String, encNtfInfo: String)
|
||||||
|
case apiGetConnNtfMessages(connMsgReqs: [ConnMsgReq])
|
||||||
|
case receiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?, inline: Bool?)
|
||||||
|
case setFileToReceive(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?)
|
||||||
|
|
||||||
|
var cmdString: String {
|
||||||
|
switch self {
|
||||||
|
case .showActiveUser: return "/u"
|
||||||
|
case let .startChat(mainApp, enableSndFiles): return "/_start main=\(onOff(mainApp)) snd_files=\(onOff(enableSndFiles))"
|
||||||
|
case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))"
|
||||||
|
case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)"
|
||||||
|
case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))"
|
||||||
|
case let .apiSetAppFilePaths(filesFolder, tempFolder, assetsFolder):
|
||||||
|
return "/set file paths \(encodeJSON(AppFilePaths(appFilesFolder: filesFolder, appTempFolder: tempFolder, appAssetsFolder: assetsFolder)))"
|
||||||
|
case let .apiSetEncryptLocalFiles(enable): return "/_files_encrypt \(onOff(enable))"
|
||||||
|
case let .apiGetNtfConns(nonce, encNtfInfo): return "/_ntf conns \(nonce) \(encNtfInfo)"
|
||||||
|
case let .apiGetConnNtfMessages(connMsgReqs): return "/_ntf conn messages \(connMsgReqs.map { $0.cmdString }.joined(separator: ","))"
|
||||||
|
case let .receiveFile(fileId, userApprovedRelays, encrypt, inline): return "/freceive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))\(onOffParam("inline", inline))"
|
||||||
|
case let .setFileToReceive(fileId, userApprovedRelays, encrypt): return "/_set_file_to_receive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func onOffParam(_ param: String, _ b: Bool?) -> String {
|
||||||
|
if let b = b {
|
||||||
|
" \(param)=\(onOff(b))"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum NSEChatResponse: Decodable, Error, ChatRespProtocol {
|
||||||
|
case response(type: String, json: String)
|
||||||
|
case activeUser(user: User)
|
||||||
|
case chatStarted
|
||||||
|
case chatRunning
|
||||||
|
case chatSuspended
|
||||||
|
case contactConnected(user: UserRef, contact: Contact, userCustomProfile: Profile?)
|
||||||
|
case receivedContactRequest(user: UserRef, contactRequest: UserContactRequest)
|
||||||
|
case newChatItems(user: UserRef, chatItems: [AChatItem])
|
||||||
|
case rcvFileAccepted(user: UserRef, chatItem: AChatItem)
|
||||||
|
case rcvFileSndCancelled(user: UserRef, chatItem: AChatItem, rcvFileTransfer: RcvFileTransfer)
|
||||||
|
case sndFileComplete(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer)
|
||||||
|
case sndFileRcvCancelled(user: UserRef, chatItem_: AChatItem?, sndFileTransfer: SndFileTransfer)
|
||||||
|
case callInvitation(callInvitation: RcvCallInvitation)
|
||||||
|
case ntfConns(ntfConns: [NtfConn])
|
||||||
|
case connNtfMessages(receivedMsgs: [NtfMsgInfo?])
|
||||||
|
case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgAckInfo)
|
||||||
|
case cmdOk(user_: UserRef?)
|
||||||
|
case chatCmdError(user_: UserRef?, chatError: ChatError)
|
||||||
|
case chatError(user_: UserRef?, chatError: ChatError)
|
||||||
|
|
||||||
|
var responseType: String {
|
||||||
|
switch self {
|
||||||
|
case let .response(type, _): "* \(type)"
|
||||||
|
case .activeUser: "activeUser"
|
||||||
|
case .chatStarted: "chatStarted"
|
||||||
|
case .chatRunning: "chatRunning"
|
||||||
|
case .chatSuspended: "chatSuspended"
|
||||||
|
case .contactConnected: "contactConnected"
|
||||||
|
case .receivedContactRequest: "receivedContactRequest"
|
||||||
|
case .newChatItems: "newChatItems"
|
||||||
|
case .rcvFileAccepted: "rcvFileAccepted"
|
||||||
|
case .rcvFileSndCancelled: "rcvFileSndCancelled"
|
||||||
|
case .sndFileComplete: "sndFileComplete"
|
||||||
|
case .sndFileRcvCancelled: "sndFileRcvCancelled"
|
||||||
|
case .callInvitation: "callInvitation"
|
||||||
|
case .ntfConns: "ntfConns"
|
||||||
|
case .connNtfMessages: "connNtfMessages"
|
||||||
|
case .ntfMessage: "ntfMessage"
|
||||||
|
case .cmdOk: "cmdOk"
|
||||||
|
case .chatCmdError: "chatCmdError"
|
||||||
|
case .chatError: "chatError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var details: String {
|
||||||
|
switch self {
|
||||||
|
case let .response(_, json): return json
|
||||||
|
case let .activeUser(user): return String(describing: user)
|
||||||
|
case .chatStarted: return noDetails
|
||||||
|
case .chatRunning: return noDetails
|
||||||
|
case .chatSuspended: return noDetails
|
||||||
|
case let .contactConnected(u, contact, _): return withUser(u, String(describing: contact))
|
||||||
|
case let .receivedContactRequest(u, contactRequest): return withUser(u, String(describing: contactRequest))
|
||||||
|
case let .newChatItems(u, chatItems):
|
||||||
|
let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n")
|
||||||
|
return withUser(u, itemsString)
|
||||||
|
case let .rcvFileAccepted(u, chatItem): return withUser(u, String(describing: chatItem))
|
||||||
|
case let .rcvFileSndCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||||
|
case let .sndFileComplete(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||||
|
case let .sndFileRcvCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||||
|
case let .callInvitation(inv): return String(describing: inv)
|
||||||
|
case let .ntfConns(ntfConns): return String(describing: ntfConns)
|
||||||
|
case let .connNtfMessages(receivedMsgs): return "receivedMsgs: \(String(describing: receivedMsgs))"
|
||||||
|
case let .ntfMessage(u, connEntity, ntfMessage): return withUser(u, "connEntity: \(String(describing: connEntity))\nntfMessage: \(String(describing: ntfMessage))")
|
||||||
|
case .cmdOk: return noDetails
|
||||||
|
case let .chatCmdError(u, chatError): return withUser(u, String(describing: chatError))
|
||||||
|
case let .chatError(u, chatError): return withUser(u, String(describing: chatError))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var noDetails: String { "\(responseType): no details" }
|
||||||
|
|
||||||
|
static func chatResponse(_ s: String) -> NSEChatResponse {
|
||||||
|
let d = s.data(using: .utf8)!
|
||||||
|
// TODO is there a way to do it without copying the data? e.g:
|
||||||
|
// let p = UnsafeMutableRawPointer.init(mutating: UnsafeRawPointer(cjson))
|
||||||
|
// let d = Data.init(bytesNoCopy: p, count: strlen(cjson), deallocator: .free)
|
||||||
|
do {
|
||||||
|
let r = try jsonDecoder.decode(APIResponse<NSEChatResponse>.self, from: d)
|
||||||
|
return r.resp
|
||||||
|
} catch {
|
||||||
|
logger.error("chatResponse jsonDecoder.decode error: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
|
||||||
|
var type: String?
|
||||||
|
var json: String?
|
||||||
|
if let j = try? JSONSerialization.jsonObject(with: d) as? NSDictionary {
|
||||||
|
if let jResp = j["resp"] as? NSDictionary, jResp.count == 1 || jResp.count == 2 {
|
||||||
|
type = jResp.allKeys[0] as? String
|
||||||
|
if jResp.count == 2 && type == "_owsf" {
|
||||||
|
type = jResp.allKeys[1] as? String
|
||||||
|
}
|
||||||
|
if type == "chatCmdError" {
|
||||||
|
if let jError = jResp["chatCmdError"] as? NSDictionary {
|
||||||
|
return .chatCmdError(user_: decodeUser_(jError), chatError: .invalidJSON(json: errorJson(jError) ?? ""))
|
||||||
|
}
|
||||||
|
} else if type == "chatError" {
|
||||||
|
if let jError = jResp["chatError"] as? NSDictionary {
|
||||||
|
return .chatError(user_: decodeUser_(jError), chatError: .invalidJSON(json: errorJson(jError) ?? ""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json = serializeJSON(j, options: .prettyPrinted)
|
||||||
|
}
|
||||||
|
return NSEChatResponse.response(type: type ?? "invalid", json: json ?? s)
|
||||||
|
}
|
||||||
|
|
||||||
|
var chatError: ChatError? {
|
||||||
|
switch self {
|
||||||
|
case let .chatCmdError(_, error): error
|
||||||
|
case let .chatError(_, error): error
|
||||||
|
default: nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var chatErrorType: ChatErrorType? {
|
||||||
|
switch self {
|
||||||
|
case let .chatCmdError(_, .error(error)): error
|
||||||
|
case let .chatError(_, .error(error)): error
|
||||||
|
default: nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -789,9 +789,9 @@ func receiveMessages() async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func chatRecvMsg() async -> ChatResponse? {
|
func chatRecvMsg() async -> NSEChatResponse? {
|
||||||
await withCheckedContinuation { cont in
|
await withCheckedContinuation { cont in
|
||||||
let resp = recvSimpleXMsg()
|
let resp: NSEChatResponse? = recvSimpleXMsg()
|
||||||
cont.resume(returning: resp)
|
cont.resume(returning: resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -799,7 +799,7 @@ func chatRecvMsg() async -> ChatResponse? {
|
||||||
private let isInChina = SKStorefront().countryCode == "CHN"
|
private let isInChina = SKStorefront().countryCode == "CHN"
|
||||||
private func useCallKit() -> Bool { !isInChina && callKitEnabledGroupDefault.get() }
|
private func useCallKit() -> Bool { !isInChina && callKitEnabledGroupDefault.get() }
|
||||||
|
|
||||||
func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotificationData)? {
|
func receivedMsgNtf(_ res: NSEChatResponse) async -> (String, NSENotificationData)? {
|
||||||
logger.debug("NotificationService receivedMsgNtf: \(res.responseType)")
|
logger.debug("NotificationService receivedMsgNtf: \(res.responseType)")
|
||||||
switch res {
|
switch res {
|
||||||
case let .contactConnected(user, contact, _):
|
case let .contactConnected(user, contact, _):
|
||||||
|
@ -868,7 +868,7 @@ func updateNetCfg() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiGetActiveUser() -> User? {
|
func apiGetActiveUser() -> User? {
|
||||||
let r = sendSimpleXCmd(.showActiveUser)
|
let r: NSEChatResponse = sendSimpleXCmd(NSEChatCommand.showActiveUser)
|
||||||
logger.debug("apiGetActiveUser sendSimpleXCmd response: \(r.responseType)")
|
logger.debug("apiGetActiveUser sendSimpleXCmd response: \(r.responseType)")
|
||||||
switch r {
|
switch r {
|
||||||
case let .activeUser(user): return user
|
case let .activeUser(user): return user
|
||||||
|
@ -885,7 +885,7 @@ func apiGetActiveUser() -> User? {
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiStartChat() throws -> Bool {
|
func apiStartChat() throws -> Bool {
|
||||||
let r = sendSimpleXCmd(.startChat(mainApp: false, enableSndFiles: false))
|
let r: NSEChatResponse = sendSimpleXCmd(NSEChatCommand.startChat(mainApp: false, enableSndFiles: false))
|
||||||
switch r {
|
switch r {
|
||||||
case .chatStarted: return true
|
case .chatStarted: return true
|
||||||
case .chatRunning: return false
|
case .chatRunning: return false
|
||||||
|
@ -895,27 +895,27 @@ func apiStartChat() throws -> Bool {
|
||||||
|
|
||||||
func apiActivateChat() -> Bool {
|
func apiActivateChat() -> Bool {
|
||||||
chatReopenStore()
|
chatReopenStore()
|
||||||
let r = sendSimpleXCmd(.apiActivateChat(restoreChat: false))
|
let r: NSEChatResponse = sendSimpleXCmd(NSEChatCommand.apiActivateChat(restoreChat: false))
|
||||||
if case .cmdOk = r { return true }
|
if case .cmdOk = r { return true }
|
||||||
logger.error("NotificationService apiActivateChat error: \(String(describing: r))")
|
logger.error("NotificationService apiActivateChat error: \(String(describing: r))")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiSuspendChat(timeoutMicroseconds: Int) -> Bool {
|
func apiSuspendChat(timeoutMicroseconds: Int) -> Bool {
|
||||||
let r = sendSimpleXCmd(.apiSuspendChat(timeoutMicroseconds: timeoutMicroseconds))
|
let r: NSEChatResponse = sendSimpleXCmd(NSEChatCommand.apiSuspendChat(timeoutMicroseconds: timeoutMicroseconds))
|
||||||
if case .cmdOk = r { return true }
|
if case .cmdOk = r { return true }
|
||||||
logger.error("NotificationService apiSuspendChat error: \(String(describing: r))")
|
logger.error("NotificationService apiSuspendChat error: \(String(describing: r))")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String) throws {
|
func apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String) throws {
|
||||||
let r = sendSimpleXCmd(.apiSetAppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder))
|
let r: NSEChatResponse = sendSimpleXCmd(NSEChatCommand.apiSetAppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder))
|
||||||
if case .cmdOk = r { return }
|
if case .cmdOk = r { return }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiSetEncryptLocalFiles(_ enable: Bool) throws {
|
func apiSetEncryptLocalFiles(_ enable: Bool) throws {
|
||||||
let r = sendSimpleXCmd(.apiSetEncryptLocalFiles(enable: enable))
|
let r: NSEChatResponse = sendSimpleXCmd(NSEChatCommand.apiSetEncryptLocalFiles(enable: enable))
|
||||||
if case .cmdOk = r { return }
|
if case .cmdOk = r { return }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
@ -925,7 +925,7 @@ func apiGetNtfConns(nonce: String, encNtfInfo: String) -> [NtfConn]? {
|
||||||
logger.debug("no active user")
|
logger.debug("no active user")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let r = sendSimpleXCmd(.apiGetNtfConns(nonce: nonce, encNtfInfo: encNtfInfo))
|
let r: NSEChatResponse = sendSimpleXCmd(NSEChatCommand.apiGetNtfConns(nonce: nonce, encNtfInfo: encNtfInfo))
|
||||||
if case let .ntfConns(ntfConns) = r {
|
if case let .ntfConns(ntfConns) = r {
|
||||||
logger.debug("apiGetNtfConns response ntfConns: \(ntfConns.count)")
|
logger.debug("apiGetNtfConns response ntfConns: \(ntfConns.count)")
|
||||||
return ntfConns
|
return ntfConns
|
||||||
|
@ -942,8 +942,8 @@ func apiGetConnNtfMessages(connMsgReqs: [ConnMsgReq]) -> [NtfMsgInfo?]? {
|
||||||
logger.debug("no active user")
|
logger.debug("no active user")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
logger.debug("apiGetConnNtfMessages command: \(ChatCommand.apiGetConnNtfMessages(connMsgReqs: connMsgReqs).cmdString)")
|
logger.debug("apiGetConnNtfMessages command: \(NSEChatCommand.apiGetConnNtfMessages(connMsgReqs: connMsgReqs).cmdString)")
|
||||||
let r = sendSimpleXCmd(.apiGetConnNtfMessages(connMsgReqs: connMsgReqs))
|
let r: NSEChatResponse = sendSimpleXCmd(NSEChatCommand.apiGetConnNtfMessages(connMsgReqs: connMsgReqs))
|
||||||
if case let .connNtfMessages(receivedMsgs) = r {
|
if case let .connNtfMessages(receivedMsgs) = r {
|
||||||
logger.debug("apiGetConnNtfMessages response receivedMsgs: total \(receivedMsgs.count), expecting messages \(receivedMsgs.count { $0 != nil })")
|
logger.debug("apiGetConnNtfMessages response receivedMsgs: total \(receivedMsgs.count), expecting messages \(receivedMsgs.count { $0 != nil })")
|
||||||
return receivedMsgs
|
return receivedMsgs
|
||||||
|
@ -962,7 +962,7 @@ func getConnNtfMessage(connMsgReq: ConnMsgReq) -> NtfMsgInfo? {
|
||||||
|
|
||||||
func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil) -> AChatItem? {
|
func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil) -> AChatItem? {
|
||||||
let userApprovedRelays = !privacyAskToApproveRelaysGroupDefault.get()
|
let userApprovedRelays = !privacyAskToApproveRelaysGroupDefault.get()
|
||||||
let r = sendSimpleXCmd(.receiveFile(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted, inline: inline))
|
let r: NSEChatResponse = sendSimpleXCmd(NSEChatCommand.receiveFile(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted, inline: inline))
|
||||||
if case let .rcvFileAccepted(_, chatItem) = r { return chatItem }
|
if case let .rcvFileAccepted(_, chatItem) = r { return chatItem }
|
||||||
logger.error("receiveFile error: \(responseError(r))")
|
logger.error("receiveFile error: \(responseError(r))")
|
||||||
return nil
|
return nil
|
||||||
|
@ -970,7 +970,7 @@ func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil) -> ACha
|
||||||
|
|
||||||
func apiSetFileToReceive(fileId: Int64, encrypted: Bool) {
|
func apiSetFileToReceive(fileId: Int64, encrypted: Bool) {
|
||||||
let userApprovedRelays = !privacyAskToApproveRelaysGroupDefault.get()
|
let userApprovedRelays = !privacyAskToApproveRelaysGroupDefault.get()
|
||||||
let r = sendSimpleXCmd(.setFileToReceive(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted))
|
let r: NSEChatResponse = sendSimpleXCmd(NSEChatCommand.setFileToReceive(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted))
|
||||||
if case .cmdOk = r { return }
|
if case .cmdOk = r { return }
|
||||||
logger.error("setFileToReceive error: \(responseError(r))")
|
logger.error("setFileToReceive error: \(responseError(r))")
|
||||||
}
|
}
|
||||||
|
@ -989,7 +989,7 @@ func autoReceiveFile(_ file: CIFile) -> ChatItem? {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setNetworkConfig(_ cfg: NetCfg) throws {
|
func setNetworkConfig(_ cfg: NetCfg) throws {
|
||||||
let r = sendSimpleXCmd(.apiSetNetworkConfig(networkConfig: cfg))
|
let r: NSEChatResponse = sendSimpleXCmd(NSEChatCommand.apiSetNetworkConfig(networkConfig: cfg))
|
||||||
if case .cmdOk = r { return }
|
if case .cmdOk = r { return }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import SimpleXChat
|
||||||
let logger = Logger()
|
let logger = Logger()
|
||||||
|
|
||||||
func apiGetActiveUser() throws -> User? {
|
func apiGetActiveUser() throws -> User? {
|
||||||
let r = sendSimpleXCmd(.showActiveUser)
|
let r: SEChatResponse = sendSimpleXCmd(SEChatCommand.showActiveUser)
|
||||||
switch r {
|
switch r {
|
||||||
case let .activeUser(user): return user
|
case let .activeUser(user): return user
|
||||||
case .chatCmdError(_, .error(.noActiveUser)): return nil
|
case .chatCmdError(_, .error(.noActiveUser)): return nil
|
||||||
|
@ -22,7 +22,7 @@ func apiGetActiveUser() throws -> User? {
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiStartChat() throws -> Bool {
|
func apiStartChat() throws -> Bool {
|
||||||
let r = sendSimpleXCmd(.startChat(mainApp: false, enableSndFiles: true))
|
let r: SEChatResponse = sendSimpleXCmd(SEChatCommand.startChat(mainApp: false, enableSndFiles: true))
|
||||||
switch r {
|
switch r {
|
||||||
case .chatStarted: return true
|
case .chatStarted: return true
|
||||||
case .chatRunning: return false
|
case .chatRunning: return false
|
||||||
|
@ -31,25 +31,25 @@ func apiStartChat() throws -> Bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiSetNetworkConfig(_ cfg: NetCfg) throws {
|
func apiSetNetworkConfig(_ cfg: NetCfg) throws {
|
||||||
let r = sendSimpleXCmd(.apiSetNetworkConfig(networkConfig: cfg))
|
let r: SEChatResponse = sendSimpleXCmd(SEChatCommand.apiSetNetworkConfig(networkConfig: cfg))
|
||||||
if case .cmdOk = r { return }
|
if case .cmdOk = r { return }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String) throws {
|
func apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String) throws {
|
||||||
let r = sendSimpleXCmd(.apiSetAppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder))
|
let r: SEChatResponse = sendSimpleXCmd(SEChatCommand.apiSetAppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder))
|
||||||
if case .cmdOk = r { return }
|
if case .cmdOk = r { return }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiSetEncryptLocalFiles(_ enable: Bool) throws {
|
func apiSetEncryptLocalFiles(_ enable: Bool) throws {
|
||||||
let r = sendSimpleXCmd(.apiSetEncryptLocalFiles(enable: enable))
|
let r: SEChatResponse = sendSimpleXCmd(SEChatCommand.apiSetEncryptLocalFiles(enable: enable))
|
||||||
if case .cmdOk = r { return }
|
if case .cmdOk = r { return }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiGetChats(userId: User.ID) throws -> Array<ChatData> {
|
func apiGetChats(userId: User.ID) throws -> Array<ChatData> {
|
||||||
let r = sendSimpleXCmd(.apiGetChats(userId: userId))
|
let r: SEChatResponse = sendSimpleXCmd(SEChatCommand.apiGetChats(userId: userId))
|
||||||
if case let .apiChats(user: _, chats: chats) = r { return chats }
|
if case let .apiChats(user: _, chats: chats) = r { return chats }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
@ -58,13 +58,13 @@ func apiSendMessages(
|
||||||
chatInfo: ChatInfo,
|
chatInfo: ChatInfo,
|
||||||
composedMessages: [ComposedMessage]
|
composedMessages: [ComposedMessage]
|
||||||
) throws -> [AChatItem] {
|
) throws -> [AChatItem] {
|
||||||
let r = sendSimpleXCmd(
|
let r: SEChatResponse = sendSimpleXCmd(
|
||||||
chatInfo.chatType == .local
|
chatInfo.chatType == .local
|
||||||
? .apiCreateChatItems(
|
? SEChatCommand.apiCreateChatItems(
|
||||||
noteFolderId: chatInfo.apiId,
|
noteFolderId: chatInfo.apiId,
|
||||||
composedMessages: composedMessages
|
composedMessages: composedMessages
|
||||||
)
|
)
|
||||||
: .apiSendMessages(
|
: SEChatCommand.apiSendMessages(
|
||||||
type: chatInfo.chatType,
|
type: chatInfo.chatType,
|
||||||
id: chatInfo.apiId,
|
id: chatInfo.apiId,
|
||||||
live: false,
|
live: false,
|
||||||
|
@ -84,19 +84,20 @@ func apiSendMessages(
|
||||||
|
|
||||||
func apiActivateChat() throws {
|
func apiActivateChat() throws {
|
||||||
chatReopenStore()
|
chatReopenStore()
|
||||||
let r = sendSimpleXCmd(.apiActivateChat(restoreChat: false))
|
let r: SEChatResponse = sendSimpleXCmd(SEChatCommand.apiActivateChat(restoreChat: false))
|
||||||
if case .cmdOk = r { return }
|
if case .cmdOk = r { return }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiSuspendChat(expired: Bool) {
|
func apiSuspendChat(expired: Bool) {
|
||||||
let r = sendSimpleXCmd(.apiSuspendChat(timeoutMicroseconds: expired ? 0 : 3_000000))
|
let r: SEChatResponse = sendSimpleXCmd(SEChatCommand.apiSuspendChat(timeoutMicroseconds: expired ? 0 : 3_000000))
|
||||||
// Block until `chatSuspended` received or 3 seconds has passed
|
// Block until `chatSuspended` received or 3 seconds has passed
|
||||||
var suspended = false
|
var suspended = false
|
||||||
if case .cmdOk = r, !expired {
|
if case .cmdOk = r, !expired {
|
||||||
let startTime = CFAbsoluteTimeGetCurrent()
|
let startTime = CFAbsoluteTimeGetCurrent()
|
||||||
while CFAbsoluteTimeGetCurrent() - startTime < 3 {
|
while CFAbsoluteTimeGetCurrent() - startTime < 3 {
|
||||||
switch recvSimpleXMsg(messageTimeout: 3_500000) {
|
let msg: SEChatResponse? = recvSimpleXMsg(messageTimeout: 3_500000)
|
||||||
|
switch msg {
|
||||||
case .chatSuspended:
|
case .chatSuspended:
|
||||||
suspended = false
|
suspended = false
|
||||||
break
|
break
|
||||||
|
@ -105,9 +106,166 @@ func apiSuspendChat(expired: Bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !suspended {
|
if !suspended {
|
||||||
_ = sendSimpleXCmd(.apiSuspendChat(timeoutMicroseconds: 0))
|
let _r1: SEChatResponse = sendSimpleXCmd(SEChatCommand.apiSuspendChat(timeoutMicroseconds: 0))
|
||||||
}
|
}
|
||||||
logger.debug("close store")
|
logger.debug("close store")
|
||||||
chatCloseStore()
|
chatCloseStore()
|
||||||
SEChatState.shared.set(.inactive)
|
SEChatState.shared.set(.inactive)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum SEChatCommand: ChatCmdProtocol {
|
||||||
|
case showActiveUser
|
||||||
|
case startChat(mainApp: Bool, enableSndFiles: Bool)
|
||||||
|
case apiActivateChat(restoreChat: Bool)
|
||||||
|
case apiSuspendChat(timeoutMicroseconds: Int)
|
||||||
|
case apiSetNetworkConfig(networkConfig: NetCfg)
|
||||||
|
case apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String)
|
||||||
|
case apiSetEncryptLocalFiles(enable: Bool)
|
||||||
|
case apiGetChats(userId: Int64)
|
||||||
|
case apiCreateChatItems(noteFolderId: Int64, composedMessages: [ComposedMessage])
|
||||||
|
case apiSendMessages(type: ChatType, id: Int64, live: Bool, ttl: Int?, composedMessages: [ComposedMessage])
|
||||||
|
|
||||||
|
var cmdString: String {
|
||||||
|
switch self {
|
||||||
|
case .showActiveUser: return "/u"
|
||||||
|
case let .startChat(mainApp, enableSndFiles): return "/_start main=\(onOff(mainApp)) snd_files=\(onOff(enableSndFiles))"
|
||||||
|
case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))"
|
||||||
|
case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)"
|
||||||
|
case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))"
|
||||||
|
case let .apiSetAppFilePaths(filesFolder, tempFolder, assetsFolder):
|
||||||
|
return "/set file paths \(encodeJSON(AppFilePaths(appFilesFolder: filesFolder, appTempFolder: tempFolder, appAssetsFolder: assetsFolder)))"
|
||||||
|
case let .apiSetEncryptLocalFiles(enable): return "/_files_encrypt \(onOff(enable))"
|
||||||
|
case let .apiGetChats(userId): return "/_get chats \(userId) pcc=on"
|
||||||
|
case let .apiCreateChatItems(noteFolderId, composedMessages):
|
||||||
|
let msgs = encodeJSON(composedMessages)
|
||||||
|
return "/_create *\(noteFolderId) json \(msgs)"
|
||||||
|
case let .apiSendMessages(type, id, live, ttl, composedMessages):
|
||||||
|
let msgs = encodeJSON(composedMessages)
|
||||||
|
let ttlStr = ttl != nil ? "\(ttl!)" : "default"
|
||||||
|
return "/_send \(ref(type, id)) live=\(onOff(live)) ttl=\(ttlStr) json \(msgs)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ref(_ type: ChatType, _ id: Int64) -> String {
|
||||||
|
"\(type.rawValue)\(id)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SEChatResponse: Decodable, Error, ChatRespProtocol {
|
||||||
|
case response(type: String, json: String)
|
||||||
|
case activeUser(user: User)
|
||||||
|
case chatStarted
|
||||||
|
case chatRunning
|
||||||
|
case chatSuspended
|
||||||
|
case apiChats(user: UserRef, chats: [ChatData])
|
||||||
|
case newChatItems(user: UserRef, chatItems: [AChatItem])
|
||||||
|
case sndFileProgressXFTP(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, sentSize: Int64, totalSize: Int64)
|
||||||
|
case sndFileCompleteXFTP(user: UserRef, chatItem: AChatItem, fileTransferMeta: FileTransferMeta)
|
||||||
|
case chatItemsStatusesUpdated(user: UserRef, chatItems: [AChatItem])
|
||||||
|
case sndFileError(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, errorMessage: String)
|
||||||
|
case sndFileWarning(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, errorMessage: String)
|
||||||
|
case cmdOk(user_: UserRef?)
|
||||||
|
case chatCmdError(user_: UserRef?, chatError: ChatError)
|
||||||
|
case chatError(user_: UserRef?, chatError: ChatError)
|
||||||
|
|
||||||
|
var responseType: String {
|
||||||
|
switch self {
|
||||||
|
case let .response(type, _): "* \(type)"
|
||||||
|
case .activeUser: "activeUser"
|
||||||
|
case .chatStarted: "chatStarted"
|
||||||
|
case .chatRunning: "chatRunning"
|
||||||
|
case .chatSuspended: "chatSuspended"
|
||||||
|
case .apiChats: "apiChats"
|
||||||
|
case .newChatItems: "newChatItems"
|
||||||
|
case .sndFileProgressXFTP: "sndFileProgressXFTP"
|
||||||
|
case .sndFileCompleteXFTP: "sndFileCompleteXFTP"
|
||||||
|
case .chatItemsStatusesUpdated: "chatItemsStatusesUpdated"
|
||||||
|
case .sndFileError: "sndFileError"
|
||||||
|
case .sndFileWarning: "sndFileWarning"
|
||||||
|
case .cmdOk: "cmdOk"
|
||||||
|
case .chatCmdError: "chatCmdError"
|
||||||
|
case .chatError: "chatError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var details: String {
|
||||||
|
switch self {
|
||||||
|
case let .response(_, json): return json
|
||||||
|
case let .activeUser(user): return String(describing: user)
|
||||||
|
case .chatStarted: return noDetails
|
||||||
|
case .chatRunning: return noDetails
|
||||||
|
case .chatSuspended: return noDetails
|
||||||
|
case let .apiChats(u, chats): return withUser(u, String(describing: chats))
|
||||||
|
case let .newChatItems(u, chatItems):
|
||||||
|
let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n")
|
||||||
|
return withUser(u, itemsString)
|
||||||
|
case let .sndFileProgressXFTP(u, chatItem, _, sentSize, totalSize): return withUser(u, "chatItem: \(String(describing: chatItem))\nsentSize: \(sentSize)\ntotalSize: \(totalSize)")
|
||||||
|
case let .sndFileCompleteXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||||
|
case let .chatItemsStatusesUpdated(u, chatItems):
|
||||||
|
let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n")
|
||||||
|
return withUser(u, itemsString)
|
||||||
|
case let .sndFileError(u, chatItem, _, err): return withUser(u, "error: \(String(describing: err))\nchatItem: \(String(describing: chatItem))")
|
||||||
|
case let .sndFileWarning(u, chatItem, _, err): return withUser(u, "error: \(String(describing: err))\nchatItem: \(String(describing: chatItem))")
|
||||||
|
case .cmdOk: return noDetails
|
||||||
|
case let .chatCmdError(u, chatError): return withUser(u, String(describing: chatError))
|
||||||
|
case let .chatError(u, chatError): return withUser(u, String(describing: chatError))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var noDetails: String { "\(responseType): no details" }
|
||||||
|
|
||||||
|
static func chatResponse(_ s: String) -> SEChatResponse {
|
||||||
|
let d = s.data(using: .utf8)!
|
||||||
|
// TODO is there a way to do it without copying the data? e.g:
|
||||||
|
// let p = UnsafeMutableRawPointer.init(mutating: UnsafeRawPointer(cjson))
|
||||||
|
// let d = Data.init(bytesNoCopy: p, count: strlen(cjson), deallocator: .free)
|
||||||
|
do {
|
||||||
|
let r = try jsonDecoder.decode(APIResponse<SEChatResponse>.self, from: d)
|
||||||
|
return r.resp
|
||||||
|
} catch {
|
||||||
|
logger.error("chatResponse jsonDecoder.decode error: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
|
||||||
|
var type: String?
|
||||||
|
var json: String?
|
||||||
|
if let j = try? JSONSerialization.jsonObject(with: d) as? NSDictionary {
|
||||||
|
if let jResp = j["resp"] as? NSDictionary, jResp.count == 1 || jResp.count == 2 {
|
||||||
|
type = jResp.allKeys[0] as? String
|
||||||
|
if jResp.count == 2 && type == "_owsf" {
|
||||||
|
type = jResp.allKeys[1] as? String
|
||||||
|
}
|
||||||
|
if type == "apiChats" {
|
||||||
|
if let r = parseApiChats(jResp) {
|
||||||
|
return .apiChats(user: r.user, chats: r.chats)
|
||||||
|
}
|
||||||
|
} else if type == "chatCmdError" {
|
||||||
|
if let jError = jResp["chatCmdError"] as? NSDictionary {
|
||||||
|
return .chatCmdError(user_: decodeUser_(jError), chatError: .invalidJSON(json: errorJson(jError) ?? ""))
|
||||||
|
}
|
||||||
|
} else if type == "chatError" {
|
||||||
|
if let jError = jResp["chatError"] as? NSDictionary {
|
||||||
|
return .chatError(user_: decodeUser_(jError), chatError: .invalidJSON(json: errorJson(jError) ?? ""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json = serializeJSON(j, options: .prettyPrinted)
|
||||||
|
}
|
||||||
|
return SEChatResponse.response(type: type ?? "invalid", json: json ?? s)
|
||||||
|
}
|
||||||
|
|
||||||
|
var chatError: ChatError? {
|
||||||
|
switch self {
|
||||||
|
case let .chatCmdError(_, error): error
|
||||||
|
case let .chatError(_, error): error
|
||||||
|
default: nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var chatErrorType: ChatErrorType? {
|
||||||
|
switch self {
|
||||||
|
case let .chatCmdError(_, .error(error)): error
|
||||||
|
case let .chatError(_, .error(error)): error
|
||||||
|
default: nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -303,7 +303,8 @@ class ShareModel: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch recvSimpleXMsg(messageTimeout: 1_000_000) {
|
let r: SEChatResponse? = recvSimpleXMsg(messageTimeout: 1_000_000)
|
||||||
|
switch r {
|
||||||
case let .sndFileProgressXFTP(_, ci, _, sentSize, totalSize):
|
case let .sndFileProgressXFTP(_, ci, _, sentSize, totalSize):
|
||||||
guard isMessage(for: ci) else { continue }
|
guard isMessage(for: ci) else { continue }
|
||||||
networkTimeout = CFAbsoluteTimeGetCurrent()
|
networkTimeout = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
|
@ -242,6 +242,8 @@
|
||||||
E5DCF9712C590272007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF96F2C590272007928CC /* Localizable.strings */; };
|
E5DCF9712C590272007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF96F2C590272007928CC /* Localizable.strings */; };
|
||||||
E5DCF9842C5902CE007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9822C5902CE007928CC /* Localizable.strings */; };
|
E5DCF9842C5902CE007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9822C5902CE007928CC /* Localizable.strings */; };
|
||||||
E5DCF9982C5906FF007928CC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9962C5906FF007928CC /* InfoPlist.strings */; };
|
E5DCF9982C5906FF007928CC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9962C5906FF007928CC /* InfoPlist.strings */; };
|
||||||
|
E5DDBE6E2DC4106800A0EFF0 /* AppAPITypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5DDBE6D2DC4106200A0EFF0 /* AppAPITypes.swift */; };
|
||||||
|
E5DDBE702DC4217900A0EFF0 /* NSEAPITypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5DDBE6F2DC4217900A0EFF0 /* NSEAPITypes.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -643,6 +645,8 @@
|
||||||
E5DCF9A62C590731007928CC /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
E5DCF9A62C590731007928CC /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
E5DCF9A72C590732007928CC /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
E5DCF9A72C590732007928CC /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
E5DCF9A82C590732007928CC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
E5DCF9A82C590732007928CC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
|
E5DDBE6D2DC4106200A0EFF0 /* AppAPITypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAPITypes.swift; sourceTree = "<group>"; };
|
||||||
|
E5DDBE6F2DC4217900A0EFF0 /* NSEAPITypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEAPITypes.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -795,6 +799,7 @@
|
||||||
5C764E87279CBC8E000C6508 /* Model */ = {
|
5C764E87279CBC8E000C6508 /* Model */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E5DDBE6D2DC4106200A0EFF0 /* AppAPITypes.swift */,
|
||||||
5C764E88279CBCB3000C6508 /* ChatModel.swift */,
|
5C764E88279CBCB3000C6508 /* ChatModel.swift */,
|
||||||
5C2E260627A2941F00F70299 /* SimpleXAPI.swift */,
|
5C2E260627A2941F00F70299 /* SimpleXAPI.swift */,
|
||||||
5C35CFC727B2782E00FB6C6D /* BGManager.swift */,
|
5C35CFC727B2782E00FB6C6D /* BGManager.swift */,
|
||||||
|
@ -990,6 +995,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
5CDCAD5128186DE400503DA2 /* SimpleX NSE.entitlements */,
|
5CDCAD5128186DE400503DA2 /* SimpleX NSE.entitlements */,
|
||||||
|
E5DDBE6F2DC4217900A0EFF0 /* NSEAPITypes.swift */,
|
||||||
5CDCAD472818589900503DA2 /* NotificationService.swift */,
|
5CDCAD472818589900503DA2 /* NotificationService.swift */,
|
||||||
5CDCAD492818589900503DA2 /* Info.plist */,
|
5CDCAD492818589900503DA2 /* Info.plist */,
|
||||||
5CB0BA862826CB3A00B3292C /* InfoPlist.strings */,
|
5CB0BA862826CB3A00B3292C /* InfoPlist.strings */,
|
||||||
|
@ -1006,9 +1012,9 @@
|
||||||
5CDCAD7228188CFF00503DA2 /* ChatTypes.swift */,
|
5CDCAD7228188CFF00503DA2 /* ChatTypes.swift */,
|
||||||
5CDCAD7428188D2900503DA2 /* APITypes.swift */,
|
5CDCAD7428188D2900503DA2 /* APITypes.swift */,
|
||||||
5C5E5D3C282447AB00B0488A /* CallTypes.swift */,
|
5C5E5D3C282447AB00B0488A /* CallTypes.swift */,
|
||||||
|
5CDCAD7D2818941F00503DA2 /* API.swift */,
|
||||||
CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */,
|
CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */,
|
||||||
5C9FD96A27A56D4D0075386C /* JSON.swift */,
|
5C9FD96A27A56D4D0075386C /* JSON.swift */,
|
||||||
5CDCAD7D2818941F00503DA2 /* API.swift */,
|
|
||||||
5CDCAD80281A7E2700503DA2 /* Notifications.swift */,
|
5CDCAD80281A7E2700503DA2 /* Notifications.swift */,
|
||||||
5CBD2859295711D700EC2CF4 /* ImageUtils.swift */,
|
5CBD2859295711D700EC2CF4 /* ImageUtils.swift */,
|
||||||
CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */,
|
CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */,
|
||||||
|
@ -1534,6 +1540,7 @@
|
||||||
5CA059EB279559F40002BEB4 /* SimpleXApp.swift in Sources */,
|
5CA059EB279559F40002BEB4 /* SimpleXApp.swift in Sources */,
|
||||||
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */,
|
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */,
|
||||||
6448BBB628FA9D56000D2AB9 /* GroupLinkView.swift in Sources */,
|
6448BBB628FA9D56000D2AB9 /* GroupLinkView.swift in Sources */,
|
||||||
|
E5DDBE6E2DC4106800A0EFF0 /* AppAPITypes.swift in Sources */,
|
||||||
8C9BC2652C240D5200875A27 /* ThemeModeEditor.swift in Sources */,
|
8C9BC2652C240D5200875A27 /* ThemeModeEditor.swift in Sources */,
|
||||||
5CB346E92869E8BA001FD2EF /* PushEnvironment.swift in Sources */,
|
5CB346E92869E8BA001FD2EF /* PushEnvironment.swift in Sources */,
|
||||||
5C55A91F283AD0E400C4E99E /* CallManager.swift in Sources */,
|
5C55A91F283AD0E400C4E99E /* CallManager.swift in Sources */,
|
||||||
|
@ -1606,6 +1613,7 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */,
|
5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */,
|
||||||
|
E5DDBE702DC4217900A0EFF0 /* NSEAPITypes.swift in Sources */,
|
||||||
5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */,
|
5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
|
@ -110,19 +110,19 @@ public func resetChatCtrl() {
|
||||||
migrationResult = nil
|
migrationResult = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sendSimpleXCmd(_ cmd: ChatCommand, _ ctrl: chat_ctrl? = nil) -> ChatResponse {
|
public func sendSimpleXCmd<CR: ChatRespProtocol>(_ cmd: ChatCmdProtocol, _ ctrl: chat_ctrl? = nil) -> CR {
|
||||||
var c = cmd.cmdString.cString(using: .utf8)!
|
var c = cmd.cmdString.cString(using: .utf8)!
|
||||||
let cjson = chat_send_cmd(ctrl ?? getChatCtrl(), &c)!
|
let cjson = chat_send_cmd(ctrl ?? getChatCtrl(), &c)!
|
||||||
return chatResponse(fromCString(cjson))
|
return CR.chatResponse(fromCString(cjson))
|
||||||
}
|
}
|
||||||
|
|
||||||
// in microseconds
|
// in microseconds
|
||||||
public let MESSAGE_TIMEOUT: Int32 = 15_000_000
|
public let MESSAGE_TIMEOUT: Int32 = 15_000_000
|
||||||
|
|
||||||
public func recvSimpleXMsg(_ ctrl: chat_ctrl? = nil, messageTimeout: Int32 = MESSAGE_TIMEOUT) -> ChatResponse? {
|
public func recvSimpleXMsg<CR: ChatRespProtocol>(_ ctrl: chat_ctrl? = nil, messageTimeout: Int32 = MESSAGE_TIMEOUT) -> CR? {
|
||||||
if let cjson = chat_recv_msg_wait(ctrl ?? getChatCtrl(), messageTimeout) {
|
if let cjson = chat_recv_msg_wait(ctrl ?? getChatCtrl(), messageTimeout) {
|
||||||
let s = fromCString(cjson)
|
let s = fromCString(cjson)
|
||||||
return s == "" ? nil : chatResponse(s)
|
return s == "" ? nil : CR.chatResponse(s)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -177,89 +177,7 @@ public func fromCString(_ c: UnsafeMutablePointer<CChar>) -> String {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
public func chatResponse(_ s: String) -> ChatResponse {
|
public func decodeUser_(_ jDict: NSDictionary) -> UserRef? {
|
||||||
let d = s.data(using: .utf8)!
|
|
||||||
// TODO is there a way to do it without copying the data? e.g:
|
|
||||||
// let p = UnsafeMutableRawPointer.init(mutating: UnsafeRawPointer(cjson))
|
|
||||||
// let d = Data.init(bytesNoCopy: p, count: strlen(cjson), deallocator: .free)
|
|
||||||
do {
|
|
||||||
let r = try callWithLargeStack {
|
|
||||||
try jsonDecoder.decode(APIResponse.self, from: d)
|
|
||||||
}
|
|
||||||
return r.resp
|
|
||||||
} catch {
|
|
||||||
logger.error("chatResponse jsonDecoder.decode error: \(error.localizedDescription)")
|
|
||||||
}
|
|
||||||
|
|
||||||
var type: String?
|
|
||||||
var json: String?
|
|
||||||
if let j = try? JSONSerialization.jsonObject(with: d) as? NSDictionary {
|
|
||||||
if let jResp = j["resp"] as? NSDictionary, jResp.count == 1 || jResp.count == 2 {
|
|
||||||
type = jResp.allKeys[0] as? String
|
|
||||||
if jResp.count == 2 && type == "_owsf" {
|
|
||||||
type = jResp.allKeys[1] as? String
|
|
||||||
}
|
|
||||||
if type == "apiChats" {
|
|
||||||
if let jApiChats = jResp["apiChats"] as? NSDictionary,
|
|
||||||
let user: UserRef = try? decodeObject(jApiChats["user"] as Any),
|
|
||||||
let jChats = jApiChats["chats"] as? NSArray {
|
|
||||||
let chats = jChats.map { jChat in
|
|
||||||
if let chatData = try? parseChatData(jChat) {
|
|
||||||
return chatData.0
|
|
||||||
}
|
|
||||||
return ChatData.invalidJSON(serializeJSON(jChat, options: .prettyPrinted) ?? "")
|
|
||||||
}
|
|
||||||
return .apiChats(user: user, chats: chats)
|
|
||||||
}
|
|
||||||
} else if type == "apiChat" {
|
|
||||||
if let jApiChat = jResp["apiChat"] as? NSDictionary,
|
|
||||||
let user: UserRef = try? decodeObject(jApiChat["user"] as Any),
|
|
||||||
let jChat = jApiChat["chat"] as? NSDictionary,
|
|
||||||
let (chat, navInfo) = try? parseChatData(jChat, jApiChat["navInfo"] as? NSDictionary) {
|
|
||||||
return .apiChat(user: user, chat: chat, navInfo: navInfo)
|
|
||||||
}
|
|
||||||
} else if type == "chatCmdError" {
|
|
||||||
if let jError = jResp["chatCmdError"] as? NSDictionary {
|
|
||||||
return .chatCmdError(user_: decodeUser_(jError), chatError: .invalidJSON(json: errorJson(jError) ?? ""))
|
|
||||||
}
|
|
||||||
} else if type == "chatError" {
|
|
||||||
if let jError = jResp["chatError"] as? NSDictionary {
|
|
||||||
return .chatError(user_: decodeUser_(jError), chatError: .invalidJSON(json: errorJson(jError) ?? ""))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
json = serializeJSON(j, options: .prettyPrinted)
|
|
||||||
}
|
|
||||||
return ChatResponse.response(type: type ?? "invalid", json: json ?? s)
|
|
||||||
}
|
|
||||||
|
|
||||||
private let largeStackSize: Int = 2 * 1024 * 1024
|
|
||||||
|
|
||||||
private func callWithLargeStack<T>(_ f: @escaping () throws -> T) throws -> T {
|
|
||||||
let semaphore = DispatchSemaphore(value: 0)
|
|
||||||
var result: Result<T, Error>?
|
|
||||||
let thread = Thread {
|
|
||||||
do {
|
|
||||||
result = .success(try f())
|
|
||||||
} catch {
|
|
||||||
result = .failure(error)
|
|
||||||
}
|
|
||||||
semaphore.signal()
|
|
||||||
}
|
|
||||||
|
|
||||||
thread.stackSize = largeStackSize
|
|
||||||
thread.qualityOfService = Thread.current.qualityOfService
|
|
||||||
thread.start()
|
|
||||||
|
|
||||||
semaphore.wait()
|
|
||||||
|
|
||||||
switch result! {
|
|
||||||
case let .success(r): return r
|
|
||||||
case let .failure(e): throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func decodeUser_(_ jDict: NSDictionary) -> UserRef? {
|
|
||||||
if let user_ = jDict["user_"] {
|
if let user_ = jDict["user_"] {
|
||||||
try? decodeObject(user_ as Any)
|
try? decodeObject(user_ as Any)
|
||||||
} else {
|
} else {
|
||||||
|
@ -267,7 +185,7 @@ private func decodeUser_(_ jDict: NSDictionary) -> UserRef? {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func errorJson(_ jDict: NSDictionary) -> String? {
|
public func errorJson(_ jDict: NSDictionary) -> String? {
|
||||||
if let chatError = jDict["chatError"] {
|
if let chatError = jDict["chatError"] {
|
||||||
serializeJSON(chatError)
|
serializeJSON(chatError)
|
||||||
} else {
|
} else {
|
||||||
|
@ -275,7 +193,7 @@ private func errorJson(_ jDict: NSDictionary) -> String? {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseChatData(_ jChat: Any, _ jNavInfo: Any? = nil) throws -> (ChatData, NavigationInfo) {
|
public func parseChatData(_ jChat: Any, _ jNavInfo: Any? = nil) throws -> (ChatData, NavigationInfo) {
|
||||||
let jChatDict = jChat as! NSDictionary
|
let jChatDict = jChat as! NSDictionary
|
||||||
let chatInfo: ChatInfo = try decodeObject(jChatDict["chatInfo"]!)
|
let chatInfo: ChatInfo = try decodeObject(jChatDict["chatInfo"]!)
|
||||||
let chatStats: ChatStats = try decodeObject(jChatDict["chatStats"]!)
|
let chatStats: ChatStats = try decodeObject(jChatDict["chatStats"]!)
|
||||||
|
@ -294,7 +212,7 @@ func parseChatData(_ jChat: Any, _ jNavInfo: Any? = nil) throws -> (ChatData, Na
|
||||||
return (ChatData(chatInfo: chatInfo, chatItems: chatItems, chatStats: chatStats), navInfo)
|
return (ChatData(chatInfo: chatInfo, chatItems: chatItems, chatStats: chatStats), navInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeObject<T: Decodable>(_ obj: Any) throws -> T {
|
public func decodeObject<T: Decodable>(_ obj: Any) throws -> T {
|
||||||
try jsonDecoder.decode(T.self, from: JSONSerialization.data(withJSONObject: obj))
|
try jsonDecoder.decode(T.self, from: JSONSerialization.data(withJSONObject: obj))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,7 +223,7 @@ func decodeProperty<T: Decodable>(_ obj: Any, _ prop: NSString) -> T? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func serializeJSON(_ obj: Any, options: JSONSerialization.WritingOptions = []) -> String? {
|
public func serializeJSON(_ obj: Any, options: JSONSerialization.WritingOptions = []) -> String? {
|
||||||
if let d = try? JSONSerialization.data(withJSONObject: obj, options: options) {
|
if let d = try? JSONSerialization.data(withJSONObject: obj, options: options) {
|
||||||
return String(decoding: d, as: UTF8.self)
|
return String(decoding: d, as: UTF8.self)
|
||||||
}
|
}
|
||||||
|
@ -313,14 +231,14 @@ func serializeJSON(_ obj: Any, options: JSONSerialization.WritingOptions = []) -
|
||||||
}
|
}
|
||||||
|
|
||||||
public func responseError(_ err: Error) -> String {
|
public func responseError(_ err: Error) -> String {
|
||||||
if let r = err as? ChatResponse {
|
if let r = err as? ChatRespProtocol {
|
||||||
switch r {
|
if let e = r.chatError {
|
||||||
case let .chatCmdError(_, chatError): return chatErrorString(chatError)
|
chatErrorString(e)
|
||||||
case let .chatError(_, chatError): return chatErrorString(chatError)
|
} else {
|
||||||
default: return "\(String(describing: r.responseType)), details: \(String(describing: r.details))"
|
"\(String(describing: r.responseType)), details: \(String(describing: r.details))"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return String(describing: err)
|
String(describing: err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3935,7 +3935,7 @@ public enum MsgContent: Equatable, Hashable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmdString: String {
|
public var cmdString: String {
|
||||||
"json \(encodeJSON(self))"
|
"json \(encodeJSON(self))"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ public struct ErrorAlert: Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(_ error: any Error) {
|
public init(_ error: any Error) {
|
||||||
self = if let chatResponse = error as? ChatResponse {
|
self = if let chatResponse = error as? ChatRespProtocol {
|
||||||
ErrorAlert(chatResponse)
|
ErrorAlert(chatResponse)
|
||||||
} else {
|
} else {
|
||||||
ErrorAlert("\(error.localizedDescription)")
|
ErrorAlert("\(error.localizedDescription)")
|
||||||
|
@ -48,7 +48,7 @@ public struct ErrorAlert: Error {
|
||||||
self = ErrorAlert("\(chatErrorString(chatError))")
|
self = ErrorAlert("\(chatErrorString(chatError))")
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(_ chatResponse: ChatResponse) {
|
public init(_ chatResponse: ChatRespProtocol) {
|
||||||
self = if let networkErrorAlert = getNetworkErrorAlert(chatResponse) {
|
self = if let networkErrorAlert = getNetworkErrorAlert(chatResponse) {
|
||||||
networkErrorAlert
|
networkErrorAlert
|
||||||
} else {
|
} else {
|
||||||
|
@ -94,22 +94,21 @@ extension View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getNetworkErrorAlert(_ r: ChatResponse) -> ErrorAlert? {
|
public func getNetworkErrorAlert(_ r: ChatRespProtocol) -> ErrorAlert? {
|
||||||
switch r {
|
switch r.chatError {
|
||||||
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TIMEOUT))):
|
case let .errorAgent(.BROKER(addr, .TIMEOUT)):
|
||||||
return ErrorAlert(title: "Connection timeout", message: "Please check your network connection with \(serverHostname(addr)) and try again.")
|
ErrorAlert(title: "Connection timeout", message: "Please check your network connection with \(serverHostname(addr)) and try again.")
|
||||||
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .NETWORK))):
|
case let .errorAgent(.BROKER(addr, .NETWORK)):
|
||||||
return ErrorAlert(title: "Connection error", message: "Please check your network connection with \(serverHostname(addr)) and try again.")
|
ErrorAlert(title: "Connection error", message: "Please check your network connection with \(serverHostname(addr)) and try again.")
|
||||||
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .HOST))):
|
case let .errorAgent(.BROKER(addr, .HOST)):
|
||||||
return ErrorAlert(title: "Connection error", message: "Server address is incompatible with network settings: \(serverHostname(addr)).")
|
ErrorAlert(title: "Connection error", message: "Server address is incompatible with network settings: \(serverHostname(addr)).")
|
||||||
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TRANSPORT(.version)))):
|
case let .errorAgent(.BROKER(addr, .TRANSPORT(.version))):
|
||||||
return ErrorAlert(title: "Connection error", message: "Server version is incompatible with your app: \(serverHostname(addr)).")
|
ErrorAlert(title: "Connection error", message: "Server version is incompatible with your app: \(serverHostname(addr)).")
|
||||||
case let .chatCmdError(_, .errorAgent(.SMP(serverAddress, .PROXY(proxyErr)))):
|
case let .errorAgent(.SMP(serverAddress, .PROXY(proxyErr))):
|
||||||
return smpProxyErrorAlert(proxyErr, serverAddress)
|
smpProxyErrorAlert(proxyErr, serverAddress)
|
||||||
case let .chatCmdError(_, .errorAgent(.PROXY(proxyServer, relayServer, .protocolError(.PROXY(proxyErr))))):
|
case let .errorAgent(.PROXY(proxyServer, relayServer, .protocolError(.PROXY(proxyErr)))):
|
||||||
return proxyDestinationErrorAlert(proxyErr, proxyServer, relayServer)
|
proxyDestinationErrorAlert(proxyErr, proxyServer, relayServer)
|
||||||
default:
|
default: nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue