mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a4f1c9c915 | |||
| b8141c3555 | |||
| 47214e33ea | |||
| 2e337a753f | |||
| 82dd5751c1 | |||
| cb49f6f01d | |||
| 05f57e98ad | |||
| 9530a6055a | |||
| dc8d10d068 | |||
| 951245d33f | |||
| 6311ba451b | |||
| 545f110fb8 | |||
| 4c6387c854 | |||
| 77ac972e09 | |||
| b3544cfbc3 | |||
| 0a4a3a24e1 | |||
| 3d48eded3d | |||
| 4a7ceb00fb | |||
| bfa8717ed4 | |||
| cdb3b6aafd | |||
| 047aad592e | |||
| 79e208193a | |||
| 5f1aa6fa9d | |||
| ef21fd1d26 | |||
| 8b3300e197 | |||
| f27de052cf | |||
| dc8ca4cf89 | |||
| b62dd801f1 | |||
| cc127e56fe | |||
| 831231d8e6 | |||
| f323c8e112 | |||
| d8373262bc | |||
| bd4259e89e | |||
| 5ef0eda2d7 | |||
| 45ada450a2 | |||
| 901610eec5 | |||
| 13215d91d7 | |||
| 5fcbade1bc | |||
| 80ddb50e1c | |||
| 0c23ff9ae3 | |||
| 1e2104cabf | |||
| f0991cc0ba | |||
| 82cd70a75c | |||
| c459e71d02 | |||
| 477d98d75a | |||
| ca78958667 | |||
| 8343285d93 | |||
| fb9485190d | |||
| 9ed723bafa | |||
| bb374c68b1 | |||
| 7c12e82042 | |||
| c4d7e5307c | |||
| 7fd3b4d6ba | |||
| 95008eeeaf | |||
| ea2b5f2ccf | |||
| 5c14c3b349 | |||
| 02db38ffd3 | |||
| c435cbdc7b | |||
| 41eb2e5689 | |||
| c60af078d7 | |||
| f66405e79b | |||
| e4fbe66d95 | |||
| 187fef0c5a | |||
| bcbee67709 | |||
| 2bd049db87 | |||
| 30db24265e | |||
| b4257f7767 | |||
| cf46469cd5 | |||
| 9defa44f0c | |||
| f81557b4fd | |||
| a63caf4640 | |||
| 340552321e |
@@ -9,7 +9,6 @@ on:
|
||||
tags:
|
||||
- "v*"
|
||||
- "!*-fdroid"
|
||||
- "!*-armv7a"
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -16,13 +16,13 @@ private var nseSubscribers: [UUID:NSESubscriber] = [:]
|
||||
private let SUSPENDING_TIMEOUT: TimeInterval = 2
|
||||
|
||||
// timeout should be larger than SUSPENDING_TIMEOUT
|
||||
func waitNSESuspended(timeout: TimeInterval, suspended: @escaping (Bool) -> Void) {
|
||||
func waitNSESuspended(timeout: TimeInterval, dispatchQueue: DispatchQueue = DispatchQueue.main, suspended: @escaping (Bool) -> Void) {
|
||||
if timeout <= SUSPENDING_TIMEOUT {
|
||||
logger.warning("waitNSESuspended: small timeout \(timeout), using \(SUSPENDING_TIMEOUT + 1)")
|
||||
}
|
||||
var state = nseStateGroupDefault.get()
|
||||
if case .suspended = state {
|
||||
DispatchQueue.main.async { suspended(true) }
|
||||
dispatchQueue.async { suspended(true) }
|
||||
return
|
||||
}
|
||||
let id = UUID()
|
||||
@@ -45,7 +45,7 @@ func waitNSESuspended(timeout: TimeInterval, suspended: @escaping (Bool) -> Void
|
||||
logger.debug("waitNSESuspended notifySuspended: calling suspended(\(ok))")
|
||||
suspendedCalled = true
|
||||
nseSubscribers.removeValue(forKey: id)
|
||||
DispatchQueue.main.async { suspended(ok) }
|
||||
dispatchQueue.async { suspended(ok) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ func apiDeleteUser(_ userId: Int64, _ delSMPQueues: Bool, viewPwd: String?) asyn
|
||||
}
|
||||
|
||||
func apiStartChat() throws -> Bool {
|
||||
let r = chatSendCmdSync(.startChat(mainApp: true))
|
||||
let r = chatSendCmdSync(.startChat(subscribe: true, expire: true, xftp: true))
|
||||
switch r {
|
||||
case .chatStarted: return true
|
||||
case .chatRunning: return false
|
||||
@@ -403,7 +403,7 @@ func apiGetNtfToken() -> (DeviceToken?, NtfTknStatus?, NotificationsMode) {
|
||||
case let .ntfToken(token, status, ntfMode): return (token, status, ntfMode)
|
||||
case .chatCmdError(_, .errorAgent(.CMD(.PROHIBITED))): return (nil, nil, .off)
|
||||
default:
|
||||
logger.debug("apiGetNtfToken response: \(String(describing: r))")
|
||||
logger.debug("apiGetNtfToken response: \(String(describing: r), privacy: .public)")
|
||||
return (nil, nil, .off)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,11 @@ let terminationTimeout: Int = 3 // seconds
|
||||
|
||||
let activationDelay: TimeInterval = 1.5
|
||||
|
||||
let nseSuspendTimeout: TimeInterval = 5
|
||||
|
||||
private func _suspendChat(timeout: Int) {
|
||||
// this is a redundant check to prevent logical errors, like the one fixed in this PR
|
||||
let state = AppChatState.shared.value
|
||||
if !state.canSuspend {
|
||||
logger.error("_suspendChat called, current state: \(state.rawValue)")
|
||||
logger.error("_suspendChat called, current state: \(state.rawValue, privacy: .public)")
|
||||
} else if ChatModel.ok {
|
||||
AppChatState.shared.set(.suspending)
|
||||
apiSuspendChat(timeoutMicroseconds: timeout * 1000000)
|
||||
@@ -136,33 +134,20 @@ func initChatAndMigrate(refreshInvitations: Bool = true) {
|
||||
}
|
||||
}
|
||||
|
||||
func startChatForCall() {
|
||||
logger.debug("DEBUGGING: startChatForCall")
|
||||
if ChatModel.shared.chatRunning == true {
|
||||
ChatReceiver.shared.start()
|
||||
logger.debug("DEBUGGING: startChatForCall: after ChatReceiver.shared.start")
|
||||
}
|
||||
if .active != AppChatState.shared.value {
|
||||
logger.debug("DEBUGGING: startChatForCall: before activateChat")
|
||||
activateChat()
|
||||
logger.debug("DEBUGGING: startChatForCall: after activateChat")
|
||||
}
|
||||
}
|
||||
|
||||
func startChatAndActivate(_ completion: @escaping () -> Void) {
|
||||
func startChatAndActivate(dispatchQueue: DispatchQueue = DispatchQueue.main, _ completion: @escaping () -> Void) {
|
||||
logger.debug("DEBUGGING: startChatAndActivate")
|
||||
if ChatModel.shared.chatRunning == true {
|
||||
ChatReceiver.shared.start()
|
||||
logger.debug("DEBUGGING: startChatAndActivate: after ChatReceiver.shared.start")
|
||||
}
|
||||
if case .active = AppChatState.shared.value {
|
||||
if .active == AppChatState.shared.value {
|
||||
completion()
|
||||
} else if nseStateGroupDefault.get().inactive {
|
||||
activate()
|
||||
} else {
|
||||
// setting app state to "activating" to notify NSE that it should suspend
|
||||
setAppState(.activating)
|
||||
waitNSESuspended(timeout: nseSuspendTimeout) { ok in
|
||||
waitNSESuspended(timeout: 10, dispatchQueue: dispatchQueue) { ok in
|
||||
if !ok {
|
||||
// if for some reason NSE failed to suspend,
|
||||
// e.g., it crashed previously without setting its state to "suspended",
|
||||
|
||||
@@ -98,12 +98,12 @@ struct SimpleXApp: App {
|
||||
if legacyDatabase, case .documents = dbContainerGroupDefault.get() {
|
||||
dbContainerGroupDefault.set(.documents)
|
||||
setMigrationState(.offer)
|
||||
logger.debug("SimpleXApp init: using legacy DB in documents folder: \(getAppDatabasePath())*.db")
|
||||
logger.debug("SimpleXApp init: using legacy DB in documents folder: \(getAppDatabasePath(), privacy: .public)*.db")
|
||||
} else {
|
||||
dbContainerGroupDefault.set(.group)
|
||||
setMigrationState(.ready)
|
||||
logger.debug("SimpleXApp init: using DB in app group container: \(getAppDatabasePath())*.db")
|
||||
logger.debug("SimpleXApp init: legacy DB\(legacyDatabase ? "" : " not") present")
|
||||
logger.debug("SimpleXApp init: using DB in app group container: \(getAppDatabasePath(), privacy: .public)*.db")
|
||||
logger.debug("SimpleXApp init: legacy DB\(legacyDatabase ? "" : " not", privacy: .public) present")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,13 +38,13 @@ struct ActiveCallView: View {
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
logger.debug("ActiveCallView: appear client is nil \(client == nil), scenePhase \(String(describing: scenePhase)), canConnectCall \(canConnectCall)")
|
||||
logger.debug("ActiveCallView: appear client is nil \(client == nil), scenePhase \(String(describing: scenePhase), privacy: .public), canConnectCall \(canConnectCall)")
|
||||
AppDelegate.keepScreenOn(true)
|
||||
createWebRTCClient()
|
||||
dismissAllSheets()
|
||||
}
|
||||
.onChange(of: canConnectCall) { _ in
|
||||
logger.debug("ActiveCallView: canConnectCall changed to \(canConnectCall)")
|
||||
logger.debug("ActiveCallView: canConnectCall changed to \(canConnectCall, privacy: .public)")
|
||||
createWebRTCClient()
|
||||
}
|
||||
.onDisappear {
|
||||
|
||||
@@ -130,7 +130,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
||||
// The delay allows to accept the second call before suspending a chat
|
||||
// see `.onChange(of: scenePhase)` in SimpleXApp
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
|
||||
logger.debug("CallController: shouldSuspendChat \(String(describing: self?.shouldSuspendChat))")
|
||||
logger.debug("CallController: shouldSuspendChat \(String(describing: self?.shouldSuspendChat), privacy: .public)")
|
||||
if ChatModel.shared.activeCall == nil && self?.shouldSuspendChat == true {
|
||||
self?.shouldSuspendChat = false
|
||||
suspendChat()
|
||||
@@ -142,57 +142,45 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
||||
|
||||
@objc(pushRegistry:didUpdatePushCredentials:forType:)
|
||||
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
|
||||
logger.debug("CallController: didUpdate push credentials for type \(type.rawValue)")
|
||||
logger.debug("CallController: didUpdate push credentials for type \(type.rawValue, privacy: .public)")
|
||||
}
|
||||
|
||||
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
|
||||
logger.debug("CallController: did receive push with type \(type.rawValue)")
|
||||
logger.debug("CallController: did receive push with type \(type.rawValue, privacy: .public)")
|
||||
if type != .voIP {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
if AppChatState.shared.value == .stopped {
|
||||
self.reportExpiredCall(payload: payload, completion)
|
||||
return
|
||||
}
|
||||
logger.debug("CallController: initializing chat")
|
||||
if (!ChatModel.shared.chatInitialized) {
|
||||
logger.debug("CallController: initializing chat")
|
||||
do {
|
||||
try initializeChat(start: true, refreshInvitations: false)
|
||||
} catch let error {
|
||||
logger.error("CallController: initializing chat error: \(error)")
|
||||
self.reportExpiredCall(payload: payload, completion)
|
||||
return
|
||||
}
|
||||
initChatAndMigrate(refreshInvitations: false)
|
||||
}
|
||||
logger.debug("CallController: initialized chat")
|
||||
startChatForCall()
|
||||
logger.debug("CallController: started chat")
|
||||
self.shouldSuspendChat = true
|
||||
// There are no invitations in the model, as it was processed by NSE
|
||||
_ = try? justRefreshCallInvitations()
|
||||
logger.debug("CallController: updated call invitations chat")
|
||||
// logger.debug("CallController justRefreshCallInvitations: \(String(describing: m.callInvitations))")
|
||||
// Extract the call information from the push notification payload
|
||||
let m = ChatModel.shared
|
||||
if let contactId = payload.dictionaryPayload["contactId"] as? String,
|
||||
let invitation = m.callInvitations[contactId] {
|
||||
let update = self.cxCallUpdate(invitation: invitation)
|
||||
if let uuid = invitation.callkitUUID {
|
||||
logger.debug("CallController: report pushkit call via CallKit")
|
||||
startChatAndActivate(dispatchQueue: DispatchQueue.global()) {
|
||||
self.shouldSuspendChat = true
|
||||
// There are no invitations in the model, as it was processed by NSE
|
||||
_ = try? justRefreshCallInvitations()
|
||||
// logger.debug("CallController justRefreshCallInvitations: \(String(describing: m.callInvitations))")
|
||||
// Extract the call information from the push notification payload
|
||||
let m = ChatModel.shared
|
||||
if let contactId = payload.dictionaryPayload["contactId"] as? String,
|
||||
let invitation = m.callInvitations[contactId] {
|
||||
let update = self.cxCallUpdate(invitation: invitation)
|
||||
self.provider.reportNewIncomingCall(with: uuid, update: update) { error in
|
||||
if error != nil {
|
||||
m.callInvitations.removeValue(forKey: contactId)
|
||||
if let uuid = invitation.callkitUUID {
|
||||
logger.debug("CallController: report pushkit call via CallKit")
|
||||
let update = self.cxCallUpdate(invitation: invitation)
|
||||
self.provider.reportNewIncomingCall(with: uuid, update: update) { error in
|
||||
if error != nil {
|
||||
m.callInvitations.removeValue(forKey: contactId)
|
||||
}
|
||||
// Tell PushKit that the notification is handled.
|
||||
completion()
|
||||
}
|
||||
// Tell PushKit that the notification is handled.
|
||||
completion()
|
||||
} else {
|
||||
self.reportExpiredCall(update: update, completion)
|
||||
}
|
||||
} else {
|
||||
self.reportExpiredCall(update: update, completion)
|
||||
self.reportExpiredCall(payload: payload, completion)
|
||||
}
|
||||
} else {
|
||||
self.reportExpiredCall(payload: payload, completion)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +211,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
||||
}
|
||||
|
||||
func reportNewIncomingCall(invitation: RcvCallInvitation, completion: @escaping (Error?) -> Void) {
|
||||
logger.debug("CallController.reportNewIncomingCall, UUID=\(String(describing: invitation.callkitUUID))")
|
||||
logger.debug("CallController.reportNewIncomingCall, UUID=\(String(describing: invitation.callkitUUID), privacy: .public)")
|
||||
if CallController.useCallKit(), let uuid = invitation.callkitUUID {
|
||||
if invitation.callTs.timeIntervalSinceNow >= -180 {
|
||||
let update = cxCallUpdate(invitation: invitation)
|
||||
@@ -363,7 +351,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
||||
private func requestTransaction(with action: CXAction, onSuccess: @escaping () -> Void = {}) {
|
||||
controller.request(CXTransaction(action: action)) { error in
|
||||
if let error = error {
|
||||
logger.error("CallController.requestTransaction error requesting transaction: \(error.localizedDescription)")
|
||||
logger.error("CallController.requestTransaction error requesting transaction: \(error.localizedDescription, privacy: .public)")
|
||||
} else {
|
||||
logger.debug("CallController.requestTransaction requested transaction successfully")
|
||||
onSuccess()
|
||||
|
||||
@@ -16,11 +16,9 @@ let logger = Logger()
|
||||
|
||||
let appSuspendingDelay: UInt64 = 2_500_000_000
|
||||
|
||||
typealias SuspendSchedule = (delay: TimeInterval, timeout: Int)
|
||||
let nseSuspendDelay: TimeInterval = 2
|
||||
|
||||
let nseSuspendSchedule: SuspendSchedule = (2, 4)
|
||||
|
||||
let fastNSESuspendSchedule: SuspendSchedule = (1, 1)
|
||||
let nseSuspendTimeout: Int = 5
|
||||
|
||||
typealias NtfStream = ConcurrentQueue<NSENotification>
|
||||
|
||||
@@ -34,7 +32,7 @@ actor PendingNtfs {
|
||||
private var ntfStreams: [String: NtfStream] = [:]
|
||||
|
||||
func createStream(_ id: String) async {
|
||||
logger.debug("NotificationService PendingNtfs.createStream: \(id)")
|
||||
logger.debug("NotificationService PendingNtfs.createStream: \(id, privacy: .public)")
|
||||
if ntfStreams[id] == nil {
|
||||
ntfStreams[id] = ConcurrentQueue()
|
||||
logger.debug("NotificationService PendingNtfs.createStream: created ConcurrentQueue")
|
||||
@@ -42,14 +40,14 @@ actor PendingNtfs {
|
||||
}
|
||||
|
||||
func readStream(_ id: String, for nse: NotificationService, ntfInfo: NtfMessages) async {
|
||||
logger.debug("NotificationService PendingNtfs.readStream: \(id) \(ntfInfo.ntfMessages.count)")
|
||||
logger.debug("NotificationService PendingNtfs.readStream: \(id, privacy: .public) \(ntfInfo.ntfMessages.count, privacy: .public)")
|
||||
if !ntfInfo.user.showNotifications {
|
||||
nse.setBestAttemptNtf(.empty)
|
||||
}
|
||||
if let s = ntfStreams[id] {
|
||||
logger.debug("NotificationService PendingNtfs.readStream: has stream")
|
||||
var expected = Set(ntfInfo.ntfMessages.map { $0.msgId })
|
||||
logger.debug("NotificationService PendingNtfs.readStream: expecting: \(expected)")
|
||||
logger.debug("NotificationService PendingNtfs.readStream: expecting: \(expected, privacy: .public)")
|
||||
var readCancelled = false
|
||||
var dequeued: DequeueElement<NSENotification>?
|
||||
nse.cancelRead = {
|
||||
@@ -68,7 +66,7 @@ actor PendingNtfs {
|
||||
} else if case let .msgInfo(info) = ntf {
|
||||
let found = expected.remove(info.msgId)
|
||||
if found != nil {
|
||||
logger.debug("NotificationService PendingNtfs.readStream: msgInfo, last: \(expected.isEmpty)")
|
||||
logger.debug("NotificationService PendingNtfs.readStream: msgInfo, last: \(expected.isEmpty, privacy: .public)")
|
||||
if expected.isEmpty { break }
|
||||
} else if let msgTs = ntfInfo.msgTs, info.msgTs > msgTs {
|
||||
logger.debug("NotificationService PendingNtfs.readStream: unexpected msgInfo")
|
||||
@@ -90,7 +88,7 @@ actor PendingNtfs {
|
||||
}
|
||||
|
||||
func writeStream(_ id: String, _ ntf: NSENotification) async {
|
||||
logger.debug("NotificationService PendingNtfs.writeStream: \(id)")
|
||||
logger.debug("NotificationService PendingNtfs.writeStream: \(id, privacy: .public)")
|
||||
if let s = ntfStreams[id] {
|
||||
logger.debug("NotificationService PendingNtfs.writeStream: writing ntf")
|
||||
s.enqueue(ntf)
|
||||
@@ -210,7 +208,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
self.contentHandler = contentHandler
|
||||
registerGroupDefaults()
|
||||
let appState = appStateGroupDefault.get()
|
||||
logger.debug("NotificationService: app is \(appState.rawValue)")
|
||||
logger.debug("NotificationService: app is \(appState.rawValue, privacy: .public)")
|
||||
switch appState {
|
||||
case .stopped:
|
||||
setBadgeCount()
|
||||
@@ -240,7 +238,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.debug("NotificationService: app state is now \(state.rawValue)")
|
||||
logger.debug("NotificationService: app state is now \(state.rawValue, privacy: .public)")
|
||||
if state.inactive {
|
||||
receiveNtfMessages(request, contentHandler)
|
||||
} else {
|
||||
@@ -269,7 +267,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
let dbStatus = startChat()
|
||||
if case .ok = dbStatus,
|
||||
let ntfInfo = apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo) {
|
||||
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.ntfMessages.count))")
|
||||
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.ntfMessages.count), privacy: .public)")
|
||||
if let connEntity = ntfInfo.connEntity_ {
|
||||
setBestAttemptNtf(
|
||||
ntfInfo.ntfsEnabled
|
||||
@@ -281,7 +279,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
NtfStreamSemaphores.shared.waitForStream(id)
|
||||
if receiveEntityId != nil {
|
||||
Task {
|
||||
logger.debug("NotificationService: receiveNtfMessages: in Task, connEntity id \(id)")
|
||||
logger.debug("NotificationService: receiveNtfMessages: in Task, connEntity id \(id, privacy: .public)")
|
||||
await PendingNtfs.shared.createStream(id)
|
||||
await PendingNtfs.shared.readStream(id, for: self, ntfInfo: ntfInfo)
|
||||
deliverBestAttemptNtf()
|
||||
@@ -299,7 +297,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
override func serviceExtensionTimeWillExpire() {
|
||||
logger.debug("DEBUGGING: NotificationService.serviceExtensionTimeWillExpire")
|
||||
deliverBestAttemptNtf(urgent: true)
|
||||
deliverBestAttemptNtf()
|
||||
}
|
||||
|
||||
func setBadgeCount() {
|
||||
@@ -321,7 +319,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
}
|
||||
|
||||
private func deliverBestAttemptNtf(urgent: Bool = false) {
|
||||
private func deliverBestAttemptNtf() {
|
||||
logger.debug("NotificationService.deliverBestAttemptNtf")
|
||||
if let cancel = cancelRead {
|
||||
cancelRead = nil
|
||||
@@ -331,55 +329,20 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
receiveEntityId = nil
|
||||
NtfStreamSemaphores.shared.signalStreamReady(id)
|
||||
}
|
||||
let suspend: Bool
|
||||
if let t = threadId {
|
||||
threadId = nil
|
||||
suspend = NSEThreads.shared.endThread(t) && NSEThreads.shared.noThreads
|
||||
} else {
|
||||
suspend = false
|
||||
}
|
||||
deliverCallkitOrNotification(urgent: urgent, suspend: suspend)
|
||||
}
|
||||
|
||||
private func deliverCallkitOrNotification(urgent: Bool, suspend: Bool = false) {
|
||||
if case .callkit = bestAttemptNtf {
|
||||
logger.debug("NotificationService.deliverCallkitOrNotification: will suspend, callkit")
|
||||
if urgent {
|
||||
// suspending NSE even though there may be other notifications
|
||||
// to allow the app to process callkit call
|
||||
suspendChat(0)
|
||||
deliverNotification()
|
||||
} else {
|
||||
// suspending NSE with delay and delivering after the suspension
|
||||
// because pushkit notification must be processed without delay
|
||||
// to avoid app termination
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + fastNSESuspendSchedule.delay) {
|
||||
suspendChat(fastNSESuspendSchedule.timeout)
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + Double(fastNSESuspendSchedule.timeout)) {
|
||||
self.deliverNotification()
|
||||
if NSEThreads.shared.endThread(t) {
|
||||
logger.debug("NotificationService.deliverBestAttemptNtf: will suspend")
|
||||
// suspension is delayed to allow chat core finalise any processing
|
||||
// (e.g., send delivery receipts)
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + nseSuspendDelay) {
|
||||
if NSEThreads.shared.noThreads {
|
||||
logger.debug("NotificationService.deliverBestAttemptNtf: suspending...")
|
||||
suspendChat(nseSuspendTimeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if suspend {
|
||||
logger.debug("NotificationService.deliverCallkitOrNotification: will suspend")
|
||||
if urgent {
|
||||
suspendChat(0)
|
||||
} else {
|
||||
// suspension is delayed to allow chat core finalise any processing
|
||||
// (e.g., send delivery receipts)
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + nseSuspendSchedule.delay) {
|
||||
if NSEThreads.shared.noThreads {
|
||||
suspendChat(nseSuspendSchedule.timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
deliverNotification()
|
||||
}
|
||||
}
|
||||
|
||||
private func deliverNotification() {
|
||||
if let handler = contentHandler, let ntf = bestAttemptNtf {
|
||||
contentHandler = nil
|
||||
bestAttemptNtf = nil
|
||||
@@ -394,14 +357,17 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
switch ntf {
|
||||
case let .nse(content): deliver(content)
|
||||
case let .callkit(invitation):
|
||||
logger.debug("NotificationService reportNewIncomingVoIPPushPayload for \(invitation.contact.id)")
|
||||
CXProvider.reportNewIncomingVoIPPushPayload([
|
||||
"displayName": invitation.contact.displayName,
|
||||
"contactId": invitation.contact.id,
|
||||
"media": invitation.callType.media.rawValue
|
||||
]) { error in
|
||||
logger.debug("reportNewIncomingVoIPPushPayload result: \(error)")
|
||||
deliver(error == nil ? nil : createCallInvitationNtf(invitation))
|
||||
if error == nil {
|
||||
deliver(nil)
|
||||
} else {
|
||||
logger.debug("NotificationService reportNewIncomingVoIPPushPayload success to CallController for \(invitation.contact.id)")
|
||||
deliver(createCallInvitationNtf(invitation))
|
||||
}
|
||||
}
|
||||
case .empty: deliver(nil) // used to mute notifications that did not unsubscribe yet
|
||||
case .msgInfo: deliver(nil) // unreachable, the best attempt is never set to msgInfo
|
||||
@@ -436,14 +402,14 @@ var appSubscriber: AppSubscriber = appStateSubscriber { state in
|
||||
logger.debug("NotificationService: appSubscriber")
|
||||
if state.running && NSEChatState.shared.value.canSuspend {
|
||||
logger.debug("NotificationService: appSubscriber app state \(state.rawValue), suspending")
|
||||
suspendChat(fastNSESuspendSchedule.timeout)
|
||||
suspendChat(nseSuspendTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
func appStateSubscriber(onState: @escaping (AppState) -> Void) -> AppSubscriber {
|
||||
appMessageSubscriber { msg in
|
||||
if case let .state(state) = msg {
|
||||
logger.debug("NotificationService: appStateSubscriber \(state.rawValue)")
|
||||
logger.debug("NotificationService: appStateSubscriber \(state.rawValue, privacy: .public)")
|
||||
onState(state)
|
||||
}
|
||||
}
|
||||
@@ -459,33 +425,24 @@ let xftpConfig: XFTPFileConfig? = getXFTPCfg()
|
||||
// Subsequent calls to didReceive will be waiting on semaphore and won't start chat again, as it will be .active
|
||||
func startChat() -> DBMigrationResult? {
|
||||
logger.debug("NotificationService: startChat")
|
||||
// only skip creating if there is chat controller
|
||||
if case .active = NSEChatState.shared.value, hasChatCtrl() { return .ok }
|
||||
if case .active = NSEChatState.shared.value { return .ok }
|
||||
|
||||
startLock.wait()
|
||||
defer { startLock.signal() }
|
||||
|
||||
if hasChatCtrl() {
|
||||
return switch NSEChatState.shared.value {
|
||||
case .created: doStartChat()
|
||||
case .starting: .ok // it should never get to this branch, as it would be waiting for start on startLock
|
||||
case .active: .ok
|
||||
case .suspending: activateChat()
|
||||
case .suspended: activateChat()
|
||||
}
|
||||
} else {
|
||||
// Ignore state in preference if there is no chat controller.
|
||||
// State in preference may have failed to update e.g. because of a crash.
|
||||
NSEChatState.shared.set(.created)
|
||||
return doStartChat()
|
||||
return switch NSEChatState.shared.value {
|
||||
case .created: doStartChat()
|
||||
case .starting: .ok // it should never get to this branch, as it would be waiting for start on startLock
|
||||
case .active: .ok
|
||||
case .suspending: activateChat()
|
||||
case .suspended: activateChat()
|
||||
}
|
||||
}
|
||||
|
||||
func doStartChat() -> DBMigrationResult? {
|
||||
logger.debug("NotificationService: doStartChat")
|
||||
haskell_init_nse()
|
||||
hs_init(0, nil)
|
||||
let (_, dbStatus) = chatMigrateInit(confirmMigrations: defaultMigrationConfirmation(), backgroundMode: true)
|
||||
logger.debug("NotificationService: doStartChat \(String(describing: dbStatus))")
|
||||
if dbStatus != .ok {
|
||||
resetChatCtrl()
|
||||
NSEChatState.shared.set(.created)
|
||||
@@ -520,7 +477,7 @@ func doStartChat() -> DBMigrationResult? {
|
||||
return .ok
|
||||
}
|
||||
} catch {
|
||||
logger.error("NotificationService startChat error: \(responseError(error))")
|
||||
logger.error("NotificationService startChat error: \(responseError(error), privacy: .public)")
|
||||
}
|
||||
} else {
|
||||
logger.debug("NotificationService: no active user")
|
||||
@@ -547,10 +504,8 @@ func suspendChat(_ timeout: Int) {
|
||||
logger.debug("NotificationService: suspendChat")
|
||||
let state = NSEChatState.shared.value
|
||||
if !state.canSuspend {
|
||||
logger.error("NotificationService suspendChat called, current state: \(state.rawValue)")
|
||||
} else if hasChatCtrl() {
|
||||
// only suspend if we have chat controller to avoid crashes when suspension is
|
||||
// attempted when chat controller was not created
|
||||
logger.error("NotificationService suspendChat called, current state: \(state.rawValue, privacy: .public)")
|
||||
} else {
|
||||
suspendLock.wait()
|
||||
defer { suspendLock.signal() }
|
||||
|
||||
@@ -616,7 +571,7 @@ private let isInChina = SKStorefront().countryCode == "CHN"
|
||||
private func useCallKit() -> Bool { !isInChina && callKitEnabledGroupDefault.get() }
|
||||
|
||||
func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
|
||||
logger.debug("NotificationService receivedMsgNtf: \(res.responseType)")
|
||||
logger.debug("NotificationService receivedMsgNtf: \(res.responseType, privacy: .public)")
|
||||
switch res {
|
||||
case let .contactConnected(user, contact, _):
|
||||
return (contact.id, .nse(createContactConnectedNtf(user, contact)))
|
||||
@@ -658,9 +613,6 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
|
||||
case .chatSuspended:
|
||||
chatSuspended()
|
||||
return nil
|
||||
case let .chatError(_, err):
|
||||
logger.error("NotificationService receivedMsgNtf error: \(String(describing: err))")
|
||||
return nil
|
||||
default:
|
||||
logger.debug("NotificationService receivedMsgNtf ignored event: \(res.responseType)")
|
||||
return nil
|
||||
@@ -675,22 +627,17 @@ func updateNetCfg() {
|
||||
try setNetworkConfig(networkConfig)
|
||||
networkConfig = newNetConfig
|
||||
} catch {
|
||||
logger.error("NotificationService apply changed network config error: \(responseError(error))")
|
||||
logger.error("NotificationService apply changed network config error: \(responseError(error), privacy: .public)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func apiGetActiveUser() -> User? {
|
||||
let r = sendSimpleXCmd(.showActiveUser)
|
||||
logger.debug("apiGetActiveUser sendSimpleXCmd response: \(r.responseType)")
|
||||
logger.debug("apiGetActiveUser sendSimpleXCmd response: \(String(describing: r))")
|
||||
switch r {
|
||||
case let .activeUser(user): return user
|
||||
case .chatCmdError(_, .error(.noActiveUser)):
|
||||
logger.debug("apiGetActiveUser sendSimpleXCmd no active user")
|
||||
return nil
|
||||
case let .chatCmdError(_, err):
|
||||
logger.debug("apiGetActiveUser sendSimpleXCmd error: \(String(describing: err))")
|
||||
return nil
|
||||
case .chatCmdError(_, .error(.noActiveUser)): return nil
|
||||
default:
|
||||
logger.error("NotificationService apiGetActiveUser unexpected response: \(String(describing: r))")
|
||||
return nil
|
||||
@@ -698,7 +645,7 @@ func apiGetActiveUser() -> User? {
|
||||
}
|
||||
|
||||
func apiStartChat() throws -> Bool {
|
||||
let r = sendSimpleXCmd(.startChat(mainApp: false))
|
||||
let r = sendSimpleXCmd(.startChat(subscribe: false, expire: false, xftp: false))
|
||||
switch r {
|
||||
case .chatStarted: return true
|
||||
case .chatRunning: return false
|
||||
@@ -752,12 +699,11 @@ func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
|
||||
}
|
||||
let r = sendSimpleXCmd(.apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo))
|
||||
if case let .ntfMessages(user, connEntity_, msgTs, ntfMessages) = r, let user = user {
|
||||
logger.debug("apiGetNtfMessage response ntfMessages: \(ntfMessages.count)")
|
||||
return NtfMessages(user: user, connEntity_: connEntity_, msgTs: msgTs, ntfMessages: ntfMessages)
|
||||
} else if case let .chatCmdError(_, error) = r {
|
||||
logger.debug("apiGetNtfMessage error response: \(String.init(describing: error))")
|
||||
} else {
|
||||
logger.debug("apiGetNtfMessage ignored response: \(r.responseType) \(String.init(describing: r))")
|
||||
logger.debug("apiGetNtfMessage ignored response: \(r.responseType, privacy: .public) \(String.init(describing: r), privacy: .private)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -30,11 +30,6 @@
|
||||
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
|
||||
5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; };
|
||||
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
|
||||
5C245F232B4EAA5E001CC39F /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F1E2B4EAA5E001CC39F /* libgmpxx.a */; };
|
||||
5C245F242B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F1F2B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH-ghc9.6.3.a */; };
|
||||
5C245F252B4EAA5E001CC39F /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F202B4EAA5E001CC39F /* libgmp.a */; };
|
||||
5C245F262B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F212B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH.a */; };
|
||||
5C245F272B4EAA5E001CC39F /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F222B4EAA5E001CC39F /* libffi.a */; };
|
||||
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; };
|
||||
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; };
|
||||
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; };
|
||||
@@ -48,6 +43,11 @@
|
||||
5C3F1D562842B68D00EC8A82 /* IntegrityErrorItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3F1D552842B68D00EC8A82 /* IntegrityErrorItemView.swift */; };
|
||||
5C3F1D58284363C400EC8A82 /* PrivacySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3F1D57284363C400EC8A82 /* PrivacySettings.swift */; };
|
||||
5C4B3B0A285FB130003915F2 /* DatabaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C4B3B09285FB130003915F2 /* DatabaseView.swift */; };
|
||||
5C4E80DA2B3CCD090080FAE2 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C4E80D52B3CCD090080FAE2 /* libgmp.a */; };
|
||||
5C4E80DB2B3CCD090080FAE2 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C4E80D62B3CCD090080FAE2 /* libffi.a */; };
|
||||
5C4E80DC2B3CCD090080FAE2 /* libHSsimplex-chat-5.4.2.1-FP1oxJSttEYhorN1FRfI5.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C4E80D72B3CCD090080FAE2 /* libHSsimplex-chat-5.4.2.1-FP1oxJSttEYhorN1FRfI5.a */; };
|
||||
5C4E80DD2B3CCD090080FAE2 /* libHSsimplex-chat-5.4.2.1-FP1oxJSttEYhorN1FRfI5-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C4E80D82B3CCD090080FAE2 /* libHSsimplex-chat-5.4.2.1-FP1oxJSttEYhorN1FRfI5-ghc9.6.3.a */; };
|
||||
5C4E80DE2B3CCD090080FAE2 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C4E80D92B3CCD090080FAE2 /* libgmpxx.a */; };
|
||||
5C5346A827B59A6A004DF848 /* ChatHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5346A727B59A6A004DF848 /* ChatHelp.swift */; };
|
||||
5C55A91F283AD0E400C4E99E /* CallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C55A91E283AD0E400C4E99E /* CallManager.swift */; };
|
||||
5C55A921283CCCB700C4E99E /* IncomingCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C55A920283CCCB700C4E99E /* IncomingCallView.swift */; };
|
||||
@@ -285,11 +285,6 @@
|
||||
5C13730A28156D2700F43030 /* ContactConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionView.swift; sourceTree = "<group>"; };
|
||||
5C13730C2815740A00F43030 /* DebugJSON.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = DebugJSON.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = "<group>"; };
|
||||
5C245F1E2B4EAA5E001CC39F /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C245F1F2B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
5C245F202B4EAA5E001CC39F /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C245F212B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH.a"; sourceTree = "<group>"; };
|
||||
5C245F222B4EAA5E001CC39F /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C2E260627A2941F00F70299 /* SimpleXAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleXAPI.swift; sourceTree = "<group>"; };
|
||||
5C2E260A27A30CFA00F70299 /* ChatListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListView.swift; sourceTree = "<group>"; };
|
||||
5C2E260E27A30FDC00F70299 /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; };
|
||||
@@ -304,6 +299,11 @@
|
||||
5C3F1D57284363C400EC8A82 /* PrivacySettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacySettings.swift; sourceTree = "<group>"; };
|
||||
5C422A7C27A9A6FA0097A1E1 /* SimpleX (iOS).entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "SimpleX (iOS).entitlements"; sourceTree = "<group>"; };
|
||||
5C4B3B09285FB130003915F2 /* DatabaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseView.swift; sourceTree = "<group>"; };
|
||||
5C4E80D52B3CCD090080FAE2 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C4E80D62B3CCD090080FAE2 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C4E80D72B3CCD090080FAE2 /* libHSsimplex-chat-5.4.2.1-FP1oxJSttEYhorN1FRfI5.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.2.1-FP1oxJSttEYhorN1FRfI5.a"; sourceTree = "<group>"; };
|
||||
5C4E80D82B3CCD090080FAE2 /* libHSsimplex-chat-5.4.2.1-FP1oxJSttEYhorN1FRfI5-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.2.1-FP1oxJSttEYhorN1FRfI5-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
5C4E80D92B3CCD090080FAE2 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C5346A727B59A6A004DF848 /* ChatHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatHelp.swift; sourceTree = "<group>"; };
|
||||
5C55A91E283AD0E400C4E99E /* CallManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallManager.swift; sourceTree = "<group>"; };
|
||||
5C55A920283CCCB700C4E99E /* IncomingCallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncomingCallView.swift; sourceTree = "<group>"; };
|
||||
@@ -529,13 +529,13 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5C245F232B4EAA5E001CC39F /* libgmpxx.a in Frameworks */,
|
||||
5C245F262B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH.a in Frameworks */,
|
||||
5C245F252B4EAA5E001CC39F /* libgmp.a in Frameworks */,
|
||||
5C4E80DD2B3CCD090080FAE2 /* libHSsimplex-chat-5.4.2.1-FP1oxJSttEYhorN1FRfI5-ghc9.6.3.a in Frameworks */,
|
||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||
5C245F272B4EAA5E001CC39F /* libffi.a in Frameworks */,
|
||||
5C4E80DA2B3CCD090080FAE2 /* libgmp.a in Frameworks */,
|
||||
5C4E80DC2B3CCD090080FAE2 /* libHSsimplex-chat-5.4.2.1-FP1oxJSttEYhorN1FRfI5.a in Frameworks */,
|
||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||
5C245F242B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH-ghc9.6.3.a in Frameworks */,
|
||||
5C4E80DB2B3CCD090080FAE2 /* libffi.a in Frameworks */,
|
||||
5C4E80DE2B3CCD090080FAE2 /* libgmpxx.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -597,11 +597,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5C245F222B4EAA5E001CC39F /* libffi.a */,
|
||||
5C245F202B4EAA5E001CC39F /* libgmp.a */,
|
||||
5C245F1E2B4EAA5E001CC39F /* libgmpxx.a */,
|
||||
5C245F1F2B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH-ghc9.6.3.a */,
|
||||
5C245F212B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH.a */,
|
||||
5C4E80D62B3CCD090080FAE2 /* libffi.a */,
|
||||
5C4E80D52B3CCD090080FAE2 /* libgmp.a */,
|
||||
5C4E80D92B3CCD090080FAE2 /* libgmpxx.a */,
|
||||
5C4E80D82B3CCD090080FAE2 /* libHSsimplex-chat-5.4.2.1-FP1oxJSttEYhorN1FRfI5-ghc9.6.3.a */,
|
||||
5C4E80D72B3CCD090080FAE2 /* libHSsimplex-chat-5.4.2.1-FP1oxJSttEYhorN1FRfI5.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
@@ -1528,7 +1528,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 188;
|
||||
CURRENT_PROJECT_VERSION = 186;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1550,7 +1550,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.4.3;
|
||||
MARKETING_VERSION = 5.4.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1571,7 +1571,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 188;
|
||||
CURRENT_PROJECT_VERSION = 186;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1593,7 +1593,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.4.3;
|
||||
MARKETING_VERSION = 5.4.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1652,7 +1652,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 188;
|
||||
CURRENT_PROJECT_VERSION = 186;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1665,7 +1665,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.4.3;
|
||||
MARKETING_VERSION = 5.4.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -1684,7 +1684,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 188;
|
||||
CURRENT_PROJECT_VERSION = 186;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1697,7 +1697,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.4.3;
|
||||
MARKETING_VERSION = 5.4.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -1716,7 +1716,7 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 188;
|
||||
CURRENT_PROJECT_VERSION = 186;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@@ -1740,7 +1740,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Libraries/sim",
|
||||
);
|
||||
MARKETING_VERSION = 5.4.3;
|
||||
MARKETING_VERSION = 5.4.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1762,7 +1762,7 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 188;
|
||||
CURRENT_PROJECT_VERSION = 186;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@@ -1786,7 +1786,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Libraries/sim",
|
||||
);
|
||||
MARKETING_VERSION = 5.4.3;
|
||||
MARKETING_VERSION = 5.4.2;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SDKROOT = iphoneos;
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1400"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "1.3">
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@@ -47,14 +47,16 @@
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
|
||||
@@ -12,11 +12,7 @@ private var chatController: chat_ctrl?
|
||||
|
||||
private var migrationResult: (Bool, DBMigrationResult)?
|
||||
|
||||
public func hasChatCtrl() -> Bool {
|
||||
chatController != nil
|
||||
}
|
||||
|
||||
public func getChatCtrl() -> chat_ctrl {
|
||||
public func getChatCtrl(_ useKey: String? = nil) -> chat_ctrl {
|
||||
if let controller = chatController { return controller }
|
||||
fatalError("chat controller not initialized")
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public enum ChatCommand {
|
||||
case apiMuteUser(userId: Int64)
|
||||
case apiUnmuteUser(userId: Int64)
|
||||
case apiDeleteUser(userId: Int64, delSMPQueues: Bool, viewPwd: String?)
|
||||
case startChat(mainApp: Bool)
|
||||
case startChat(subscribe: Bool, expire: Bool, xftp: Bool)
|
||||
case apiStopChat
|
||||
case apiActivateChat(restoreChat: Bool)
|
||||
case apiSuspendChat(timeoutMicroseconds: Int)
|
||||
@@ -154,7 +154,7 @@ public enum ChatCommand {
|
||||
case let .apiMuteUser(userId): return "/_mute user \(userId)"
|
||||
case let .apiUnmuteUser(userId): return "/_unmute user \(userId)"
|
||||
case let .apiDeleteUser(userId, delSMPQueues, viewPwd): return "/_delete user \(userId) del_smp=\(onOff(delSMPQueues))\(maybePwd(viewPwd))"
|
||||
case let .startChat(mainApp): return "/_start main=\(onOff(mainApp))"
|
||||
case let .startChat(subscribe, expire, xftp): return "/_start subscribe=\(onOff(subscribe)) expire=\(onOff(expire)) xftp=\(onOff(xftp))"
|
||||
case .apiStopChat: return "/_stop"
|
||||
case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))"
|
||||
case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)"
|
||||
|
||||
@@ -23,19 +23,3 @@ void haskell_init(void) {
|
||||
char **pargv = argv;
|
||||
hs_init_with_rtsopts(&argc, &pargv);
|
||||
}
|
||||
|
||||
void haskell_init_nse(void) {
|
||||
int argc = 7;
|
||||
char *argv[] = {
|
||||
"simplex",
|
||||
"+RTS", // requires `hs_init_with_rtsopts`
|
||||
"-A1m", // chunk size for new allocations
|
||||
"-H1m", // initial heap size
|
||||
"-F0.5", // heap growth triggering GC
|
||||
"-Fd1", // memory return
|
||||
"-c", // compacting garbage collector
|
||||
0
|
||||
};
|
||||
char **pargv = argv;
|
||||
hs_init_with_rtsopts(&argc, &pargv);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,4 @@
|
||||
|
||||
void haskell_init(void);
|
||||
|
||||
void haskell_init_nse(void);
|
||||
|
||||
#endif /* hs_init_h */
|
||||
|
||||
@@ -164,14 +164,13 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
androidAppContext = this
|
||||
APPLICATION_ID = BuildConfig.APPLICATION_ID
|
||||
ntfManager = object : chat.simplex.common.platform.NtfManager() {
|
||||
override fun notifyCallInvitation(invitation: RcvCallInvitation): Boolean = NtfManager.notifyCallInvitation(invitation)
|
||||
override fun notifyCallInvitation(invitation: RcvCallInvitation) = NtfManager.notifyCallInvitation(invitation)
|
||||
override fun hasNotificationsForChat(chatId: String): Boolean = NtfManager.hasNotificationsForChat(chatId)
|
||||
override fun cancelNotificationsForChat(chatId: String) = NtfManager.cancelNotificationsForChat(chatId)
|
||||
override fun displayNotification(user: UserLike, chatId: String, displayName: String, msgText: String, image: String?, actions: List<Pair<NotificationAction, () -> Unit>>) = NtfManager.displayNotification(user, chatId, displayName, msgText, image, actions.map { it.first })
|
||||
override fun androidCreateNtfChannelsMaybeShowAlert() = NtfManager.createNtfChannelsMaybeShowAlert()
|
||||
override fun cancelCallNotification() = NtfManager.cancelCallNotification()
|
||||
override fun cancelAllNotifications() = NtfManager.cancelAllNotifications()
|
||||
override fun showMessage(title: String, text: String) = NtfManager.showMessage(title, text)
|
||||
}
|
||||
platform = object : PlatformInterface {
|
||||
override suspend fun androidServiceStart() {
|
||||
|
||||
+5
-39
@@ -30,7 +30,7 @@ object NtfManager {
|
||||
const val ShowChatsAction: String = "chat.simplex.app.SHOW_CHATS"
|
||||
|
||||
// DO NOT change notification channel settings / names
|
||||
const val CallChannel: String = "chat.simplex.app.CALL_NOTIFICATION_2"
|
||||
const val CallChannel: String = "chat.simplex.app.CALL_NOTIFICATION_1"
|
||||
const val AcceptCallAction: String = "chat.simplex.app.ACCEPT_CALL"
|
||||
const val RejectCallAction: String = "chat.simplex.app.REJECT_CALL"
|
||||
const val CallNotificationId: Int = -1
|
||||
@@ -59,7 +59,7 @@ object NtfManager {
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
|
||||
.build()
|
||||
val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/raw/ring_once")
|
||||
val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/" + R.raw.ring_once)
|
||||
Log.d(TAG, "callNotificationChannel sound: $soundUri")
|
||||
callChannel.setSound(soundUri, attrs)
|
||||
callChannel.enableVibration(true)
|
||||
@@ -140,7 +140,7 @@ object NtfManager {
|
||||
}
|
||||
}
|
||||
|
||||
fun notifyCallInvitation(invitation: RcvCallInvitation): Boolean {
|
||||
fun notifyCallInvitation(invitation: RcvCallInvitation) {
|
||||
val keyguardManager = getKeyguardManager(context)
|
||||
Log.d(
|
||||
TAG,
|
||||
@@ -149,7 +149,7 @@ object NtfManager {
|
||||
"callOnLockScreen ${appPreferences.callOnLockScreen.get()}, " +
|
||||
"onForeground ${isAppOnForeground}"
|
||||
)
|
||||
if (isAppOnForeground) return false
|
||||
if (isAppOnForeground) return
|
||||
val contactId = invitation.contact.id
|
||||
Log.d(TAG, "notifyCallInvitation $contactId")
|
||||
val image = invitation.contact.image
|
||||
@@ -163,7 +163,7 @@ object NtfManager {
|
||||
.setFullScreenIntent(fullScreenPendingIntent, true)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
} else {
|
||||
val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/raw/ring_once")
|
||||
val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/" + R.raw.ring_once)
|
||||
val fullScreenPendingIntent = PendingIntent.getActivity(context, 0, Intent(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||
NotificationCompat.Builder(context, CallChannel)
|
||||
.setContentIntent(chatPendingIntent(OpenChatAction, invitation.user.userId, invitation.contact.id))
|
||||
@@ -206,39 +206,6 @@ object NtfManager {
|
||||
notify(CallNotificationId, notification)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun showMessage(title: String, text: String) {
|
||||
val builder = NotificationCompat.Builder(context, MessageChannel)
|
||||
.setContentTitle(title)
|
||||
.setContentText(text)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setGroup(MessageGroup)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
|
||||
.setSmallIcon(R.drawable.ntf_icon)
|
||||
.setLargeIcon(null)
|
||||
.setColor(0x88FFFF)
|
||||
.setAutoCancel(true)
|
||||
.setVibrate(null)
|
||||
.setContentIntent(chatPendingIntent(ShowChatsAction, null, null))
|
||||
.setSilent(false)
|
||||
|
||||
val summary = NotificationCompat.Builder(context, MessageChannel)
|
||||
.setSmallIcon(R.drawable.ntf_icon)
|
||||
.setColor(0x88FFFF)
|
||||
.setGroup(MessageGroup)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
|
||||
.setGroupSummary(true)
|
||||
.setContentIntent(chatPendingIntent(ShowChatsAction, null))
|
||||
.build()
|
||||
|
||||
with(NotificationManagerCompat.from(context)) {
|
||||
if (ActivityCompat.checkSelfPermission(SimplexApp.context, android.Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
|
||||
notify("MESSAGE".hashCode(), builder.build())
|
||||
notify(0, summary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelCallNotification() {
|
||||
@@ -281,7 +248,6 @@ object NtfManager {
|
||||
manager.createNotificationChannel(callNotificationChannel(CallChannel, generalGetString(MR.strings.ntf_channel_calls)))
|
||||
// Remove old channels since they can't be edited
|
||||
manager.deleteNotificationChannel("chat.simplex.app.CALL_NOTIFICATION")
|
||||
manager.deleteNotificationChannel("chat.simplex.app.CALL_NOTIFICATION_1")
|
||||
manager.deleteNotificationChannel("chat.simplex.app.LOCK_SCREEN_CALL_NOTIFICATION")
|
||||
}
|
||||
|
||||
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package chat.simplex.common.views.database
|
||||
|
||||
import chat.simplex.common.views.usersettings.restartApp
|
||||
|
||||
actual fun restartChatOrApp() {
|
||||
restartApp()
|
||||
}
|
||||
+1
-1
@@ -28,7 +28,7 @@ actual fun SettingsSectionApp(
|
||||
}
|
||||
|
||||
|
||||
fun restartApp() {
|
||||
private fun restartApp() {
|
||||
ProcessPhoenix.triggerRebirth(androidAppContext)
|
||||
shutdownApp()
|
||||
}
|
||||
|
||||
@@ -122,9 +122,6 @@ object ChatModel {
|
||||
val remoteHostPairing = mutableStateOf<Pair<RemoteHostInfo?, RemoteHostSessionState>?>(null)
|
||||
val remoteCtrlSession = mutableStateOf<RemoteCtrlSession?>(null)
|
||||
|
||||
val processedCriticalError: ProcessedErrors<AgentErrorType.CRITICAL> = ProcessedErrors(60_000)
|
||||
val processedInternalError: ProcessedErrors<AgentErrorType.INTERNAL> = ProcessedErrors(20_000)
|
||||
|
||||
fun getUser(userId: Long): User? = if (currentUser.value?.userId == userId) {
|
||||
currentUser.value
|
||||
} else {
|
||||
|
||||
+3
-15
@@ -108,7 +108,6 @@ class AppPreferences {
|
||||
val chatArchiveTime = mkDatePreference(SHARED_PREFS_CHAT_ARCHIVE_TIME, null)
|
||||
val chatLastStart = mkDatePreference(SHARED_PREFS_CHAT_LAST_START, null)
|
||||
val developerTools = mkBoolPreference(SHARED_PREFS_DEVELOPER_TOOLS, false)
|
||||
val showInternalErrors = mkBoolPreference(SHARED_PREFS_SHOW_INTERNAL_ERRORS, false)
|
||||
val terminalAlwaysVisible = mkBoolPreference(SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE, false)
|
||||
val networkUseSocksProxy = mkBoolPreference(SHARED_PREFS_NETWORK_USE_SOCKS_PROXY, false)
|
||||
val networkProxyHostPort = mkStrPreference(SHARED_PREFS_NETWORK_PROXY_HOST_PORT, "localhost:9050")
|
||||
@@ -277,7 +276,6 @@ class AppPreferences {
|
||||
private const val SHARED_PREFS_ONBOARDING_STAGE = "OnboardingStage"
|
||||
private const val SHARED_PREFS_CHAT_LAST_START = "ChatLastStart"
|
||||
private const val SHARED_PREFS_DEVELOPER_TOOLS = "DeveloperTools"
|
||||
private const val SHARED_PREFS_SHOW_INTERNAL_ERRORS = "ShowInternalErrors"
|
||||
private const val SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE = "TerminalAlwaysVisible"
|
||||
private const val SHARED_PREFS_NETWORK_USE_SOCKS_PROXY = "NetworkUseSocksProxy"
|
||||
private const val SHARED_PREFS_NETWORK_PROXY_HOST_PORT = "NetworkProxyHostPort"
|
||||
@@ -585,7 +583,7 @@ object ChatController {
|
||||
}
|
||||
|
||||
suspend fun apiStartChat(): Boolean {
|
||||
val r = sendCmd(null, CC.StartChat(mainApp = true))
|
||||
val r = sendCmd(null, CC.StartChat(expire = true))
|
||||
when (r) {
|
||||
is CR.ChatStarted -> return true
|
||||
is CR.ChatRunning -> return false
|
||||
@@ -1922,14 +1920,6 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.ChatCmdError -> when {
|
||||
r.chatError is ChatError.ChatErrorAgent && r.chatError.agentError is AgentErrorType.CRITICAL -> {
|
||||
chatModel.processedCriticalError.newError(r.chatError.agentError, r.chatError.agentError.offerRestart)
|
||||
}
|
||||
r.chatError is ChatError.ChatErrorAgent && r.chatError.agentError is AgentErrorType.INTERNAL && appPrefs.showInternalErrors.get() -> {
|
||||
chatModel.processedInternalError.newError(r.chatError.agentError, false)
|
||||
}
|
||||
}
|
||||
else ->
|
||||
Log.d(TAG , "unsupported event: ${r.responseType}")
|
||||
}
|
||||
@@ -2171,7 +2161,7 @@ sealed class CC {
|
||||
class ApiMuteUser(val userId: Long): CC()
|
||||
class ApiUnmuteUser(val userId: Long): CC()
|
||||
class ApiDeleteUser(val userId: Long, val delSMPQueues: Boolean, val viewPwd: String?): CC()
|
||||
class StartChat(val mainApp: Boolean): CC()
|
||||
class StartChat(val expire: Boolean): CC()
|
||||
class ApiStopChat: CC()
|
||||
class SetTempFolder(val tempFolder: String): CC()
|
||||
class SetFilesFolder(val filesFolder: String): CC()
|
||||
@@ -2298,7 +2288,7 @@ sealed class CC {
|
||||
is ApiMuteUser -> "/_mute user $userId"
|
||||
is ApiUnmuteUser -> "/_unmute user $userId"
|
||||
is ApiDeleteUser -> "/_delete user $userId del_smp=${onOff(delSMPQueues)}${maybePwd(viewPwd)}"
|
||||
is StartChat -> "/_start main=${onOff(mainApp)}"
|
||||
is StartChat -> "/_start subscribe=on expire=${onOff(expire)} xftp=on"
|
||||
is ApiStopChat -> "/_stop"
|
||||
is SetTempFolder -> "/_temp_folder $tempFolder"
|
||||
is SetFilesFolder -> "/_files_folder $filesFolder"
|
||||
@@ -4720,7 +4710,6 @@ sealed class AgentErrorType {
|
||||
is AGENT -> "AGENT ${agentErr.string}"
|
||||
is INTERNAL -> "INTERNAL $internalErr"
|
||||
is INACTIVE -> "INACTIVE"
|
||||
is CRITICAL -> "CRITICAL $offerRestart $criticalErr"
|
||||
}
|
||||
@Serializable @SerialName("CMD") class CMD(val cmdErr: CommandErrorType): AgentErrorType()
|
||||
@Serializable @SerialName("CONN") class CONN(val connErr: ConnectionErrorType): AgentErrorType()
|
||||
@@ -4732,7 +4721,6 @@ sealed class AgentErrorType {
|
||||
@Serializable @SerialName("AGENT") class AGENT(val agentErr: SMPAgentError): AgentErrorType()
|
||||
@Serializable @SerialName("INTERNAL") class INTERNAL(val internalErr: String): AgentErrorType()
|
||||
@Serializable @SerialName("INACTIVE") object INACTIVE: AgentErrorType()
|
||||
@Serializable @SerialName("CRITICAL") data class CRITICAL(val offerRestart: Boolean, val criticalErr: String): AgentErrorType()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
||||
+1
-2
@@ -93,13 +93,12 @@ abstract class NtfManager {
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun notifyCallInvitation(invitation: RcvCallInvitation): Boolean
|
||||
abstract fun notifyCallInvitation(invitation: RcvCallInvitation)
|
||||
abstract fun hasNotificationsForChat(chatId: String): Boolean
|
||||
abstract fun cancelNotificationsForChat(chatId: String)
|
||||
abstract fun displayNotification(user: UserLike, chatId: String, displayName: String, msgText: String, image: String? = null, actions: List<Pair<NotificationAction, () -> Unit>> = emptyList())
|
||||
abstract fun cancelCallNotification()
|
||||
abstract fun cancelAllNotifications()
|
||||
abstract fun showMessage(title: String, text: String)
|
||||
// Android only
|
||||
abstract fun androidCreateNtfChannelsMaybeShowAlert()
|
||||
|
||||
|
||||
+1
-1
@@ -13,8 +13,8 @@ class CallManager(val chatModel: ChatModel) {
|
||||
callInvitations[invitation.contact.id] = invitation
|
||||
if (invitation.user.showNotifications) {
|
||||
if (Clock.System.now() - invitation.callTs <= 3.minutes) {
|
||||
invitation.sentNotification = ntfManager.notifyCallInvitation(invitation)
|
||||
activeCallInvitation.value = invitation
|
||||
ntfManager.notifyCallInvitation(invitation)
|
||||
} else {
|
||||
val contact = invitation.contact
|
||||
ntfManager.displayNotification(user = invitation.user, chatId = contact.id, displayName = contact.displayName, msgText = invitation.callTypeText)
|
||||
|
||||
+3
-6
@@ -15,10 +15,11 @@ import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.ProfileImage
|
||||
import chat.simplex.common.views.usersettings.ProfilePreview
|
||||
import chat.simplex.common.platform.ntfManager
|
||||
import chat.simplex.common.platform.SoundPlayer
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
@@ -26,11 +27,7 @@ import kotlinx.datetime.Clock
|
||||
fun IncomingCallAlertView(invitation: RcvCallInvitation, chatModel: ChatModel) {
|
||||
val cm = chatModel.callManager
|
||||
val scope = rememberCoroutineScope()
|
||||
LaunchedEffect(Unit) {
|
||||
if (chatModel.activeCallInvitation.value?.sentNotification == false || appPlatform.isDesktop) {
|
||||
SoundPlayer.start(scope, sound = !chatModel.showCallView.value)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(true) { SoundPlayer.start(scope, sound = !chatModel.showCallView.value) }
|
||||
DisposableEffect(true) { onDispose { SoundPlayer.stop() } }
|
||||
IncomingCallAlertLayout(
|
||||
invitation,
|
||||
|
||||
-3
@@ -112,9 +112,6 @@ sealed class WCallResponse {
|
||||
CallMediaType.Video -> MR.strings.incoming_video_call
|
||||
CallMediaType.Audio -> MR.strings.incoming_audio_call
|
||||
})
|
||||
|
||||
// Shows whether notification was shown or not to prevent playing sound twice in both notification and in-app
|
||||
var sentNotification: Boolean = false
|
||||
}
|
||||
@Serializable data class CallCapabilities(val encryption: Boolean)
|
||||
@Serializable data class ConnectionInfo(private val localCandidate: RTCIceCandidate?, private val remoteCandidate: RTCIceCandidate?) {
|
||||
|
||||
+6
-8
@@ -4,6 +4,7 @@ import SectionBottomSpacer
|
||||
import SectionDividerSpaced
|
||||
import SectionTextFooter
|
||||
import SectionItemView
|
||||
import SectionSpacer
|
||||
import SectionView
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.layout.*
|
||||
@@ -366,7 +367,7 @@ fun chatArchiveTitle(chatArchiveTime: Instant, chatLastStart: Instant): String {
|
||||
return stringResource(if (chatArchiveTime < chatLastStart) MR.strings.old_database_archive else MR.strings.new_database_archive)
|
||||
}
|
||||
|
||||
fun startChat(m: ChatModel, chatLastStart: MutableState<Instant?>, chatDbChanged: MutableState<Boolean>) {
|
||||
private fun startChat(m: ChatModel, chatLastStart: MutableState<Instant?>, chatDbChanged: MutableState<Boolean>) {
|
||||
withApi {
|
||||
try {
|
||||
if (chatDbChanged.value) {
|
||||
@@ -406,8 +407,6 @@ private fun stopChatAlert(m: ChatModel) {
|
||||
)
|
||||
}
|
||||
|
||||
expect fun restartChatOrApp()
|
||||
|
||||
private fun exportProhibitedAlert() {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.set_password_to_export),
|
||||
@@ -415,7 +414,7 @@ private fun exportProhibitedAlert() {
|
||||
)
|
||||
}
|
||||
|
||||
fun authStopChat(m: ChatModel, onStop: (() -> Unit)? = null) {
|
||||
private fun authStopChat(m: ChatModel) {
|
||||
if (m.controller.appPrefs.performLA.get()) {
|
||||
authenticate(
|
||||
generalGetString(MR.strings.auth_stop_chat),
|
||||
@@ -423,7 +422,7 @@ fun authStopChat(m: ChatModel, onStop: (() -> Unit)? = null) {
|
||||
completed = { laResult ->
|
||||
when (laResult) {
|
||||
LAResult.Success, is LAResult.Unavailable -> {
|
||||
stopChat(m, onStop)
|
||||
stopChat(m)
|
||||
}
|
||||
is LAResult.Error -> {
|
||||
m.chatRunning.value = true
|
||||
@@ -435,16 +434,15 @@ fun authStopChat(m: ChatModel, onStop: (() -> Unit)? = null) {
|
||||
}
|
||||
)
|
||||
} else {
|
||||
stopChat(m, onStop)
|
||||
stopChat(m)
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopChat(m: ChatModel, onStop: (() -> Unit)? = null) {
|
||||
private fun stopChat(m: ChatModel) {
|
||||
withApi {
|
||||
try {
|
||||
stopChatAsync(m)
|
||||
platform.androidChatStopped()
|
||||
onStop?.invoke()
|
||||
} catch (e: Error) {
|
||||
m.chatRunning.value = true
|
||||
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error_stopping_chat), e.toString())
|
||||
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package chat.simplex.common.views.helpers
|
||||
|
||||
import chat.simplex.common.model.AgentErrorType
|
||||
import chat.simplex.common.platform.Log
|
||||
import chat.simplex.common.platform.TAG
|
||||
import chat.simplex.common.platform.ntfManager
|
||||
import chat.simplex.common.views.database.restartChatOrApp
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
class ProcessedErrors <T: AgentErrorType>(val interval: Long) {
|
||||
private var lastShownTimestamp: Long = -1
|
||||
private var lastShownOfferRestart: Boolean = false
|
||||
private var timer: Job = Job()
|
||||
|
||||
fun newError(error: T, offerRestart: Boolean) {
|
||||
timer.cancel()
|
||||
timer = withBGApi {
|
||||
val delayBeforeNext = (lastShownTimestamp + interval) - System.currentTimeMillis()
|
||||
if ((lastShownOfferRestart || !offerRestart) && delayBeforeNext >= 0) {
|
||||
delay(delayBeforeNext)
|
||||
}
|
||||
lastShownTimestamp = System.currentTimeMillis()
|
||||
lastShownOfferRestart = offerRestart
|
||||
AlertManager.shared.hideAllAlerts()
|
||||
showMessage(error, offerRestart)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showMessage(error: T, offerRestart: Boolean) {
|
||||
when (error) {
|
||||
is AgentErrorType.CRITICAL -> {
|
||||
val title = generalGetString(MR.strings.agent_critical_error_title)
|
||||
val text = generalGetString(MR.strings.agent_critical_error_desc).format(error.criticalErr)
|
||||
try {
|
||||
ntfManager.showMessage(title, text)
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, e.stackTraceToString())
|
||||
}
|
||||
if (offerRestart) {
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = title,
|
||||
text = text,
|
||||
confirmText = generalGetString(MR.strings.restart_chat_button),
|
||||
onConfirm = {
|
||||
withApi { restartChatOrApp() }
|
||||
})
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = title,
|
||||
text = text,
|
||||
)
|
||||
}
|
||||
}
|
||||
is AgentErrorType.INTERNAL -> {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.agent_internal_error_title),
|
||||
text = generalGetString(MR.strings.agent_internal_error_desc).format(error.internalErr),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -10,11 +10,10 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import chat.simplex.common.model.*
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.platform.appPlatform
|
||||
import chat.simplex.common.platform.appPreferences
|
||||
import chat.simplex.common.views.TerminalView
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
@@ -45,7 +44,6 @@ fun DeveloperView(
|
||||
m.controller.appPrefs.terminalAlwaysVisible.set(false)
|
||||
}
|
||||
}
|
||||
SettingsPreferenceItem(painterResource(MR.images.ic_report), stringResource(MR.strings.show_internal_errors), appPreferences.showInternalErrors)
|
||||
}
|
||||
}
|
||||
SectionTextFooter(
|
||||
|
||||
@@ -662,7 +662,6 @@
|
||||
<string name="hide_dev_options">Hide:</string>
|
||||
<string name="show_developer_options">Show developer options</string>
|
||||
<string name="developer_options">Database IDs and Transport isolation option.</string>
|
||||
<string name="show_internal_errors">Show internal errors</string>
|
||||
<string name="shutdown_alert_question">Shutdown?</string>
|
||||
<string name="shutdown_alert_desc">Notifications will stop working until you re-launch the app</string>
|
||||
|
||||
@@ -1725,11 +1724,4 @@
|
||||
<string name="connect_plan_you_are_already_joining_the_group_via_this_link">You are already joining the group via this link.</string>
|
||||
<string name="connect_plan_you_are_already_in_group_vName">You are already in group %1$s.</string>
|
||||
<string name="connect_plan_connect_via_link">Connect via link?</string>
|
||||
|
||||
<!-- Errors -->
|
||||
<string name="agent_critical_error_title">Critical error</string>
|
||||
<string name="agent_critical_error_desc">Please report it to the developers: \n%s\n\nIt is recommended to restart the app.</string>
|
||||
<string name="agent_internal_error_title">Internal error</string>
|
||||
<string name="agent_internal_error_desc">Please report it to the developers: \n%s</string>
|
||||
<string name="restart_chat_button">Restart chat</string>
|
||||
</resources>
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M479.895-284Q494-284 504-293.895q10-9.894 10-24Q514-332 504.105-342q-9.894-10-24-10Q466-352 456-342.105q-10 9.894-10 24Q446-304 455.895-294q9.894 10 24 10ZM451.5-425H509v-261h-57.5v261ZM332-124.5 124.5-332.176V-628l207.676-207.5H628l207.5 207.676V-332L627.824-124.5H332Zm24.222-57.5h248.243L778-356.222v-248.243L604.242-778H356L182-604.242V-356l174.222 174ZM480-480Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 472 B |
+2
-7
@@ -16,8 +16,8 @@ import javax.imageio.ImageIO
|
||||
object NtfManager {
|
||||
private val prevNtfs = arrayListOf<Pair<ChatId, Slice>>()
|
||||
|
||||
fun notifyCallInvitation(invitation: RcvCallInvitation): Boolean {
|
||||
if (simplexWindowState.windowFocused.value) return false
|
||||
fun notifyCallInvitation(invitation: RcvCallInvitation) {
|
||||
if (simplexWindowState.windowFocused.value) return
|
||||
val contactId = invitation.contact.id
|
||||
Log.d(TAG, "notifyCallInvitation $contactId")
|
||||
val image = invitation.contact.image
|
||||
@@ -45,11 +45,6 @@ object NtfManager {
|
||||
displayNotificationViaLib(contactId, title, text, prepareIconPath(largeIcon), actions) {
|
||||
ntfManager.openChatAction(invitation.user.userId, contactId)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun showMessage(title: String, text: String) {
|
||||
displayNotificationViaLib("MESSAGE", title, text, null, emptyList()) {}
|
||||
}
|
||||
|
||||
fun hasNotificationsForChat(chatId: ChatId) = false//prevNtfs.any { it.first == chatId }
|
||||
|
||||
+1
-2
@@ -16,14 +16,13 @@ val defaultLocale: Locale = Locale.getDefault()
|
||||
|
||||
fun initApp() {
|
||||
ntfManager = object : NtfManager() {
|
||||
override fun notifyCallInvitation(invitation: RcvCallInvitation): Boolean = chat.simplex.common.model.NtfManager.notifyCallInvitation(invitation)
|
||||
override fun notifyCallInvitation(invitation: RcvCallInvitation) = chat.simplex.common.model.NtfManager.notifyCallInvitation(invitation)
|
||||
override fun hasNotificationsForChat(chatId: String): Boolean = chat.simplex.common.model.NtfManager.hasNotificationsForChat(chatId)
|
||||
override fun cancelNotificationsForChat(chatId: String) = chat.simplex.common.model.NtfManager.cancelNotificationsForChat(chatId)
|
||||
override fun displayNotification(user: UserLike, chatId: String, displayName: String, msgText: String, image: String?, actions: List<Pair<NotificationAction, () -> Unit>>) = chat.simplex.common.model.NtfManager.displayNotification(user, chatId, displayName, msgText, image, actions)
|
||||
override fun androidCreateNtfChannelsMaybeShowAlert() {}
|
||||
override fun cancelCallNotification() {}
|
||||
override fun cancelAllNotifications() = chat.simplex.common.model.NtfManager.cancelAllNotifications()
|
||||
override fun showMessage(title: String, text: String) = chat.simplex.common.model.NtfManager.showMessage(title, text)
|
||||
}
|
||||
applyAppLocale()
|
||||
withBGApi {
|
||||
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
package chat.simplex.common.views.database
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import chat.simplex.common.platform.chatModel
|
||||
import chat.simplex.common.views.helpers.withApi
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.datetime.Instant
|
||||
|
||||
actual fun restartChatOrApp() {
|
||||
if (chatModel.chatRunning.value == false) {
|
||||
chatModel.chatDbChanged.value = true
|
||||
startChat(chatModel, mutableStateOf(Instant.DISTANT_PAST), chatModel.chatDbChanged)
|
||||
} else {
|
||||
authStopChat(chatModel) {
|
||||
withApi {
|
||||
// adding delay in order to prevent locked database by previous initialization
|
||||
delay(1000)
|
||||
chatModel.chatDbChanged.value = true
|
||||
startChat(chatModel, mutableStateOf(Instant.DISTANT_PAST), chatModel.chatDbChanged)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,11 +25,11 @@ android.nonTransitiveRClass=true
|
||||
android.enableJetifier=true
|
||||
kotlin.mpp.androidSourceSetLayoutVersion=2
|
||||
|
||||
android.version_name=5.4.3
|
||||
android.version_code=169
|
||||
android.version_name=5.4.2
|
||||
android.version_code=166
|
||||
|
||||
desktop.version_name=5.4.3
|
||||
desktop.version_code=22
|
||||
desktop.version_name=5.4.2
|
||||
desktop.version_code=20
|
||||
|
||||
kotlin.version=1.8.20
|
||||
gradle.plugin.version=7.4.2
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ constraints: zip +disable-bzip2 +disable-zstd
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/simplex-chat/simplexmq.git
|
||||
tag: f6ed4640d407f8879273d104a3e69069806dcb7c
|
||||
tag: d0588bd0ac23a459cbfc9a4789633014e91ffa19
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
|
||||
Generated
+150
-92
@@ -51,11 +51,11 @@
|
||||
"cabal-34": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1640353650,
|
||||
"narHash": "sha256-N1t6M3/wqj90AEdRkeC8i923gQYUpzSr8b40qVOZ1Rk=",
|
||||
"lastModified": 1645834128,
|
||||
"narHash": "sha256-wG3d+dOt14z8+ydz4SL7pwGfe7SiimxcD/LOuPCV6xM=",
|
||||
"owner": "haskell",
|
||||
"repo": "cabal",
|
||||
"rev": "942639c18c0cd8ec53e0a6f8d120091af35312cd",
|
||||
"rev": "5ff598c67f53f7c4f48e31d722ba37172230c462",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -68,11 +68,11 @@
|
||||
"cabal-36": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1641652457,
|
||||
"narHash": "sha256-BlFPKP4C4HRUJeAbdembX1Rms1LD380q9s0qVDeoAak=",
|
||||
"lastModified": 1669081697,
|
||||
"narHash": "sha256-I5or+V7LZvMxfbYgZATU4awzkicBwwok4mVoje+sGmU=",
|
||||
"owner": "haskell",
|
||||
"repo": "cabal",
|
||||
"rev": "f27667f8ec360c475027dcaee0138c937477b070",
|
||||
"rev": "8fd619e33d34924a94e691c5fea2c42f0fc7f144",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -159,15 +159,16 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1635892615,
|
||||
"narHash": "sha256-harGbMZr4hzat2BWBU+Y5OYXlu+fVz7E4WeQzHi5o8A=",
|
||||
"lastModified": 1672831974,
|
||||
"narHash": "sha256-z9k3MfslLjWQfnjBtEtJZdq3H7kyi2kQtUThfTgdRk0=",
|
||||
"owner": "input-output-hk",
|
||||
"repo": "flake-compat",
|
||||
"rev": "eca47d3377946315596da653862d341ee5341318",
|
||||
"rev": "45f2638735f8cdc40fe302742b79f248d23eb368",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "input-output-hk",
|
||||
"ref": "hkm/gitlab-fix",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -190,11 +191,11 @@
|
||||
},
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1667395993,
|
||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||
"lastModified": 1676283394,
|
||||
"narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||
"rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -205,11 +206,11 @@
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"locked": {
|
||||
"lastModified": 1644229661,
|
||||
"narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=",
|
||||
"lastModified": 1667395993,
|
||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797",
|
||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -248,21 +249,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_5": {
|
||||
"locked": {
|
||||
"lastModified": 1653893745,
|
||||
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"ghc-8.6.5-iohk": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -330,6 +316,7 @@
|
||||
],
|
||||
"hpc-coveralls": "hpc-coveralls",
|
||||
"hydra": "hydra",
|
||||
"iserv-proxy": "iserv-proxy",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
@@ -337,21 +324,23 @@
|
||||
"nixpkgs-2105": "nixpkgs-2105",
|
||||
"nixpkgs-2111": "nixpkgs-2111",
|
||||
"nixpkgs-2205": "nixpkgs-2205",
|
||||
"nixpkgs-2211": "nixpkgs-2211",
|
||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||
"old-ghc-nix": "old-ghc-nix",
|
||||
"stackage": "stackage",
|
||||
"tullia": "tullia"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1672501055,
|
||||
"narHash": "sha256-Wy6KqoYqQOP1rBvfHUvM3ex8HIdBA4kwnvZj2qQ1VLU=",
|
||||
"owner": "simplex-chat",
|
||||
"lastModified": 1677975916,
|
||||
"narHash": "sha256-dbe8lEEPyfzjdRwpePClv7J9p9lQg7BwbBqAMCw4RLw=",
|
||||
"owner": "input-output-hk",
|
||||
"repo": "haskell.nix",
|
||||
"rev": "dc719cd6dc318923c4a524d005c13f474c00d3d3",
|
||||
"rev": "ab5efd87ce3fd8ade38a01d97693d29a4f1ae7e4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "simplex-chat",
|
||||
"owner": "input-output-hk",
|
||||
"ref": "armv7a",
|
||||
"repo": "haskell.nix",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -383,11 +372,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1646878427,
|
||||
"narHash": "sha256-KtbrofMtN8GlM7D+n90kixr7QpSlVmdN+vK5CA/aRzc=",
|
||||
"lastModified": 1671755331,
|
||||
"narHash": "sha256-hXsgJj0Cy0ZiCiYdW2OdBz5WmFyOMKuw4zyxKpgUKm4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "hydra",
|
||||
"rev": "28b682b85b7efc5cf7974065792a1f22203a5927",
|
||||
"rev": "f48f00ee6d5727ae3e488cbf9ce157460853fea8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -395,6 +384,46 @@
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"incl": {
|
||||
"inputs": {
|
||||
"nixlib": [
|
||||
"haskellNix",
|
||||
"tullia",
|
||||
"std",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1669263024,
|
||||
"narHash": "sha256-E/+23NKtxAqYG/0ydYgxlgarKnxmDbg6rCMWnOBqn9Q=",
|
||||
"owner": "divnix",
|
||||
"repo": "incl",
|
||||
"rev": "ce7bebaee048e4cd7ebdb4cee7885e00c4e2abca",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "divnix",
|
||||
"repo": "incl",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"iserv-proxy": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1670983692,
|
||||
"narHash": "sha256-avLo34JnI9HNyOuauK5R69usJm+GfW3MlyGlYxZhTgY=",
|
||||
"ref": "hkm/remote-iserv",
|
||||
"rev": "50d0abb3317ac439a4e7495b185a64af9b7b9300",
|
||||
"revCount": 10,
|
||||
"type": "git",
|
||||
"url": "https://gitlab.haskell.org/hamishmack/iserv-proxy.git"
|
||||
},
|
||||
"original": {
|
||||
"ref": "hkm/remote-iserv",
|
||||
"type": "git",
|
||||
"url": "https://gitlab.haskell.org/hamishmack/iserv-proxy.git"
|
||||
}
|
||||
},
|
||||
"lowdown-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -411,25 +440,14 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"mdbook-kroki-preprocessor": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1661755005,
|
||||
"narHash": "sha256-1TJuUzfyMycWlOQH67LR63/ll2GDZz25I3JfScy/Jnw=",
|
||||
"owner": "JoelCourtney",
|
||||
"repo": "mdbook-kroki-preprocessor",
|
||||
"rev": "93adb5716d035829efed27f65f2f0833a7d3e76f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "JoelCourtney",
|
||||
"repo": "mdbook-kroki-preprocessor",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"n2c": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_5",
|
||||
"flake-utils": [
|
||||
"haskellNix",
|
||||
"tullia",
|
||||
"std",
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"haskellNix",
|
||||
"tullia",
|
||||
@@ -458,16 +476,16 @@
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1643066034,
|
||||
"narHash": "sha256-xEPeMcNJVOeZtoN+d+aRwolpW8mFSEQx76HTRdlhPhg=",
|
||||
"lastModified": 1661606874,
|
||||
"narHash": "sha256-9+rpYzI+SmxJn+EbYxjGv68Ucp22bdFUSy/4LkHkkDQ=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nix",
|
||||
"rev": "a1cd7e58606a41fcf62bf8637804cf8306f17f62",
|
||||
"rev": "11e45768b34fdafdcf019ddbd337afa16127ff0f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "2.6.0",
|
||||
"ref": "2.11.0",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -563,17 +581,18 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1632864508,
|
||||
"narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=",
|
||||
"lastModified": 1657693803,
|
||||
"narHash": "sha256-G++2CJ9u0E7NNTAi9n5G8TdDmGJXcIjkJ3NF8cetQB8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "82891b5e2c2359d7e58d08849e4c89511ab94234",
|
||||
"rev": "365e1b3a859281cf11b94f87231adeabbdd878a2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-21.05-small",
|
||||
"type": "indirect"
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-22.05-small",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-2003": {
|
||||
@@ -626,11 +645,11 @@
|
||||
},
|
||||
"nixpkgs-2205": {
|
||||
"locked": {
|
||||
"lastModified": 1663981975,
|
||||
"narHash": "sha256-TKaxWAVJR+a5JJauKZqibmaM5e/Pi5tBDx9s8fl/kSE=",
|
||||
"lastModified": 1672580127,
|
||||
"narHash": "sha256-3lW3xZslREhJogoOkjeZtlBtvFMyxHku7I/9IVehhT8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "309faedb8338d3ae8ad8f1043b3ccf48c9cc2970",
|
||||
"rev": "0874168639713f547c05947c76124f78441ea46c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -640,6 +659,22 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-2211": {
|
||||
"locked": {
|
||||
"lastModified": 1675730325,
|
||||
"narHash": "sha256-uNvD7fzO5hNlltNQUAFBPlcEjNG5Gkbhl/ROiX+GZU4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b7ce17b1ebf600a72178f6302c77b6382d09323f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-22.11-darwin",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
@@ -650,18 +685,19 @@
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "indirect"
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-unstable": {
|
||||
"locked": {
|
||||
"lastModified": 1663905476,
|
||||
"narHash": "sha256-0CSwRKaYravh9v6qSlBpM0gNg0UhKT2lL7Yn6Zbx7UM=",
|
||||
"lastModified": 1675758091,
|
||||
"narHash": "sha256-7gFSQbSVAFUHtGCNHPF7mPc5CcqDk9M2+inlVPZSneg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e14f9fb57315f0d4abde222364f19f88c77d2b79",
|
||||
"rev": "747927516efcb5e31ba03b7ff32f61f6d47e7d87",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -720,20 +756,35 @@
|
||||
},
|
||||
"nixpkgs_5": {
|
||||
"locked": {
|
||||
"lastModified": 1669833724,
|
||||
"narHash": "sha256-/HEZNyGbnQecrgJnfE8d0WC5c1xuPSD2LUpB6YXlg4c=",
|
||||
"owner": "nixos",
|
||||
"lastModified": 1676726892,
|
||||
"narHash": "sha256-M7OYVR6dKmzmlebIjybFf3l18S2uur8lMyWWnHQooLY=",
|
||||
"owner": "angerman",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4d2b37a84fad1091b9de401eb450aae66f1a741e",
|
||||
"rev": "729469087592bdea58b360de59dadf6d58714c42",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "22.11",
|
||||
"owner": "angerman",
|
||||
"ref": "release-22.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nosys": {
|
||||
"locked": {
|
||||
"lastModified": 1667881534,
|
||||
"narHash": "sha256-FhwJ15uPLRsvaxtt/bNuqE/ykMpNAPF0upozFKhTtXM=",
|
||||
"owner": "divnix",
|
||||
"repo": "nosys",
|
||||
"rev": "2d0d5207f6a230e9d0f660903f8db9807b54814f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "divnix",
|
||||
"repo": "nosys",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"old-ghc-nix": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -762,11 +813,11 @@
|
||||
"stackage": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1669598217,
|
||||
"narHash": "sha256-UioviNyxA3fexeguXLQpgMR6uWL9Q/wulipCbET3C8w=",
|
||||
"lastModified": 1677888571,
|
||||
"narHash": "sha256-YkhRNOaN6QVagZo1cfykYV8KqkI8/q6r2F5+jypOma4=",
|
||||
"owner": "input-output-hk",
|
||||
"repo": "stackage.nix",
|
||||
"rev": "8400280d894949e26354123aebc20801a2165182",
|
||||
"rev": "cb50e6fabdfb2d7e655059039012ad0623f06a27",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -777,17 +828,23 @@
|
||||
},
|
||||
"std": {
|
||||
"inputs": {
|
||||
"arion": [
|
||||
"haskellNix",
|
||||
"tullia",
|
||||
"std",
|
||||
"blank"
|
||||
],
|
||||
"blank": "blank",
|
||||
"devshell": "devshell",
|
||||
"dmerge": "dmerge",
|
||||
"flake-utils": "flake-utils_4",
|
||||
"incl": "incl",
|
||||
"makes": [
|
||||
"haskellNix",
|
||||
"tullia",
|
||||
"std",
|
||||
"blank"
|
||||
],
|
||||
"mdbook-kroki-preprocessor": "mdbook-kroki-preprocessor",
|
||||
"microvm": [
|
||||
"haskellNix",
|
||||
"tullia",
|
||||
@@ -797,14 +854,15 @@
|
||||
"n2c": "n2c",
|
||||
"nixago": "nixago",
|
||||
"nixpkgs": "nixpkgs_4",
|
||||
"nosys": "nosys",
|
||||
"yants": "yants"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1665513321,
|
||||
"narHash": "sha256-D6Pacw9yf/HMs84KYuCxHXnNDL7v43gtcka5URagFqE=",
|
||||
"lastModified": 1674526466,
|
||||
"narHash": "sha256-tMTaS0bqLx6VJ+K+ZT6xqsXNpzvSXJTmogkraBGzymg=",
|
||||
"owner": "divnix",
|
||||
"repo": "std",
|
||||
"rev": "94a90eedb9cfc115b12ae8f6622d9904788559e4",
|
||||
"rev": "516387e3d8d059b50e742a2ff1909ed3c8f82826",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -824,11 +882,11 @@
|
||||
"std": "std"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1666200256,
|
||||
"narHash": "sha256-cJPS8zBu30SMhxMe7I8DWutwqMuhPsEez87y9gxMKc4=",
|
||||
"lastModified": 1675695930,
|
||||
"narHash": "sha256-B7rEZ/DBUMlK1AcJ9ajnAPPxqXY6zW2SBX+51bZV0Ac=",
|
||||
"owner": "input-output-hk",
|
||||
"repo": "tullia",
|
||||
"rev": "575362c2244498e8d2c97f72861510fa72e75d44",
|
||||
"rev": "621365f2c725608f381b3ad5b57afef389fd4c31",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -862,11 +920,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1660507851,
|
||||
"narHash": "sha256-BKjq7JnVuUR/xDtcv6Vm9GYGKAblisXrAgybor9hT/s=",
|
||||
"lastModified": 1667096281,
|
||||
"narHash": "sha256-wRRec6ze0gJHmGn6m57/zhz/Kdvp9HS4Nl5fkQ+uIuA=",
|
||||
"owner": "divnix",
|
||||
"repo": "yants",
|
||||
"rev": "0b895ca02a8fa72bad50b454cb3e7d8a66407c96",
|
||||
"rev": "d18f356ec25cb94dc9c275870c3a7927a10f8c3c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
description = "nix flake for simplex-chat";
|
||||
inputs.nixpkgs.url = "github:nixos/nixpkgs/22.11";
|
||||
inputs.haskellNix.url = "github:simplex-chat/haskell.nix";
|
||||
inputs.nixpkgs.url = "github:angerman/nixpkgs/release-22.11";
|
||||
inputs.haskellNix.url = "github:input-output-hk/haskell.nix/armv7a";
|
||||
inputs.haskellNix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.hackage = {
|
||||
url = "github:input-output-hk/hackage.nix";
|
||||
@@ -12,7 +12,23 @@
|
||||
outputs = { self, haskellNix, nixpkgs, flake-utils, ... }:
|
||||
let systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ]; in
|
||||
flake-utils.lib.eachSystem systems (system:
|
||||
let pkgs = haskellNix.legacyPackages.${system}; in
|
||||
# this android26 overlay makes the pkgsCross.{aarch64-android,armv7a-android-prebuilt} to set stdVer to 26 (Android 8).
|
||||
let android26 = final: prev: {
|
||||
pkgsCross = prev.pkgsCross // {
|
||||
aarch64-android = import prev.path {
|
||||
inherit system;
|
||||
inherit (prev) overlays;
|
||||
crossSystem = prev.lib.systems.examples.aarch64-android // { sdkVer = "26"; };
|
||||
};
|
||||
armv7a-android-prebuilt = import prev.path {
|
||||
inherit system;
|
||||
inherit (prev) overlays;
|
||||
crossSystem = prev.lib.systems.examples.armv7a-android-prebuilt // { sdkVer = "26"; };
|
||||
};
|
||||
};
|
||||
}; in
|
||||
# `appendOverlays` with a singleton is identical to `extend`.
|
||||
let pkgs = haskellNix.legacyPackages.${system}.appendOverlays [android26]; in
|
||||
let drv' = { extra-modules, pkgs', ... }: pkgs'.haskell-nix.project {
|
||||
compiler-nix-name = "ghc8107";
|
||||
index-state = "2023-12-12T00:00:00Z";
|
||||
@@ -81,6 +97,7 @@
|
||||
"x86_64-linux" =
|
||||
let
|
||||
androidPkgs = pkgs.pkgsCross.aarch64-android;
|
||||
android32Pkgs = pkgs.pkgsCross.armv7a-android-prebuilt;
|
||||
# For some reason building libiconv with nixpgks android setup produces
|
||||
# LANGINFO_CODESET to be found, which is not compatible with android sdk 23;
|
||||
# so we'll patch up iconv to not include that.
|
||||
@@ -96,11 +113,32 @@
|
||||
androidFFI = androidPkgs.libffi.overrideAttrs (old: {
|
||||
dontDisableStatic = true;
|
||||
hardeningDisable = [ "fortify" ];
|
||||
postConfigure = ''
|
||||
echo "#undef HAVE_MEMFD_CREATE" >> aarch64-unknown-linux-android/fficonfig.h
|
||||
'';
|
||||
});
|
||||
android32FFI = android32Pkgs.libffi.overrideAttrs (old: {
|
||||
dontDisableStatic = true;
|
||||
hardeningDisable = [ "fortify" ];
|
||||
}
|
||||
);in {
|
||||
"${pkgs.pkgsCross.musl64.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.musl64).simplex-chat.components.exes.simplex-chat;
|
||||
"${pkgs.pkgsCross.musl32.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.musl32).simplex-chat.components.exes.simplex-chat;
|
||||
# "${pkgs.pkgsCross.muslpi.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.muslpi).simplex-chat.components.exes.simplex-chat;
|
||||
"${pkgs.pkgsCross.aarch64-multiplatform-musl.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.aarch64-multiplatform-musl).simplex-chat.components.exes.simplex-chat;
|
||||
"armv7a-android:lib:support" = (drv android32Pkgs).android-support.components.library.override {
|
||||
smallAddressSpace = true; enableShared = false;
|
||||
setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ];
|
||||
postInstall = ''
|
||||
|
||||
mkdir -p $out/_pkg
|
||||
cp libsupport.so $out/_pkg
|
||||
${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsupport.so
|
||||
(cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-armv7a-android-libsupport.zip *)
|
||||
rm -fR $out/_pkg
|
||||
|
||||
mkdir -p $out/nix-support
|
||||
echo "file binary-dist \"$(echo $out/*.zip)\"" \
|
||||
> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
"aarch64-android:lib:support" = (drv androidPkgs).android-support.components.library.override {
|
||||
smallAddressSpace = true; enableShared = false;
|
||||
setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ];
|
||||
@@ -117,6 +155,70 @@
|
||||
> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
"armv7a-android:lib:simplex-chat" = (drv' {
|
||||
pkgs' = android32Pkgs;
|
||||
extra-modules = [{
|
||||
packages.direct-sqlcipher.flags.openssl = true;
|
||||
packages.direct-sqlcipher.components.library.libs = pkgs.lib.mkForce [
|
||||
(android32Pkgs.openssl.override { static = true; enableKTLS = false; })
|
||||
];
|
||||
packages.direct-sqlcipher.patches = [
|
||||
./scripts/nix/direct-sqlcipher-android-log.patch
|
||||
];
|
||||
packages.simplexmq.components.library.libs = pkgs.lib.mkForce [
|
||||
(android32Pkgs.openssl.override { static = true; enableKTLS = false; })
|
||||
];
|
||||
}];
|
||||
}).simplex-chat.components.library.override {
|
||||
smallAddressSpace = true; enableShared = false;
|
||||
# for android we build a shared library, passing these arguments is a bit tricky, as
|
||||
# we want only the threaded rts (HSrts_thr) and ffi to be linked, but not fed into iserv for
|
||||
# template haskell cross compilation. Thus we just pass them as linker options (-optl).
|
||||
setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsimplex.so" "-optl-lHSrts_thr" "-optl-lffi"];
|
||||
postInstall = ''
|
||||
set -x
|
||||
${pkgs.tree}/bin/tree $out
|
||||
mkdir -p $out/_pkg
|
||||
# copy over includes, we might want those, but maybe not.
|
||||
# cp -r $out/lib/*/*/include $out/_pkg/
|
||||
# find the libHS...ghc-X.Y.Z.a static library; this is the
|
||||
# rolled up one with all dependencies included.
|
||||
cp libsimplex.so $out/_pkg
|
||||
# find ./dist -name "lib*.so" -exec cp {} $out/_pkg \;
|
||||
# find ./dist -name "libHS*-ghc*.a" -exec cp {} $out/_pkg \;
|
||||
# find ${android32FFI}/lib -name "*.a" -exec cp {} $out/_pkg \;
|
||||
# find ${android32Pkgs.gmp6.override { withStatic = true; }}/lib -name "*.a" -exec cp {} $out/_pkg \;
|
||||
# find ${androidIconv}/lib -name "*.a" -exec cp {} $out/_pkg \;
|
||||
# find ${android32Pkgs.stdenv.cc.libc}/lib -name "*.a" -exec cp {} $out/_pkg \;
|
||||
echo ${android32Pkgs.openssl.override { enableKTLS = false; }}
|
||||
find ${(android32Pkgs.openssl.override { enableKTLS = false; }).out}/lib -name "*.so" -exec cp {} $out/_pkg \;
|
||||
|
||||
# remove the .1 and other version suffixes from .so's. Androids linker
|
||||
# doesn't play nice with them.
|
||||
for lib in $out/_pkg/*.so; do
|
||||
for dep in $(${pkgs.patchelf}/bin/patchelf --print-needed "$lib"); do
|
||||
if [[ "''${dep##*.so}" ]]; then
|
||||
echo "$lib : $dep -> ''${dep%%.so*}.so"
|
||||
chmod +w "$lib"
|
||||
${pkgs.patchelf}/bin/patchelf --replace-needed "$dep" "''${dep%%.so*}.so" "$lib"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
for lib in $out/_pkg/*.so; do
|
||||
chmod +w "$lib"
|
||||
${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so "$lib"
|
||||
[[ "$lib" != *libsimplex.so ]] && ${pkgs.patchelf}/bin/patchelf --set-soname "$(basename -a $lib)" "$lib"
|
||||
done
|
||||
|
||||
${pkgs.tree}/bin/tree $out/_pkg
|
||||
(cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-armv7a-android-libsimplex.zip *)
|
||||
rm -fR $out/_pkg
|
||||
mkdir -p $out/nix-support
|
||||
echo "file binary-dist \"$(echo $out/*.zip)\"" \
|
||||
> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
"aarch64-android:lib:simplex-chat" = (drv' {
|
||||
pkgs' = androidPkgs;
|
||||
extra-modules = [{
|
||||
@@ -181,110 +283,6 @@
|
||||
> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
"x86_64-android:lib:support" = (drv androidPkgs).android-support.components.library.override {
|
||||
smallAddressSpace = true; enableShared = false;
|
||||
setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ];
|
||||
postInstall = ''
|
||||
|
||||
mkdir -p $out/_pkg
|
||||
cp libsupport.so $out/_pkg
|
||||
${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsupport.so
|
||||
(cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-x86_64-android-libsupport.zip *)
|
||||
rm -fR $out/_pkg
|
||||
|
||||
mkdir -p $out/nix-support
|
||||
echo "file binary-dist \"$(echo $out/*.zip)\"" \
|
||||
> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
"x86_64-android:lib:simplex-chat" = (drv' {
|
||||
pkgs' = androidPkgs;
|
||||
extra-modules = [{
|
||||
packages.direct-sqlcipher.flags.openssl = true;
|
||||
}];
|
||||
}).simplex-chat.components.library.override {
|
||||
smallAddressSpace = true; enableShared = false;
|
||||
# for android we build a shared library, passing these arguments is a bit tricky, as
|
||||
# we want only the threaded rts (HSrts_thr) and ffi to be linked, but not fed into iserv for
|
||||
# template haskell cross compilation. Thus we just pass them as linker options (-optl).
|
||||
setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsimplex.so" "-optl-lHSrts_thr" "-optl-lffi"];
|
||||
postInstall = ''
|
||||
${pkgs.tree}/bin/tree $out
|
||||
mkdir -p $out/_pkg
|
||||
# copy over includes, we might want those, but maybe not.
|
||||
# cp -r $out/lib/*/*/include $out/_pkg/
|
||||
# find the libHS...ghc-X.Y.Z.a static library; this is the
|
||||
# rolled up one with all dependencies included.
|
||||
cp libsimplex.so $out/_pkg
|
||||
# find ./dist -name "lib*.so" -exec cp {} $out/_pkg \;
|
||||
# find ./dist -name "libHS*-ghc*.a" -exec cp {} $out/_pkg \;
|
||||
# find ${androidFFI}/lib -name "*.a" -exec cp {} $out/_pkg \;
|
||||
# find ${androidPkgs.gmp6.override { withStatic = true; }}/lib -name "*.a" -exec cp {} $out/_pkg \;
|
||||
# find ${androidIconv}/lib -name "*.a" -exec cp {} $out/_pkg \;
|
||||
# find ${androidPkgs.stdenv.cc.libc}/lib -name "*.a" -exec cp {} $out/_pkg \;
|
||||
|
||||
${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsimplex.so
|
||||
|
||||
${pkgs.tree}/bin/tree $out/_pkg
|
||||
(cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-x86_64-android-libsimplex.zip *)
|
||||
rm -fR $out/_pkg
|
||||
mkdir -p $out/nix-support
|
||||
echo "file binary-dist \"$(echo $out/*.zip)\"" \
|
||||
> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
"x86_64-linux:lib:support" = (drv androidPkgs).android-support.components.library.override {
|
||||
smallAddressSpace = true; enableShared = false;
|
||||
setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ];
|
||||
postInstall = ''
|
||||
|
||||
mkdir -p $out/_pkg
|
||||
cp libsupport.so $out/_pkg
|
||||
${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsupport.so
|
||||
(cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-x86_64-linux-libsupport.zip *)
|
||||
rm -fR $out/_pkg
|
||||
|
||||
mkdir -p $out/nix-support
|
||||
echo "file binary-dist \"$(echo $out/*.zip)\"" \
|
||||
> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
"x86_64-linux:lib:simplex-chat" = (drv' {
|
||||
pkgs' = androidPkgs;
|
||||
extra-modules = [{
|
||||
packages.direct-sqlcipher.flags.openssl = true;
|
||||
}];
|
||||
}).simplex-chat.components.library.override {
|
||||
smallAddressSpace = true; enableShared = false;
|
||||
# for android we build a shared library, passing these arguments is a bit tricky, as
|
||||
# we want only the threaded rts (HSrts_thr) and ffi to be linked, but not fed into iserv for
|
||||
# template haskell cross compilation. Thus we just pass them as linker options (-optl).
|
||||
setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsimplex.so" "-optl-lHSrts_thr" "-optl-lffi"];
|
||||
postInstall = ''
|
||||
${pkgs.tree}/bin/tree $out
|
||||
mkdir -p $out/_pkg
|
||||
# copy over includes, we might want those, but maybe not.
|
||||
# cp -r $out/lib/*/*/include $out/_pkg/
|
||||
# find the libHS...ghc-X.Y.Z.a static library; this is the
|
||||
# rolled up one with all dependencies included.
|
||||
cp libsimplex.so $out/_pkg
|
||||
# find ./dist -name "lib*.so" -exec cp {} $out/_pkg \;
|
||||
# find ./dist -name "libHS*-ghc*.a" -exec cp {} $out/_pkg \;
|
||||
# find ${androidFFI}/lib -name "*.a" -exec cp {} $out/_pkg \;
|
||||
# find ${androidPkgs.gmp6.override { withStatic = true; }}/lib -name "*.a" -exec cp {} $out/_pkg \;
|
||||
# find ${androidIconv}/lib -name "*.a" -exec cp {} $out/_pkg \;
|
||||
# find ${androidPkgs.stdenv.cc.libc}/lib -name "*.a" -exec cp {} $out/_pkg \;
|
||||
|
||||
${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsimplex.so
|
||||
|
||||
${pkgs.tree}/bin/tree $out/_pkg
|
||||
(cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-x86_64-linux-libsimplex.zip *)
|
||||
rm -fR $out/_pkg
|
||||
mkdir -p $out/nix-support
|
||||
echo "file binary-dist \"$(echo $out/*.zip)\"" \
|
||||
> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# builds for iOS and iOS simulator
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
name: simplex-chat
|
||||
version: 5.4.4.0
|
||||
version: 5.4.2.1
|
||||
#synopsis:
|
||||
#description:
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
@@ -36,7 +36,7 @@ dependencies:
|
||||
- network >= 3.1.2.7 && < 3.2
|
||||
- network-transport == 0.5.6
|
||||
- optparse-applicative >= 0.15 && < 0.17
|
||||
- process == 1.6.*
|
||||
- process >= 1.6 && < 1.6.18
|
||||
- random >= 1.1 && < 1.3
|
||||
- record-hasfield == 1.0.*
|
||||
- simple-logger == 0.1.*
|
||||
|
||||
@@ -108,13 +108,10 @@ build() {
|
||||
|
||||
if [ "$arch" = "armv7a" ] && [ -n "$tag_full" ] ; then
|
||||
git checkout "${tag_version}-armv7a"
|
||||
android_simplex_lib="${folder}#hydraJobs.${arch}-android:lib:simplex-chat.x86_64-linux"
|
||||
android_support_lib="${folder}#hydraJobs.${arch}-android:lib:support.x86_64-linux"
|
||||
else
|
||||
android_simplex_lib="${folder}#hydraJobs.x86_64-linux.${arch}-android:lib:simplex-chat"
|
||||
android_support_lib="${folder}#hydraJobs.x86_64-linux.${arch}-android:lib:support"
|
||||
fi
|
||||
|
||||
android_simplex_lib="${folder}#hydraJobs.${arch}-android:lib:simplex-chat.x86_64-linux"
|
||||
android_support_lib="${folder}#hydraJobs.${arch}-android:lib:support.x86_64-linux"
|
||||
android_simplex_lib_output="${PWD}/result/pkg-${arch}-android-libsimplex.zip"
|
||||
android_support_lib_output="${PWD}/result/pkg-${arch}-android-libsupport.zip"
|
||||
|
||||
|
||||
@@ -37,12 +37,12 @@ for ((i = 0 ; i < ${#arches[@]}; i++)); do
|
||||
|
||||
mkdir -p "$output_dir" 2> /dev/null
|
||||
|
||||
curl --tlsv1.2 --location -o libsupport.zip $job_repo/$arch-android:lib:support.x86_64-linux/latest/download/1 && \
|
||||
curl --location -o libsupport.zip $job_repo/$arch-android:lib:support.x86_64-linux/latest/download/1 && \
|
||||
unzip -o libsupport.zip && \
|
||||
mv libsupport.so "$output_dir" && \
|
||||
rm libsupport.zip
|
||||
|
||||
curl --tlsv1.2 --location -o libsimplex.zip "$job_repo"/"$arch"-android:lib:simplex-chat.x86_64-linux/latest/download/1 && \
|
||||
curl --location -o libsimplex.zip "$job_repo"/"$arch"-android:lib:simplex-chat.x86_64-linux/latest/download/1 && \
|
||||
unzip -o libsimplex.zip && \
|
||||
mv libsimplex.so "$output_dir" && \
|
||||
rm libsimplex.zip
|
||||
|
||||
@@ -37,7 +37,7 @@ cp *imple*.desktop usr/share/applications/
|
||||
cp $multiplatform_dir/desktop/src/jvmMain/resources/distribute/*.appdata.xml usr/share/metainfo
|
||||
|
||||
if [ ! -f ../appimagetool-x86_64.AppImage ]; then
|
||||
wget --secure-protocol=TLSv1_3 https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage -O ../appimagetool-x86_64.AppImage
|
||||
wget https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage -O ../appimagetool-x86_64.AppImage
|
||||
chmod +x ../appimagetool-x86_64.AppImage
|
||||
fi
|
||||
../appimagetool-x86_64.AppImage .
|
||||
|
||||
@@ -12,7 +12,7 @@ cd $root_dir
|
||||
if [ ! -f dist-newstyle/openssl-1.1.1w/libcrypto-1_1-x64.dll ]; then
|
||||
mkdir dist-newstyle 2>/dev/null || true
|
||||
cd dist-newstyle
|
||||
curl --tlsv1.2 https://www.openssl.org/source/openssl-1.1.1w.tar.gz -o openssl.tar.gz
|
||||
curl https://www.openssl.org/source/openssl-1.1.1w.tar.gz -o openssl.tar.gz
|
||||
$WINDIR\\System32\\tar.exe -xvzf openssl.tar.gz
|
||||
cd openssl-1.1.1w
|
||||
./Configure mingw64
|
||||
|
||||
@@ -14,7 +14,7 @@ mkdir $vlc_dir || exit 0
|
||||
cd /tmp
|
||||
mkdir tmp 2>/dev/null || true
|
||||
cd tmp
|
||||
curl --tlsv1.2 https://github.com/cmatomic/VLCplayer-AppImage/releases/download/3.0.11.1/VLC_media_player-3.0.11.1-x86_64.AppImage -L -o appimage
|
||||
curl https://github.com/cmatomic/VLCplayer-AppImage/releases/download/3.0.11.1/VLC_media_player-3.0.11.1-x86_64.AppImage -L -o appimage
|
||||
chmod +x appimage
|
||||
./appimage --appimage-extract
|
||||
cp -r squashfs-root/usr/lib/* $vlc_dir
|
||||
@@ -28,7 +28,7 @@ cd /tmp
|
||||
(
|
||||
mkdir tmp
|
||||
cd tmp
|
||||
curl --tlsv1.2 https://archive.ubuntu.com/ubuntu/pool/universe/v/vlc/libvlc5_3.0.9.2-1_amd64.deb -o libvlc
|
||||
curl http://archive.ubuntu.com/ubuntu/pool/universe/v/vlc/libvlc5_3.0.9.2-1_amd64.deb -o libvlc
|
||||
ar p libvlc data.tar.xz > data.tar.xz
|
||||
tar -xvf data.tar.xz
|
||||
mv usr/lib/x86_64-linux-gnu/libvlc.so{.5,}
|
||||
@@ -40,7 +40,7 @@ rm -rf tmp
|
||||
(
|
||||
mkdir tmp
|
||||
cd tmp
|
||||
curl --tlsv1.2 https://archive.ubuntu.com/ubuntu/pool/universe/v/vlc/libvlccore9_3.0.9.2-1_amd64.deb -o libvlccore
|
||||
curl http://archive.ubuntu.com/ubuntu/pool/universe/v/vlc/libvlccore9_3.0.9.2-1_amd64.deb -o libvlccore
|
||||
ar p libvlccore data.tar.xz > data.tar.xz
|
||||
tar -xvf data.tar.xz
|
||||
cp usr/lib/x86_64-linux-gnu/libvlccore.so* $vlc_dir
|
||||
@@ -51,7 +51,7 @@ rm -rf tmp
|
||||
(
|
||||
mkdir tmp
|
||||
cd tmp
|
||||
curl --tlsv1.2 https://mirrors.edge.kernel.org/ubuntu/pool/universe/v/vlc/vlc-plugin-base_3.0.9.2-1_amd64.deb -o plugins
|
||||
curl http://mirrors.edge.kernel.org/ubuntu/pool/universe/v/vlc/vlc-plugin-base_3.0.9.2-1_amd64.deb -o plugins
|
||||
ar p plugins data.tar.xz > data.tar.xz
|
||||
tar -xvf data.tar.xz
|
||||
find usr/lib/x86_64-linux-gnu/vlc/plugins/ -name "lib*.so*" -exec patchelf --set-rpath '$ORIGIN/../../' {} \;
|
||||
@@ -63,7 +63,7 @@ rm -rf tmp
|
||||
(
|
||||
mkdir tmp
|
||||
cd tmp
|
||||
curl --tlsv1.2 https://archive.ubuntu.com/ubuntu/pool/main/libi/libidn/libidn11_1.33-2.2ubuntu2_amd64.deb -o idn
|
||||
curl http://archive.ubuntu.com/ubuntu/pool/main/libi/libidn/libidn11_1.33-2.2ubuntu2_amd64.deb -o idn
|
||||
ar p idn data.tar.xz > data.tar.xz
|
||||
tar -xvf data.tar.xz
|
||||
cp lib/x86_64-linux-gnu/lib* $vlc_dir
|
||||
|
||||
@@ -23,7 +23,7 @@ mkdir -p $vlc_dir/vlc || exit 0
|
||||
cd /tmp
|
||||
mkdir tmp 2>/dev/null || true
|
||||
cd tmp
|
||||
curl --tlsv1.2 https://github.com/simplex-chat/vlc/releases/download/v$vlc_version/vlc-macos-$ARCH.zip -L -o vlc
|
||||
curl https://github.com/simplex-chat/vlc/releases/download/v$vlc_version/vlc-macos-$ARCH.zip -L -o vlc
|
||||
unzip -oqq vlc
|
||||
install_name_tool -add_rpath "@loader_path/VLC.app/Contents/MacOS/lib" vlc-cache-gen
|
||||
cd VLC.app/Contents/MacOS/lib
|
||||
|
||||
@@ -13,7 +13,7 @@ mkdir -p $vlc_dir/vlc || exit 0
|
||||
cd /tmp
|
||||
mkdir tmp 2>/dev/null || true
|
||||
cd tmp
|
||||
curl --tlsv1.2 https://irltoolkit.mm.fcix.net/videolan-ftp/vlc/3.0.18/win64/vlc-3.0.18-win64.zip -L -o vlc
|
||||
curl https://irltoolkit.mm.fcix.net/videolan-ftp/vlc/3.0.18/win64/vlc-3.0.18-win64.zip -L -o vlc
|
||||
$WINDIR\\System32\\tar.exe -xf vlc
|
||||
cd vlc-*
|
||||
# Setting the same date as the date that will be on the file after extraction from JAR to make VLC cache checker happy
|
||||
|
||||
@@ -35,7 +35,7 @@ for ((i = 0 ; i < ${#arches[@]}; i++)); do
|
||||
output_arch="${output_arches[$i]}"
|
||||
output_dir="$HOME/Downloads"
|
||||
|
||||
curl --tlsv1.2 --location -o "$output_dir"/pkg-ios-"$arch"-swift-json.zip "$job_repo"/"$arch"-darwin-ios:lib:simplex-chat."$arch"-darwin/latest/download/1 && \
|
||||
curl --location -o "$output_dir"/pkg-ios-"$arch"-swift-json.zip "$job_repo"/"$arch"-darwin-ios:lib:simplex-chat."$arch"-darwin/latest/download/1 && \
|
||||
unzip -o "$output_dir"/pkg-ios-"$output_arch"-swift-json.zip -d ~/Downloads/pkg-ios-"$output_arch"-swift-json
|
||||
done
|
||||
sh "$root_dir"/scripts/ios/prepare-x86_64.sh
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"https://github.com/simplex-chat/simplexmq.git"."f6ed4640d407f8879273d104a3e69069806dcb7c" = "072rakv697f85i8ldjl7bj7jc7vfmzphasx2i4ynwgz3kksydfp5";
|
||||
"https://github.com/simplex-chat/simplexmq.git"."d0588bd0ac23a459cbfc9a4789633014e91ffa19" = "0b17qy74capb0jyli8f3pg1xi4aawhcgpmaz2ykl9g3605png1na";
|
||||
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
|
||||
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
|
||||
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
|
||||
|
||||
+8
-8
@@ -5,7 +5,7 @@ cabal-version: 1.12
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: simplex-chat
|
||||
version: 5.4.4.0
|
||||
version: 5.4.2.1
|
||||
category: Web, System, Services, Cryptography
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
author: simplex.chat
|
||||
@@ -192,7 +192,7 @@ library
|
||||
, network >=3.1.2.7 && <3.2
|
||||
, network-transport ==0.5.6
|
||||
, optparse-applicative >=0.15 && <0.17
|
||||
, process ==1.6.*
|
||||
, process >=1.6 && <1.6.18
|
||||
, random >=1.1 && <1.3
|
||||
, record-hasfield ==1.0.*
|
||||
, simple-logger ==0.1.*
|
||||
@@ -251,7 +251,7 @@ executable simplex-bot
|
||||
, network >=3.1.2.7 && <3.2
|
||||
, network-transport ==0.5.6
|
||||
, optparse-applicative >=0.15 && <0.17
|
||||
, process ==1.6.*
|
||||
, process >=1.6 && <1.6.18
|
||||
, random >=1.1 && <1.3
|
||||
, record-hasfield ==1.0.*
|
||||
, simple-logger ==0.1.*
|
||||
@@ -311,7 +311,7 @@ executable simplex-bot-advanced
|
||||
, network >=3.1.2.7 && <3.2
|
||||
, network-transport ==0.5.6
|
||||
, optparse-applicative >=0.15 && <0.17
|
||||
, process ==1.6.*
|
||||
, process >=1.6 && <1.6.18
|
||||
, random >=1.1 && <1.3
|
||||
, record-hasfield ==1.0.*
|
||||
, simple-logger ==0.1.*
|
||||
@@ -373,7 +373,7 @@ executable simplex-broadcast-bot
|
||||
, network >=3.1.2.7 && <3.2
|
||||
, network-transport ==0.5.6
|
||||
, optparse-applicative >=0.15 && <0.17
|
||||
, process ==1.6.*
|
||||
, process >=1.6 && <1.6.18
|
||||
, random >=1.1 && <1.3
|
||||
, record-hasfield ==1.0.*
|
||||
, simple-logger ==0.1.*
|
||||
@@ -434,7 +434,7 @@ executable simplex-chat
|
||||
, network ==3.1.*
|
||||
, network-transport ==0.5.6
|
||||
, optparse-applicative >=0.15 && <0.17
|
||||
, process ==1.6.*
|
||||
, process >=1.6 && <1.6.18
|
||||
, random >=1.1 && <1.3
|
||||
, record-hasfield ==1.0.*
|
||||
, simple-logger ==0.1.*
|
||||
@@ -500,7 +500,7 @@ executable simplex-directory-service
|
||||
, network >=3.1.2.7 && <3.2
|
||||
, network-transport ==0.5.6
|
||||
, optparse-applicative >=0.15 && <0.17
|
||||
, process ==1.6.*
|
||||
, process >=1.6 && <1.6.18
|
||||
, random >=1.1 && <1.3
|
||||
, record-hasfield ==1.0.*
|
||||
, simple-logger ==0.1.*
|
||||
@@ -594,7 +594,7 @@ test-suite simplex-chat-test
|
||||
, network ==3.1.*
|
||||
, network-transport ==0.5.6
|
||||
, optparse-applicative >=0.15 && <0.17
|
||||
, process ==1.6.*
|
||||
, process >=1.6 && <1.6.18
|
||||
, random >=1.1 && <1.3
|
||||
, record-hasfield ==1.0.*
|
||||
, silently ==1.2.*
|
||||
|
||||
+26
-37
@@ -28,6 +28,7 @@ import Data.Bifunctor (bimap, first)
|
||||
import Data.ByteArray (ScrubbedBytes)
|
||||
import qualified Data.ByteArray as BA
|
||||
import qualified Data.ByteString.Base64 as B64
|
||||
import Data.ByteString.Builder (toLazyByteString)
|
||||
import Data.ByteString.Char8 (ByteString)
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import qualified Data.ByteString.Lazy.Char8 as LB
|
||||
@@ -232,7 +233,6 @@ newChatController
|
||||
expireCIFlags <- newTVarIO M.empty
|
||||
cleanupManagerAsync <- newTVarIO Nothing
|
||||
timedItemThreads <- atomically TM.empty
|
||||
chatActivated <- newTVarIO True
|
||||
showLiveItems <- newTVarIO False
|
||||
encryptLocalFiles <- newTVarIO False
|
||||
userXFTPFileConfig <- newTVarIO $ xftpFileConfig cfg
|
||||
@@ -268,7 +268,6 @@ newChatController
|
||||
expireCIFlags,
|
||||
cleanupManagerAsync,
|
||||
timedItemThreads,
|
||||
chatActivated,
|
||||
showLiveItems,
|
||||
encryptLocalFiles,
|
||||
userXFTPFileConfig,
|
||||
@@ -311,10 +310,10 @@ cfgServers = \case
|
||||
SPSMP -> smp
|
||||
SPXFTP -> xftp
|
||||
|
||||
startChatController :: forall m. ChatMonad' m => Bool -> m (Async ())
|
||||
startChatController mainApp = do
|
||||
startChatController :: forall m. ChatMonad' m => Bool -> Bool -> Bool -> m (Async ())
|
||||
startChatController subConns enableExpireCIs startXFTPWorkers = do
|
||||
asks smpAgent >>= resumeAgentClient
|
||||
unless mainApp $
|
||||
unless subConns $
|
||||
chatWriteVar subscriptionMode SMOnlyCreate
|
||||
users <- fromRight [] <$> runExceptT (withStoreCtx' (Just "startChatController, getUsers") getUsers)
|
||||
restoreCalls
|
||||
@@ -324,15 +323,15 @@ startChatController mainApp = do
|
||||
start s users = do
|
||||
a1 <- async agentSubscriber
|
||||
a2 <-
|
||||
if mainApp
|
||||
if subConns
|
||||
then Just <$> async (subscribeUsers False users)
|
||||
else pure Nothing
|
||||
atomically . writeTVar s $ Just (a1, a2)
|
||||
when mainApp $ do
|
||||
when startXFTPWorkers $ do
|
||||
startXFTP
|
||||
void $ forkIO $ startFilesToReceive users
|
||||
startCleanupManager
|
||||
startExpireCIs users
|
||||
startCleanupManager
|
||||
when enableExpireCIs $ startExpireCIs users
|
||||
pure a1
|
||||
startXFTP = do
|
||||
tmp <- readTVarIO =<< asks tempDirectory
|
||||
@@ -544,17 +543,16 @@ processChatCommand' vr = \case
|
||||
checkDeleteChatUser user'
|
||||
withChatLock "deleteUser" . procCmd $ deleteChatUser user' delSMPQueues
|
||||
DeleteUser uName delSMPQueues viewPwd_ -> withUserName uName $ \userId -> APIDeleteUser userId delSMPQueues viewPwd_
|
||||
StartChat mainApp -> withUser' $ \_ ->
|
||||
StartChat subConns enableExpireCIs startXFTPWorkers -> withUser' $ \_ ->
|
||||
asks agentAsync >>= readTVarIO >>= \case
|
||||
Just _ -> pure CRChatRunning
|
||||
_ -> checkStoreNotChanged $ startChatController mainApp $> CRChatStarted
|
||||
_ -> checkStoreNotChanged $ startChatController subConns enableExpireCIs startXFTPWorkers $> CRChatStarted
|
||||
APIStopChat -> do
|
||||
ask >>= stopChatController
|
||||
pure CRChatStopped
|
||||
APIActivateChat restoreChat -> withUser $ \_ -> do
|
||||
when restoreChat restoreCalls
|
||||
withAgent foregroundAgent
|
||||
chatWriteVar chatActivated True
|
||||
when restoreChat $ do
|
||||
users <- withStoreCtx' (Just "APIActivateChat, getUsers") getUsers
|
||||
void . forkIO $ subscribeUsers True users
|
||||
@@ -562,7 +560,6 @@ processChatCommand' vr = \case
|
||||
setAllExpireCIFlags True
|
||||
ok_
|
||||
APISuspendChat t -> do
|
||||
chatWriteVar chatActivated False
|
||||
setAllExpireCIFlags False
|
||||
stopRemoteCtrl
|
||||
withAgent (`suspendAgent` t)
|
||||
@@ -2483,7 +2480,6 @@ startExpireCIThread user@User {userId} = do
|
||||
flip catchChatError (toView . CRChatError (Just user)) $ do
|
||||
expireFlags <- asks expireCIFlags
|
||||
atomically $ TM.lookup userId expireFlags >>= \b -> unless (b == Just True) retry
|
||||
waitChatStartedAndActivated
|
||||
ttl <- withStoreCtx' (Just "startExpireCIThread, getChatItemTTL") (`getChatItemTTL` user)
|
||||
forM_ ttl $ \t -> expireChatItems user t False
|
||||
liftIO $ threadDelay' interval
|
||||
@@ -2977,7 +2973,7 @@ cleanupManager = do
|
||||
stepDelay <- asks (cleanupManagerStepDelay . config)
|
||||
forever $ do
|
||||
flip catchChatError (toView . CRChatError Nothing) $ do
|
||||
waitChatStartedAndActivated
|
||||
waitChatStarted
|
||||
users <- withStoreCtx' (Just "cleanupManager, getUsers 1") getUsers
|
||||
let (us, us') = partition activeUser users
|
||||
forM_ us $ cleanupUser interval stepDelay
|
||||
@@ -2987,7 +2983,7 @@ cleanupManager = do
|
||||
liftIO $ threadDelay' $ diffToMicroseconds interval
|
||||
where
|
||||
runWithoutInitialDelay cleanupInterval = flip catchChatError (toView . CRChatError Nothing) $ do
|
||||
waitChatStartedAndActivated
|
||||
waitChatStarted
|
||||
users <- withStoreCtx' (Just "cleanupManager, getUsers 2") getUsers
|
||||
let (us, us') = partition activeUser users
|
||||
forM_ us $ \u -> cleanupTimedItems cleanupInterval u `catchChatError` (toView . CRChatError (Just u))
|
||||
@@ -3042,7 +3038,7 @@ deleteTimedItem :: ChatMonad m => User -> (ChatRef, ChatItemId) -> UTCTime -> m
|
||||
deleteTimedItem user (ChatRef cType chatId, itemId) deleteAt = do
|
||||
ts <- liftIO getCurrentTime
|
||||
liftIO $ threadDelay' $ diffToMicroseconds $ diffUTCTime deleteAt ts
|
||||
waitChatStartedAndActivated
|
||||
waitChatStarted
|
||||
vr <- chatVersionRange
|
||||
case cType of
|
||||
CTDirect -> do
|
||||
@@ -3068,10 +3064,8 @@ expireChatItems user@User {userId} ttl sync = do
|
||||
let expirationDate = addUTCTime (-1 * fromIntegral ttl) currentTs
|
||||
-- this is to keep group messages created during last 12 hours even if they're expired according to item_ts
|
||||
createdAtCutoff = addUTCTime (-43200 :: NominalDiffTime) currentTs
|
||||
waitChatStartedAndActivated
|
||||
contacts <- withStoreCtx' (Just "expireChatItems, getUserContacts") (`getUserContacts` user)
|
||||
loop contacts $ processContact expirationDate
|
||||
waitChatStartedAndActivated
|
||||
groups <- withStoreCtx' (Just "expireChatItems, getUserGroupDetails") (\db -> getUserGroupDetails db vr user Nothing Nothing)
|
||||
loop groups $ processGroup expirationDate createdAtCutoff
|
||||
where
|
||||
@@ -3090,13 +3084,11 @@ expireChatItems user@User {userId} ttl sync = do
|
||||
when (expire == Just True) $ threadDelay 100000 >> a
|
||||
processContact :: UTCTime -> Contact -> m ()
|
||||
processContact expirationDate ct = do
|
||||
waitChatStartedAndActivated
|
||||
filesInfo <- withStoreCtx' (Just "processContact, getContactExpiredFileInfo") $ \db -> getContactExpiredFileInfo db user ct expirationDate
|
||||
deleteFilesAndConns user filesInfo
|
||||
withStoreCtx' (Just "processContact, deleteContactExpiredCIs") $ \db -> deleteContactExpiredCIs db user ct expirationDate
|
||||
processGroup :: UTCTime -> UTCTime -> GroupInfo -> m ()
|
||||
processGroup expirationDate createdAtCutoff gInfo = do
|
||||
waitChatStartedAndActivated
|
||||
filesInfo <- withStoreCtx' (Just "processGroup, getGroupExpiredFileInfo") $ \db -> getGroupExpiredFileInfo db user gInfo expirationDate createdAtCutoff
|
||||
deleteFilesAndConns user filesInfo
|
||||
withStoreCtx' (Just "processGroup, deleteGroupExpiredCIs") $ \db -> deleteGroupExpiredCIs db user gInfo expirationDate createdAtCutoff
|
||||
@@ -5656,7 +5648,8 @@ sendGroupMemberMessages user conn@Connection {connId} events groupId = do
|
||||
processBatch batch `catchChatError` (toView . CRChatError (Just user))
|
||||
where
|
||||
processBatch :: MsgBatch -> m ()
|
||||
processBatch (MsgBatch batchBody sndMsgs) = do
|
||||
processBatch (MsgBatch builder sndMsgs) = do
|
||||
let batchBody = LB.toStrict $ toLazyByteString builder
|
||||
agentMsgId <- withAgent $ \a -> sendMessage a (aConnId conn) MsgFlags {notification = True} batchBody
|
||||
let sndMsgDelivery = SndMsgDelivery {connId, agentMsgId}
|
||||
void . withStoreBatch' $ \db -> map (\SndMessage {msgId} -> createSndMsgDelivery db sndMsgDelivery msgId) sndMsgs
|
||||
@@ -5676,28 +5669,28 @@ directMessage chatMsgEvent = do
|
||||
chatVRange <- chatVersionRange
|
||||
let r = encodeChatMessage ChatMessage {chatVRange, msgId = Nothing, chatMsgEvent}
|
||||
case r of
|
||||
ECMEncoded encodedBody -> pure encodedBody
|
||||
ECMEncoded encodedBody -> pure . LB.toStrict $ encodedBody
|
||||
ECMLarge -> throwChatError $ CEException "large message"
|
||||
|
||||
deliverMessage :: ChatMonad m => Connection -> CMEventTag e -> MsgBody -> MessageId -> m Int64
|
||||
deliverMessage :: ChatMonad m => Connection -> CMEventTag e -> LazyMsgBody -> MessageId -> m Int64
|
||||
deliverMessage conn cmEventTag msgBody msgId = do
|
||||
let msgFlags = MsgFlags {notification = hasNotification cmEventTag}
|
||||
deliverMessage' conn msgFlags msgBody msgId
|
||||
|
||||
deliverMessage' :: ChatMonad m => Connection -> MsgFlags -> MsgBody -> MessageId -> m Int64
|
||||
deliverMessage' :: ChatMonad m => Connection -> MsgFlags -> LazyMsgBody -> MessageId -> m Int64
|
||||
deliverMessage' conn msgFlags msgBody msgId =
|
||||
deliverMessages [(conn, msgFlags, msgBody, msgId)] >>= \case
|
||||
[r] -> liftEither r
|
||||
rs -> throwChatError $ CEInternalError $ "deliverMessage: expected 1 result, got " <> show (length rs)
|
||||
|
||||
deliverMessages :: ChatMonad' m => [(Connection, MsgFlags, MsgBody, MessageId)] -> m [Either ChatError Int64]
|
||||
deliverMessages :: ChatMonad' m => [(Connection, MsgFlags, LazyMsgBody, MessageId)] -> m [Either ChatError Int64]
|
||||
deliverMessages msgReqs = do
|
||||
sent <- zipWith prepareBatch msgReqs <$> withAgent' (`sendMessages` aReqs)
|
||||
withStoreBatch $ \db -> map (bindRight $ createDelivery db) sent
|
||||
where
|
||||
aReqs = map (\(conn, msgFlags, msgBody, _msgId) -> (aConnId conn, msgFlags, msgBody)) msgReqs
|
||||
aReqs = map (\(conn, msgFlags, msgBody, _msgId) -> (aConnId conn, msgFlags, LB.toStrict msgBody)) msgReqs
|
||||
prepareBatch req = bimap (`ChatErrorAgent` Nothing) (req,)
|
||||
createDelivery :: DB.Connection -> ((Connection, MsgFlags, MsgBody, MessageId), AgentMsgId) -> IO (Either ChatError Int64)
|
||||
createDelivery :: DB.Connection -> ((Connection, MsgFlags, LazyMsgBody, MessageId), AgentMsgId) -> IO (Either ChatError Int64)
|
||||
createDelivery db ((Connection {connId}, _, _, msgId), agentMsgId) =
|
||||
Right <$> createSndMsgDelivery db (SndMsgDelivery {connId, agentMsgId}) msgId
|
||||
|
||||
@@ -6121,14 +6114,10 @@ checkSameUser userId User {userId = activeUserId} = when (userId /= activeUserId
|
||||
chatStarted :: ChatMonad m => m Bool
|
||||
chatStarted = fmap isJust . readTVarIO =<< asks agentAsync
|
||||
|
||||
waitChatStartedAndActivated :: ChatMonad m => m ()
|
||||
waitChatStartedAndActivated = do
|
||||
waitChatStarted :: ChatMonad m => m ()
|
||||
waitChatStarted = do
|
||||
agentStarted <- asks agentAsync
|
||||
chatActivated <- asks chatActivated
|
||||
atomically $ do
|
||||
started <- readTVar agentStarted
|
||||
activated <- readTVar chatActivated
|
||||
unless (isJust started && activated) retry
|
||||
atomically $ readTVar agentStarted >>= \a -> unless (isJust a) retry
|
||||
|
||||
chatVersionRange :: ChatMonad' m => m VersionRange
|
||||
chatVersionRange = do
|
||||
@@ -6165,8 +6154,8 @@ chatCommandP =
|
||||
"/_delete user " *> (APIDeleteUser <$> A.decimal <* " del_smp=" <*> onOffP <*> optional (A.space *> jsonP)),
|
||||
"/delete user " *> (DeleteUser <$> displayName <*> pure True <*> optional (A.space *> pwdP)),
|
||||
("/user" <|> "/u") $> ShowActiveUser,
|
||||
"/_start main=" *> (StartChat <$> onOffP),
|
||||
"/_start" $> StartChat True,
|
||||
"/_start subscribe=" *> (StartChat <$> onOffP <* " expire=" <*> onOffP <* " xftp=" <*> onOffP),
|
||||
"/_start" $> StartChat True True True,
|
||||
"/_stop" $> APIStopChat,
|
||||
"/_app activate restore=" *> (APIActivateChat <$> onOffP),
|
||||
"/_app activate" $> APIActivateChat True,
|
||||
|
||||
@@ -200,7 +200,6 @@ data ChatController = ChatController
|
||||
expireCIThreads :: TMap UserId (Maybe (Async ())),
|
||||
expireCIFlags :: TMap UserId Bool,
|
||||
cleanupManagerAsync :: TVar (Maybe (Async ())),
|
||||
chatActivated :: TVar Bool,
|
||||
timedItemThreads :: TMap (ChatRef, ChatItemId) (TVar (Maybe (Weak ThreadId))),
|
||||
showLiveItems :: TVar Bool,
|
||||
encryptLocalFiles :: TVar Bool,
|
||||
@@ -234,7 +233,7 @@ data ChatCommand
|
||||
| UnmuteUser
|
||||
| APIDeleteUser UserId Bool (Maybe UserPwd)
|
||||
| DeleteUser UserName Bool (Maybe UserPwd)
|
||||
| StartChat {mainApp :: Bool}
|
||||
| StartChat {subscribeConnections :: Bool, enableExpireChatItems :: Bool, startXFTPWorkers :: Bool}
|
||||
| APIStopChat
|
||||
| APIActivateChat {restoreChat :: Bool}
|
||||
| APISuspendChat {suspendTimeout :: Int}
|
||||
|
||||
@@ -35,7 +35,7 @@ runSimplexChat :: ChatOpts -> User -> ChatController -> (User -> ChatController
|
||||
runSimplexChat ChatOpts {maintenance} u cc chat
|
||||
| maintenance = wait =<< async (chat u cc)
|
||||
| otherwise = do
|
||||
a1 <- runReaderT (startChatController True) cc
|
||||
a1 <- runReaderT (startChatController True True True) cc
|
||||
a2 <- async $ chat u cc
|
||||
waitEither_ a1 a2
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import qualified Data.Aeson.Encoding as JE
|
||||
import qualified Data.Aeson.TH as JQ
|
||||
import qualified Data.Attoparsec.ByteString.Char8 as A
|
||||
import qualified Data.ByteString.Base64 as B64
|
||||
import qualified Data.ByteString.Lazy as L
|
||||
import qualified Data.ByteString.Lazy.Char8 as LB
|
||||
import Data.Char (isSpace)
|
||||
import Data.Int (Int64)
|
||||
@@ -762,10 +763,12 @@ checkChatType x = case testEquality (chatTypeI @c) (chatTypeI @c') of
|
||||
Just Refl -> Right x
|
||||
Nothing -> Left "bad chat type"
|
||||
|
||||
type LazyMsgBody = L.ByteString
|
||||
|
||||
data SndMessage = SndMessage
|
||||
{ msgId :: MessageId,
|
||||
sharedMsgId :: SharedMsgId,
|
||||
msgBody :: MsgBody
|
||||
msgBody :: LazyMsgBody
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
@@ -787,7 +790,7 @@ data RcvMessage = RcvMessage
|
||||
data PendingGroupMessage = PendingGroupMessage
|
||||
{ msgId :: MessageId,
|
||||
cmEventTag :: ACMEventTag,
|
||||
msgBody :: MsgBody,
|
||||
msgBody :: LazyMsgBody,
|
||||
introId_ :: Maybe Int64
|
||||
}
|
||||
|
||||
|
||||
@@ -1,36 +1,40 @@
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
|
||||
module Simplex.Chat.Messages.Batch (
|
||||
MsgBatch (..),
|
||||
batchMessages,
|
||||
) where
|
||||
module Simplex.Chat.Messages.Batch
|
||||
( MsgBatch (..),
|
||||
batchMessages,
|
||||
)
|
||||
where
|
||||
|
||||
import Data.ByteString.Char8 (ByteString)
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import Data.ByteString.Builder (Builder, charUtf8, lazyByteString)
|
||||
import qualified Data.ByteString.Lazy as LB
|
||||
import Data.Int (Int64)
|
||||
import Simplex.Chat.Controller (ChatError (..), ChatErrorType (..))
|
||||
import Simplex.Chat.Messages
|
||||
|
||||
data MsgBatch = MsgBatch ByteString [SndMessage]
|
||||
data MsgBatch = MsgBatch Builder [SndMessage]
|
||||
|
||||
-- | Batches [SndMessage] into batches of ByteStrings in form of JSON arrays.
|
||||
-- | Batches [SndMessage] into batches of ByteString builders in form of JSON arrays.
|
||||
-- Does not check if the resulting batch is a valid JSON.
|
||||
-- If a single element is passed, it is returned as is (a JSON string).
|
||||
-- If an element exceeds maxLen, it is returned as ChatError.
|
||||
batchMessages :: Int -> [SndMessage] -> [Either ChatError MsgBatch]
|
||||
batchMessages maxLen = addBatch . foldr addToBatch ([], [], 0, 0)
|
||||
batchMessages :: Int64 -> [SndMessage] -> [Either ChatError MsgBatch]
|
||||
batchMessages maxLen msgs =
|
||||
let (batches, batch, _, n) = foldr addToBatch ([], [], 0, 0) msgs
|
||||
in if n == 0 then batches else msgBatch batch : batches
|
||||
where
|
||||
msgBatch batch = Right (MsgBatch (encodeMessages batch) batch)
|
||||
addToBatch :: SndMessage -> ([Either ChatError MsgBatch], [SndMessage], Int, Int) -> ([Either ChatError MsgBatch], [SndMessage], Int, Int)
|
||||
addToBatch msg@SndMessage {msgBody} acc@(batches, batch, len, n)
|
||||
addToBatch :: SndMessage -> ([Either ChatError MsgBatch], [SndMessage], Int64, Int) -> ([Either ChatError MsgBatch], [SndMessage], Int64, Int)
|
||||
addToBatch msg@SndMessage {msgBody} (batches, batch, len, n)
|
||||
| batchLen <= maxLen = (batches, msg : batch, len', n + 1)
|
||||
| msgLen <= maxLen = (addBatch acc, [msg], msgLen, 1)
|
||||
| otherwise = (errLarge msg : addBatch acc, [], 0, 0)
|
||||
| msgLen <= maxLen = (batches', [msg], msgLen, 1)
|
||||
| otherwise = (errLarge msg : (if n == 0 then batches else batches'), [], 0, 0)
|
||||
where
|
||||
msgLen = B.length msgBody
|
||||
msgLen = LB.length msgBody
|
||||
batches' = msgBatch batch : batches
|
||||
len'
|
||||
| n == 0 = msgLen
|
||||
| otherwise = msgLen + len + 1 -- 1 accounts for comma
|
||||
@@ -38,11 +42,11 @@ batchMessages maxLen = addBatch . foldr addToBatch ([], [], 0, 0)
|
||||
| n == 0 = len'
|
||||
| otherwise = len' + 2 -- 2 accounts for opening and closing brackets
|
||||
errLarge SndMessage {msgId} = Left $ ChatError $ CEInternalError ("large message " <> show msgId)
|
||||
addBatch :: ([Either ChatError MsgBatch], [SndMessage], Int, Int) -> [Either ChatError MsgBatch]
|
||||
addBatch (batches, batch, _, n) = if n == 0 then batches else msgBatch batch : batches
|
||||
encodeMessages :: [SndMessage] -> ByteString
|
||||
encodeMessages = \case
|
||||
[] -> mempty
|
||||
[msg] -> body msg
|
||||
msgs -> B.concat ["[", B.intercalate "," (map body msgs), "]"]
|
||||
body SndMessage {msgBody} = msgBody
|
||||
|
||||
encodeMessages :: [SndMessage] -> Builder
|
||||
encodeMessages = \case
|
||||
[] -> mempty
|
||||
[msg] -> encodeMsg msg
|
||||
(msg : msgs) -> charUtf8 '[' <> encodeMsg msg <> mconcat [charUtf8 ',' <> encodeMsg msg' | msg' <- msgs] <> charUtf8 ']'
|
||||
where
|
||||
encodeMsg SndMessage {msgBody} = lazyByteString msgBody
|
||||
|
||||
@@ -29,7 +29,9 @@ import qualified Data.Attoparsec.ByteString.Char8 as A
|
||||
import Data.ByteString.Char8 (ByteString)
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import Data.ByteString.Internal (c2w, w2c)
|
||||
import qualified Data.ByteString.Lazy as L
|
||||
import qualified Data.ByteString.Lazy.Char8 as LB
|
||||
import Data.Int (Int64)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.String
|
||||
import Data.Text (Text)
|
||||
@@ -489,20 +491,20 @@ $(JQ.deriveJSON defaultJSON ''QuotedMsg)
|
||||
|
||||
-- this limit reserves space for metadata in forwarded messages
|
||||
-- 15780 (limit used for fileChunkSize) - 161 (x.grp.msg.forward overhead) = 15619, round to 15610
|
||||
maxChatMsgSize :: Int
|
||||
maxChatMsgSize :: Int64
|
||||
maxChatMsgSize = 15610
|
||||
|
||||
data EncodedChatMessage = ECMEncoded ByteString | ECMLarge
|
||||
data EncodedChatMessage = ECMEncoded L.ByteString | ECMLarge
|
||||
|
||||
encodeChatMessage :: MsgEncodingI e => ChatMessage e -> EncodedChatMessage
|
||||
encodeChatMessage msg = do
|
||||
case chatToAppMessage msg of
|
||||
AMJson m -> do
|
||||
let body = LB.toStrict $ J.encode m
|
||||
if B.length body > maxChatMsgSize
|
||||
let body = J.encode m
|
||||
if LB.length body > maxChatMsgSize
|
||||
then ECMLarge
|
||||
else ECMEncoded body
|
||||
AMBinary m -> ECMEncoded $ strEncode m
|
||||
AMBinary m -> ECMEncoded . LB.fromStrict $ strEncode m
|
||||
|
||||
parseChatMessages :: ByteString -> [Either String AChatMessage]
|
||||
parseChatMessages "" = [Left "empty string"]
|
||||
|
||||
@@ -413,7 +413,6 @@ xftpServerConfig =
|
||||
logStatsStartTime = 0,
|
||||
serverStatsLogFile = "tests/tmp/xftp-server-stats.daily.log",
|
||||
serverStatsBackupFile = Nothing,
|
||||
controlPort = Nothing,
|
||||
transportConfig = defaultTransportServerConfig
|
||||
}
|
||||
|
||||
|
||||
@@ -1149,7 +1149,7 @@ testSubscribeAppNSE tmp =
|
||||
alice ##> "/_app suspend 1"
|
||||
alice <## "ok"
|
||||
alice <## "chat suspended"
|
||||
nseAlice ##> "/_start main=off"
|
||||
nseAlice ##> "/_start subscribe=off expire=off xftp=off"
|
||||
nseAlice <## "chat started"
|
||||
nseAlice ##> "/ad"
|
||||
cLink <- getContactLink nseAlice True
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
module MessageBatching (batchingTests) where
|
||||
|
||||
import Crypto.Number.Serialize (os2ip)
|
||||
import Data.ByteString (ByteString)
|
||||
import qualified Data.ByteString as B
|
||||
import Data.ByteString.Builder (toLazyByteString)
|
||||
import qualified Data.ByteString.Lazy as LB
|
||||
import Data.Either (partitionEithers)
|
||||
import Data.Int (Int64)
|
||||
import Data.String (IsString (..))
|
||||
@@ -26,7 +26,7 @@ batchingTests = describe "message batching tests" $ do
|
||||
it "image x.msg.new and x.msg.file.descr should fit into single batch" testImageFitsSingleBatch
|
||||
|
||||
instance IsString SndMessage where
|
||||
fromString s = SndMessage {msgId, sharedMsgId = SharedMsgId "", msgBody = s'}
|
||||
fromString s = SndMessage {msgId, sharedMsgId = SharedMsgId "", msgBody = LB.fromStrict s'}
|
||||
where
|
||||
s' = encodeUtf8 $ T.pack s
|
||||
msgId = fromInteger $ os2ip s'
|
||||
@@ -94,14 +94,14 @@ testImageFitsSingleBatch = do
|
||||
-- 261_120 bytes (MAX_IMAGE_SIZE in UI), rounded up, example was 743
|
||||
let descrRoundedSize = 800
|
||||
|
||||
let xMsgNewStr = B.replicate xMsgNewRoundedSize 1
|
||||
descrStr = B.replicate descrRoundedSize 2
|
||||
let xMsgNewStr = LB.replicate xMsgNewRoundedSize 1
|
||||
descrStr = LB.replicate descrRoundedSize 2
|
||||
msg s = SndMessage {msgId = 0, sharedMsgId = SharedMsgId "", msgBody = s}
|
||||
batched = "[" <> xMsgNewStr <> "," <> descrStr <> "]"
|
||||
|
||||
runBatcherTest' maxChatMsgSize [msg xMsgNewStr, msg descrStr] [] [batched]
|
||||
|
||||
runBatcherTest :: Int -> [SndMessage] -> [ChatError] -> [ByteString] -> Spec
|
||||
runBatcherTest :: Int64 -> [SndMessage] -> [ChatError] -> [LB.ByteString] -> Spec
|
||||
runBatcherTest maxLen msgs expectedErrors expectedBatches =
|
||||
it
|
||||
( (show (map (\SndMessage {msgBody} -> msgBody) msgs) <> ", limit " <> show maxLen <> ": should return ")
|
||||
@@ -110,10 +110,10 @@ runBatcherTest maxLen msgs expectedErrors expectedBatches =
|
||||
)
|
||||
(runBatcherTest' maxLen msgs expectedErrors expectedBatches)
|
||||
|
||||
runBatcherTest' :: Int -> [SndMessage] -> [ChatError] -> [ByteString] -> IO ()
|
||||
runBatcherTest' :: Int64 -> [SndMessage] -> [ChatError] -> [LB.ByteString] -> IO ()
|
||||
runBatcherTest' maxLen msgs expectedErrors expectedBatches = do
|
||||
let (errors, batches) = partitionEithers $ batchMessages maxLen msgs
|
||||
batchedStrs = map (\(MsgBatch batchBody _) -> batchBody) batches
|
||||
batchedStrs = map (\(MsgBatch builder _) -> toLazyByteString builder) batches
|
||||
testErrors errors `shouldBe` testErrors expectedErrors
|
||||
batchedStrs `shouldBe` expectedBatches
|
||||
where
|
||||
|
||||
@@ -7,6 +7,7 @@ module ProtocolTests where
|
||||
|
||||
import qualified Data.Aeson as J
|
||||
import Data.ByteString.Char8 (ByteString)
|
||||
import qualified Data.ByteString.Lazy.Char8 as LB
|
||||
import Data.Time.Clock.System (SystemTime (..), systemToUTCTime)
|
||||
import Simplex.Chat.Protocol
|
||||
import Simplex.Chat.Types
|
||||
@@ -73,7 +74,7 @@ s ##== msg = do
|
||||
let r = encodeChatMessage msg
|
||||
case r of
|
||||
ECMEncoded encodedBody ->
|
||||
J.eitherDecodeStrict' encodedBody
|
||||
J.eitherDecodeStrict' (LB.toStrict encodedBody)
|
||||
`shouldBe` (J.eitherDecodeStrict' s :: Either String J.Value)
|
||||
ECMLarge -> expectationFailure $ "large message"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user