ios: receive message in NSE (#742)
This commit is contained in:
committed by
GitHub
parent
c5c65f813b
commit
291096d87f
@@ -47,7 +47,7 @@ enum TerminalItem: Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
private func beginBGTask(_ handler: (() -> Void)? = nil) -> (() -> Void) {
|
||||
func beginBGTask(_ handler: (() -> Void)? = nil) -> (() -> Void) {
|
||||
var id: UIBackgroundTaskIdentifier!
|
||||
var running = true
|
||||
let endTask = {
|
||||
@@ -143,6 +143,20 @@ func apiStartChat() throws -> Bool {
|
||||
}
|
||||
}
|
||||
|
||||
func apiStopChat() throws {
|
||||
let r = chatSendCmdSync(.apiStopChat)
|
||||
switch r {
|
||||
case .chatStopped: return
|
||||
default: throw r
|
||||
}
|
||||
}
|
||||
|
||||
func apiSetAppPhase(appPhase: AgentPhase) {
|
||||
let r = chatSendCmdSync(.apiSetAppPhase(appPhase: appPhase))
|
||||
if case .cmdOk = r { return }
|
||||
logger.error("apiSetAppPhase error: \(String(describing: r))")
|
||||
}
|
||||
|
||||
func apiSetFilesFolder(filesFolder: String) throws {
|
||||
let r = chatSendCmdSync(.setFilesFolder(filesFolder: filesFolder))
|
||||
if case .cmdOk = r { return }
|
||||
@@ -512,7 +526,7 @@ class ChatReceiver {
|
||||
func receiveMsgLoop() async {
|
||||
let msg = await chatRecvMsg()
|
||||
self._lastMsgTime = .now
|
||||
processReceivedMsg(msg)
|
||||
await processReceivedMsg(msg)
|
||||
if self.receiveMessages {
|
||||
do { try await Task.sleep(nanoseconds: 7_500_000) }
|
||||
catch { logger.error("receiveMsgLoop: Task.sleep error: \(error.localizedDescription)") }
|
||||
@@ -528,9 +542,9 @@ class ChatReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
func processReceivedMsg(_ res: ChatResponse) {
|
||||
func processReceivedMsg(_ res: ChatResponse) async {
|
||||
let m = ChatModel.shared
|
||||
DispatchQueue.main.async {
|
||||
await MainActor.run {
|
||||
m.terminalItems.append(.resp(.now, res))
|
||||
logger.debug("processReceivedMsg: \(res.responseType)")
|
||||
switch res {
|
||||
@@ -682,6 +696,8 @@ func processReceivedMsg(_ res: ChatResponse) {
|
||||
m.callCommand = .end
|
||||
// CallController.shared.reportCallRemoteEnded(call: call)
|
||||
}
|
||||
case let .appPhase(appPhase):
|
||||
setAppState(AppState(appPhase: appPhase))
|
||||
default:
|
||||
logger.debug("unsupported event: \(res.responseType)")
|
||||
}
|
||||
|
||||
@@ -11,13 +11,6 @@ import SimpleXChat
|
||||
|
||||
let logger = Logger()
|
||||
|
||||
let machMessenger = MachMessenger(APP_MACH_PORT, callback: receivedNSEMachMessage)
|
||||
|
||||
func receivedNSEMachMessage(msgId: Int32, msg: String) -> String? {
|
||||
logger.debug("MachMessenger: receivedNSEMachMessage \"\(msg)\" from NSE, replying")
|
||||
return "reply from App to: \(msg)"
|
||||
}
|
||||
|
||||
@main
|
||||
struct SimpleXApp: App {
|
||||
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
@@ -34,7 +27,6 @@ struct SimpleXApp: App {
|
||||
UserDefaults.standard.register(defaults: appDefaults)
|
||||
BGManager.shared.register()
|
||||
NtfManager.shared.registerCategories()
|
||||
machMessenger.start()
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
@@ -50,20 +42,18 @@ struct SimpleXApp: App {
|
||||
}
|
||||
.onChange(of: scenePhase) { phase in
|
||||
logger.debug("scenePhase \(String(describing: scenePhase))")
|
||||
let res = machMessenger.sendMessageWithReply(NSE_MACH_PORT, msg: "App scenePhase changed to \(String(describing: scenePhase))")
|
||||
logger.debug("MachMessenger \(String(describing: res), privacy: .public)")
|
||||
setAppState(phase)
|
||||
switch (phase) {
|
||||
case .background:
|
||||
pauseApp()
|
||||
BGManager.shared.schedule()
|
||||
if userAuthorized == true {
|
||||
enteredBackground = ProcessInfo.processInfo.systemUptime
|
||||
}
|
||||
doAuthenticate = false
|
||||
machMessenger.stop()
|
||||
case .active:
|
||||
setAppState(.active)
|
||||
apiSetAppPhase(appPhase: .active)
|
||||
doAuthenticate = authenticationExpired()
|
||||
machMessenger.start()
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -71,6 +61,18 @@ struct SimpleXApp: App {
|
||||
}
|
||||
}
|
||||
|
||||
private func pauseApp() {
|
||||
setAppState(.pausing)
|
||||
apiSetAppPhase(appPhase: .paused)
|
||||
let endTask = beginBGTask {
|
||||
if getAppState() != .active {
|
||||
setAppState(.suspending)
|
||||
apiSetAppPhase(appPhase: .suspended)
|
||||
}
|
||||
}
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + maxTaskDuration, execute: endTask)
|
||||
}
|
||||
|
||||
private func authenticationExpired() -> Bool {
|
||||
if let enteredBackground = enteredBackground {
|
||||
return ProcessInfo.processInfo.systemUptime - enteredBackground >= 30
|
||||
|
||||
@@ -12,29 +12,28 @@ import SimpleXChat
|
||||
|
||||
let logger = Logger()
|
||||
|
||||
let machMessenger = MachMessenger(NSE_MACH_PORT, callback: receivedAppMachMessage)
|
||||
|
||||
class NotificationService: UNNotificationServiceExtension {
|
||||
var contentHandler: ((UNNotificationContent) -> Void)?
|
||||
var bestAttemptContent: UNMutableNotificationContent?
|
||||
|
||||
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
||||
logger.debug("NotificationService.didReceive")
|
||||
machMessenger.start()
|
||||
let res = machMessenger.sendMessageWithReply(APP_MACH_PORT, msg: "starting NSE didReceive")
|
||||
logger.debug("MachMessenger \(String(describing: res), privacy: .public)")
|
||||
if getAppState() != .background {
|
||||
let appState = getAppState()
|
||||
if appState.running {
|
||||
contentHandler(request.content)
|
||||
machMessenger.stop()
|
||||
return
|
||||
}
|
||||
logger.debug("NotificationService: app is in the background")
|
||||
self.contentHandler = contentHandler
|
||||
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
|
||||
if let _ = startChat() {
|
||||
let userInfo = request.content.userInfo
|
||||
if let ntfData = userInfo["notificationData"] as? [AnyHashable : Any],
|
||||
let nonce = ntfData["nonce"] as? String,
|
||||
let encNtfInfo = ntfData["message"] as? String,
|
||||
let _ = startChat() {
|
||||
apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo)
|
||||
let content = receiveMessages()
|
||||
contentHandler (content)
|
||||
machMessenger.stop()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -44,7 +43,6 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
contentHandler(bestAttemptContent)
|
||||
}
|
||||
machMessenger.stop()
|
||||
}
|
||||
|
||||
override func serviceExtensionTimeWillExpire() {
|
||||
@@ -145,3 +143,12 @@ func apiSetFilesFolder(filesFolder: String) throws {
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiGetNtfMessage(nonce: String, encNtfInfo: String) {
|
||||
let r = sendSimpleXCmd(.apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo))
|
||||
if case let .ntfMessages(connEntity, msgTs, ntfMessages) = r {
|
||||
print(connEntity)
|
||||
print(msgTs)
|
||||
print(ntfMessages)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ public enum ChatCommand {
|
||||
case showActiveUser
|
||||
case createActiveUser(profile: Profile)
|
||||
case startChat
|
||||
case apiStopChat
|
||||
case apiSetAppPhase(appPhase: AgentPhase)
|
||||
case setFilesFolder(filesFolder: String)
|
||||
case apiGetChats
|
||||
case apiGetChat(type: ChatType, id: Int64)
|
||||
@@ -25,6 +27,7 @@ public enum ChatCommand {
|
||||
case apiVerifyToken(token: String, code: String, nonce: String)
|
||||
case apiIntervalNofication(token: String, interval: Int)
|
||||
case apiDeleteToken(token: String)
|
||||
case apiGetNtfMessage(nonce: String, encNtfInfo: String)
|
||||
case getUserSMPServers
|
||||
case setUserSMPServers(smpServers: [String])
|
||||
case addContact
|
||||
@@ -56,6 +59,8 @@ public enum ChatCommand {
|
||||
case .showActiveUser: return "/u"
|
||||
case let .createActiveUser(profile): return "/u \(profile.displayName) \(profile.fullName)"
|
||||
case .startChat: return "/_start"
|
||||
case .apiStopChat: return "/_stop"
|
||||
case let .apiSetAppPhase(appPhase): return "/_app phase \(appPhase)"
|
||||
case let .setFilesFolder(filesFolder): return "/_files_folder \(filesFolder)"
|
||||
case .apiGetChats: return "/_get chats pcc=on"
|
||||
case let .apiGetChat(type, id): return "/_get chat \(ref(type, id)) count=100"
|
||||
@@ -68,6 +73,7 @@ public enum ChatCommand {
|
||||
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)"
|
||||
case let .apiGetNtfMessage(nonce, encNtfInfo): return "/_ntf message \(nonce) \(encNtfInfo)"
|
||||
case .getUserSMPServers: return "/smp_servers"
|
||||
case let .setUserSMPServers(smpServers): return "/smp_servers \(smpServersStr(smpServers: smpServers))"
|
||||
case .addContact: return "/connect"
|
||||
@@ -101,6 +107,8 @@ public enum ChatCommand {
|
||||
case .showActiveUser: return "showActiveUser"
|
||||
case .createActiveUser: return "createActiveUser"
|
||||
case .startChat: return "startChat"
|
||||
case .apiStopChat: return "apiStopChat"
|
||||
case .apiSetAppPhase: return "apiSetAppPhase"
|
||||
case .setFilesFolder: return "setFilesFolder"
|
||||
case .apiGetChats: return "apiGetChats"
|
||||
case .apiGetChat: return "apiGetChat"
|
||||
@@ -111,6 +119,7 @@ public enum ChatCommand {
|
||||
case .apiVerifyToken: return "apiVerifyToken"
|
||||
case .apiIntervalNofication: return "apiIntervalNofication"
|
||||
case .apiDeleteToken: return "apiDeleteToken"
|
||||
case .apiGetNtfMessage: return "apiGetNtfMessage"
|
||||
case .getUserSMPServers: return "getUserSMPServers"
|
||||
case .setUserSMPServers: return "setUserSMPServers"
|
||||
case .addContact: return "addContact"
|
||||
@@ -156,6 +165,8 @@ public enum ChatResponse: Decodable, Error {
|
||||
case activeUser(user: User)
|
||||
case chatStarted
|
||||
case chatRunning
|
||||
case chatStopped
|
||||
case appPhase(appPhase: AgentPhase)
|
||||
case apiChats(chats: [ChatData])
|
||||
case apiChat(chat: ChatData)
|
||||
case userSMPServers(smpServers: [String])
|
||||
@@ -205,6 +216,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case callExtraInfo(contact: Contact, extraInfo: WebRTCExtraInfo)
|
||||
case callEnded(contact: Contact)
|
||||
case ntfTokenStatus(status: NtfTknStatus)
|
||||
case ntfMessages(connEntity: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
|
||||
case newContactConnection(connection: PendingContactConnection)
|
||||
case contactConnectionDeleted(connection: PendingContactConnection)
|
||||
case cmdOk
|
||||
@@ -218,6 +230,8 @@ public enum ChatResponse: Decodable, Error {
|
||||
case .activeUser: return "activeUser"
|
||||
case .chatStarted: return "chatStarted"
|
||||
case .chatRunning: return "chatRunning"
|
||||
case .chatStopped: return "chatStopped"
|
||||
case .appPhase: return "appPhase"
|
||||
case .apiChats: return "apiChats"
|
||||
case .apiChat: return "apiChat"
|
||||
case .userSMPServers: return "userSMPServers"
|
||||
@@ -265,6 +279,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case .callExtraInfo: return "callExtraInfo"
|
||||
case .callEnded: return "callEnded"
|
||||
case .ntfTokenStatus: return "ntfTokenStatus"
|
||||
case .ntfMessages: return "ntfMessages"
|
||||
case .newContactConnection: return "newContactConnection"
|
||||
case .contactConnectionDeleted: return "contactConnectionDeleted"
|
||||
case .cmdOk: return "cmdOk"
|
||||
@@ -281,6 +296,8 @@ public enum ChatResponse: Decodable, Error {
|
||||
case let .activeUser(user): return String(describing: user)
|
||||
case .chatStarted: return noDetails
|
||||
case .chatRunning: return noDetails
|
||||
case .chatStopped: return noDetails
|
||||
case let .appPhase(appPhase): return appPhase.rawValue
|
||||
case let .apiChats(chats): return String(describing: chats)
|
||||
case let .apiChat(chat): return String(describing: chat)
|
||||
case let .userSMPServers(smpServers): return String(describing: smpServers)
|
||||
@@ -328,6 +345,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case let .callExtraInfo(contact, extraInfo): return "contact: \(contact.id)\nextraInfo: \(String(describing: extraInfo))"
|
||||
case let .callEnded(contact): return "contact: \(contact.id)"
|
||||
case let .ntfTokenStatus(status): return String(describing: status)
|
||||
case let .ntfMessages(connEntity, msgTs, ntfMessages): return "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))"
|
||||
case let .newContactConnection(connection): return String(describing: connection)
|
||||
case let .contactConnectionDeleted(connection): return String(describing: connection)
|
||||
case .cmdOk: return noDetails
|
||||
@@ -346,6 +364,12 @@ struct ComposedMessage: Encodable {
|
||||
var msgContent: MsgContent
|
||||
}
|
||||
|
||||
public enum AgentPhase: String, Codable {
|
||||
case active = "ACTIVE"
|
||||
case paused = "PAUSED"
|
||||
case suspended = "SUSPENDED"
|
||||
}
|
||||
|
||||
public func decodeJSON<T: Decodable>(_ json: String) -> T? {
|
||||
if let data = json.data(using: .utf8) {
|
||||
return try? jsonDecoder.decode(T.self, from: data)
|
||||
|
||||
@@ -9,169 +9,51 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
let GROUP_DEFAULT_APP_IN_BACKGROUND = "appInBackground"
|
||||
let GROUP_DEFAULT_APP_STATE = "appState"
|
||||
|
||||
let APP_GROUP_NAME = "group.chat.simplex.app"
|
||||
|
||||
public let NSE_MACH_PORT = "\(APP_GROUP_NAME).nse" as CFString
|
||||
|
||||
public let APP_MACH_PORT = "\(APP_GROUP_NAME).app" as CFString
|
||||
|
||||
func getGroupDefaults() -> UserDefaults? {
|
||||
UserDefaults(suiteName: APP_GROUP_NAME)
|
||||
}
|
||||
|
||||
public func setAppState(_ phase: ScenePhase) {
|
||||
public enum AppState: String {
|
||||
case active
|
||||
case pausing
|
||||
case paused
|
||||
case suspending
|
||||
case suspended
|
||||
|
||||
public init(appPhase: AgentPhase) {
|
||||
switch appPhase {
|
||||
case .active: self = .active
|
||||
case .paused: self = .paused
|
||||
case .suspended: self = .suspended
|
||||
}
|
||||
}
|
||||
|
||||
public var running: Bool {
|
||||
switch self {
|
||||
case .paused: return false
|
||||
case .suspending: return false
|
||||
case .suspended: return false
|
||||
default: return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func setAppState(_ state: AppState) {
|
||||
if let defaults = getGroupDefaults() {
|
||||
defaults.set(phase == .background, forKey: GROUP_DEFAULT_APP_IN_BACKGROUND)
|
||||
defaults.set(state.rawValue, forKey: GROUP_DEFAULT_APP_STATE)
|
||||
defaults.synchronize()
|
||||
}
|
||||
}
|
||||
|
||||
public func getAppState() -> ScenePhase {
|
||||
if let defaults = getGroupDefaults() {
|
||||
if defaults.bool(forKey: GROUP_DEFAULT_APP_IN_BACKGROUND) {
|
||||
return .background
|
||||
}
|
||||
public func getAppState() -> AppState {
|
||||
if let defaults = getGroupDefaults(),
|
||||
let rawValue = defaults.string(forKey: GROUP_DEFAULT_APP_STATE),
|
||||
let state = AppState(rawValue: rawValue) {
|
||||
return state
|
||||
}
|
||||
return .active
|
||||
}
|
||||
|
||||
let MACH_SEND_TIMEOUT: CFTimeInterval = 1.0
|
||||
let MACH_REPLY_TIMEOUT: CFTimeInterval = 1.0
|
||||
|
||||
public class MachMessenger {
|
||||
public init(_ localPortName: CFString, callback: @escaping Callback) {
|
||||
self.localPortName = localPortName
|
||||
self.callback = callback
|
||||
self.localPort = nil
|
||||
}
|
||||
|
||||
var localPortName: CFString
|
||||
var callback: Callback
|
||||
var localPort: CFMessagePort?
|
||||
|
||||
public enum SendError: Error {
|
||||
case sndTimeout
|
||||
case rcvTimeout
|
||||
case portInvalid
|
||||
case sendError(Int32)
|
||||
case msgError
|
||||
}
|
||||
|
||||
public typealias Callback = (_ msgId: Int32, _ msg: String) -> String?
|
||||
|
||||
class CallbackInfo {
|
||||
internal init(callback: @escaping MachMessenger.Callback) {
|
||||
self.callback = callback
|
||||
}
|
||||
|
||||
var callback: Callback
|
||||
}
|
||||
|
||||
public static func sendError(_ code: Int32) -> SendError? {
|
||||
switch code {
|
||||
case kCFMessagePortSuccess: return nil
|
||||
case kCFMessagePortSendTimeout: return .sndTimeout
|
||||
case kCFMessagePortReceiveTimeout: return .rcvTimeout
|
||||
case kCFMessagePortIsInvalid: return .portInvalid
|
||||
case kCFMessagePortBecameInvalidError: return .portInvalid
|
||||
default: return .sendError(code)
|
||||
}
|
||||
}
|
||||
|
||||
public func start() {
|
||||
logger.debug("MachMessenger.start")
|
||||
localPort = createLocalPort(localPortName, callback: callback)
|
||||
}
|
||||
|
||||
public func stop() {
|
||||
if let port = localPort {
|
||||
logger.debug("MachMessenger.stop")
|
||||
CFMessagePortInvalidate(port)
|
||||
localPort = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func sendMessage(_ remotePortName: CFString, msgId: Int32 = 0, msg: String) -> SendError? {
|
||||
logger.debug("MachMessenger.sendMessage")
|
||||
if let port = createRemotePort(remotePortName) {
|
||||
logger.debug("MachMessenger.sendMessage: sending...")
|
||||
return sendMessage(port, msgId: msgId, msg: msg)
|
||||
} else {
|
||||
logger.debug("MachMessenger.sendMessage: no remote port")
|
||||
return .portInvalid
|
||||
}
|
||||
}
|
||||
|
||||
public func sendMessageWithReply(_ remotePortName: CFString, msgId: Int32 = 0, msg: String) -> Result<String?, SendError> {
|
||||
logger.debug("MachMessenger.sendMessageWithReply")
|
||||
if let port = createRemotePort(remotePortName) {
|
||||
logger.debug("MachMessenger.sendMessageWithReply: sending...")
|
||||
return sendMessageWithReply(port, msgId: msgId, msg: msg)
|
||||
} else {
|
||||
logger.debug("MachMessenger.sendMessageWithReply: no remote port")
|
||||
return .failure(.portInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
private func createLocalPort(_ portName: CFString, callback: @escaping Callback) -> CFMessagePort? {
|
||||
logger.debug("MachMessenger.createLocalPort")
|
||||
if let port = localPort { return port }
|
||||
logger.debug("MachMessenger.createLocalPort: creating...")
|
||||
var context = CFMessagePortContext()
|
||||
context.version = 0
|
||||
context.info = Unmanaged.passRetained(CallbackInfo(callback: callback)).toOpaque()
|
||||
let callout: CFMessagePortCallBack = { port, msgId, msgData, info in
|
||||
logger.debug("MachMessenger CFMessagePortCallBack called")
|
||||
if let data = msgData,
|
||||
let msg = String(data: data as Data, encoding: .utf8),
|
||||
let info = info,
|
||||
let resp = Unmanaged<CallbackInfo>.fromOpaque(info).takeUnretainedValue().callback(msgId, msg),
|
||||
let respData = resp.data(using: .utf8) {
|
||||
return Unmanaged.passRetained(respData as CFData)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return withUnsafeMutablePointer(to: &context) { cxt in
|
||||
let port = CFMessagePortCreateLocal(kCFAllocatorDefault, portName, callout, cxt, nil)
|
||||
CFMessagePortSetDispatchQueue(port, DispatchQueue.main);
|
||||
localPort = port
|
||||
logger.debug("MachMessenger.createLocalPort created: \(portName)")
|
||||
return port
|
||||
}
|
||||
}
|
||||
|
||||
private func createRemotePort(_ portName: CFString) -> CFMessagePort? {
|
||||
CFMessagePortCreateRemote(kCFAllocatorDefault, portName)
|
||||
}
|
||||
|
||||
private func sendMessage(_ remotePort: CFMessagePort, msgId: Int32 = 0, msg: String) -> SendError? {
|
||||
if let data = msg.data(using: .utf8) {
|
||||
logger.debug("MachMessenger sendMessage")
|
||||
let msgData = data as CFData
|
||||
let code = CFMessagePortSendRequest(remotePort, msgId, msgData, MACH_SEND_TIMEOUT, 0, nil, nil)
|
||||
logger.debug("MachMessenger sendMessage \(code)")
|
||||
return MachMessenger.sendError(code)
|
||||
}
|
||||
return .msgError
|
||||
}
|
||||
|
||||
private func sendMessageWithReply(_ remotePort: CFMessagePort, msgId: Int32 = 0, msg: String) -> Result<String?, SendError> {
|
||||
if let data = msg.data(using: .utf8) {
|
||||
let msgData = data as CFData
|
||||
var respData: Unmanaged<CFData>? = nil
|
||||
let code = CFMessagePortSendRequest(remotePort, msgId, msgData, MACH_SEND_TIMEOUT, MACH_REPLY_TIMEOUT, CFRunLoopMode.defaultMode.rawValue, &respData)
|
||||
if let err = MachMessenger.sendError(code) {
|
||||
return .failure(err)
|
||||
} else if let data = respData?.takeUnretainedValue(),
|
||||
let resp = String(data: data as Data, encoding: .utf8) {
|
||||
return .success(resp)
|
||||
} else {
|
||||
return .success(nil)
|
||||
}
|
||||
|
||||
}
|
||||
return .failure(.msgError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,6 +264,9 @@ public struct Connection: Decodable {
|
||||
)
|
||||
}
|
||||
|
||||
public struct UserContact: Decodable {
|
||||
}
|
||||
|
||||
public struct UserContactRequest: Decodable, NamedChat {
|
||||
var contactRequestId: Int64
|
||||
var localDisplayName: ContactName
|
||||
@@ -437,6 +440,18 @@ public struct MemberSubError: Decodable {
|
||||
var memberError: ChatError
|
||||
}
|
||||
|
||||
public enum ConnectionEntity: Decodable {
|
||||
case rcvDirectMsgConnection(entityConnection: Connection, contact: Contact?)
|
||||
case rcvGroupMsgConnection(entityConnection: Connection, groupInfo: GroupInfo, groupMember: GroupMember)
|
||||
case sndFileConnection(entityConnection: Connection, sndFileTransfer: SndFileTransfer)
|
||||
case rcvFileConnection(entityConnection: Connection, rcvFileTransfer: RcvFileTransfer)
|
||||
case userContactConnection(entityConnection: Connection, userContact: UserContact)
|
||||
}
|
||||
|
||||
public struct NtfMsgInfo: Decodable {
|
||||
|
||||
}
|
||||
|
||||
public struct AChatItem: Decodable {
|
||||
public var chatInfo: ChatInfo
|
||||
public var chatItem: ChatItem
|
||||
@@ -901,6 +916,10 @@ public struct SndFileTransfer: Decodable {
|
||||
|
||||
}
|
||||
|
||||
public struct RcvFileTransfer: Decodable {
|
||||
|
||||
}
|
||||
|
||||
public struct FileTransferMeta: Decodable {
|
||||
|
||||
}
|
||||
|
||||
@@ -1096,6 +1096,7 @@ processAgentMessage Nothing _ _ = throwChatError CENoActiveUser
|
||||
processAgentMessage (Just User {userId}) "" agentMessage = case agentMessage of
|
||||
DOWN srv conns -> serverEvent srv conns CRContactsDisconnected "disconnected"
|
||||
UP srv conns -> serverEvent srv conns CRContactsSubscribed "connected"
|
||||
PHASE phase -> toView $ CRAppPhase phase
|
||||
_ -> pure ()
|
||||
where
|
||||
serverEvent srv@SMP.ProtocolServer {host, port} conns event str = do
|
||||
|
||||
@@ -33,7 +33,7 @@ import Simplex.Chat.Call
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Chat.Util (safeDecodeUtf8)
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Parsers (fromTextField_, sumTypeJSON)
|
||||
import Simplex.Messaging.Parsers (fromTextField_, fstToLower, sumTypeJSON)
|
||||
import Simplex.Messaging.Util (eitherToMaybe, (<$?>))
|
||||
|
||||
data ConnectionEntity
|
||||
@@ -45,8 +45,8 @@ data ConnectionEntity
|
||||
deriving (Eq, Show, Generic)
|
||||
|
||||
instance ToJSON ConnectionEntity where
|
||||
toJSON = J.genericToJSON $ sumTypeJSON id
|
||||
toEncoding = J.genericToEncoding $ sumTypeJSON id
|
||||
toJSON = J.genericToJSON $ sumTypeJSON fstToLower
|
||||
toEncoding = J.genericToEncoding $ sumTypeJSON fstToLower
|
||||
|
||||
updateEntityConnStatus :: ConnectionEntity -> ConnStatus -> ConnectionEntity
|
||||
updateEntityConnStatus connEntity connStatus = case connEntity of
|
||||
|
||||
Reference in New Issue
Block a user