diff --git a/apps/multiplatform/android/src/main/AndroidManifest.xml b/apps/multiplatform/android/src/main/AndroidManifest.xml
index 0470977bcd..96322c0a4f 100644
--- a/apps/multiplatform/android/src/main/AndroidManifest.xml
+++ b/apps/multiplatform/android/src/main/AndroidManifest.xml
@@ -185,6 +185,13 @@
android:foregroundServiceType="mediaPlayback|microphone|camera|remoteMessaging"
/>
+
+
+
+
+
+
onVerification(pn.verification)
+ }
+ // TODO: Start same job than the periodic service ?
+ // Receiving the push notif is enough to wake the app and fetch msgs
+ // But it may not be enough when the phone is in doze, or with some
+ // vendors
+ }
+
+ override fun onNewEndpoint(endpoint: PushEndpoint, instance: String) {
+ Log.d(TAG, "onNewEndpoint")
+ endpoint.pubKeySet ?: run {
+ // Should not happen
+ Log.w(TAG, "Missing pubKeySet")
+ return
+ }
+ CoroutineScope(Dispatchers.Default).launch {
+ chatModel.controller.sendCmd(
+ null,
+ CC.APIRegisterWebPush(endpoint.url, endpoint.pubKeySet!!.auth, endpoint.pubKeySet!!.pubKey),
+ log = true
+ )
+ }
+ }
+
+ override fun onRegistrationFailed(reason: FailedReason, instance: String) {
+ Log.d(TAG, "onRegistrationFailed: $reason")
+ // TODO: notification to inform about failed registration
+ }
+
+ override fun onUnregistered(instance: String) {
+ Log.d(TAG, "onUnregistered")
+ // TODO: notification to inform about unregistration
+ CoroutineScope(Dispatchers.Default).launch {
+ chatModel.controller.sendCmd(
+ null,
+ CC.APIDeleteSavedNtf(),
+ log = true
+ )
+ }
+ }
+
+ companion object {
+ private const val TAG = "PushService"
+ }
+}
\ No newline at end of file
diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/platform/PushManager.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/platform/PushManager.kt
index 2c94373073..a96c4a435f 100644
--- a/apps/multiplatform/android/src/main/java/chat/simplex/app/platform/PushManager.kt
+++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/platform/PushManager.kt
@@ -16,6 +16,7 @@ import androidx.compose.ui.unit.dp
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.getUserServers
import chat.simplex.common.platform.Log
+import chat.simplex.common.platform.chatModel
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.networkAndServers.showAddServerDialog
import chat.simplex.res.MR
@@ -39,10 +40,11 @@ object PushManager {
* Else alert about missing service
*/
suspend fun initUnifiedPush(context: Context, scope: CoroutineScope, onSuccess: () -> Unit) {
- val userServers = getUserServers(null) ?: listOf()
+ val rh = chatModel.remoteHostId()
+ val userServers = getUserServers(rh) ?: listOf()
if (!userServers.hasNtfServer()) {
Log.d(TAG, "User doesn't have any NTF server")
- showMissingNTFDialog(scope, userServers)
+ showMissingNTFDialog(scope, rh, userServers)
// After coming back from the server view, users will have to click on "Instant" again
return
}
@@ -64,7 +66,7 @@ object PushManager {
showSelectPushServiceDialog(context, distributors) {
UnifiedPush.saveDistributor(context, it)
register(context)
- onSuccess
+ onSuccess()
}
}
}
@@ -81,13 +83,13 @@ object PushManager {
*/
private fun List.hasNtfServer(): Boolean {
// TODO: check if ntf server has a VAPID key
- return this.any { it.ntfServers.any() }
+ return this.any { it.ntfServers.any { s -> s.enabled } }
}
/**
* Show a dialog to inform about missing NTF server
*/
- private fun showMissingNTFDialog(scope: CoroutineScope, userServers: List) = AlertManager.shared.showAlert {
+ private fun showMissingNTFDialog(scope: CoroutineScope, rh: Long?, userServers: List) = AlertManager.shared.showAlert {
AlertDialog(
onDismissRequest = AlertManager.shared::hideAlert,
title = {
@@ -109,7 +111,7 @@ object PushManager {
confirmButton = {
TextButton(onClick = {
AlertManager.shared.hideAlert()
- showAddServerDialog(scope, userServers)
+ showAddServerDialog(scope, rh, userServers)
}) { Text(stringResource(MR.strings.smp_servers_add)) }
},
// Ignore
@@ -123,10 +125,10 @@ object PushManager {
/**
* Dialog to add a server, manually or with a QR code
*/
- private fun showAddServerDialog(scope: CoroutineScope, userServers: List) {
+ private fun showAddServerDialog(scope: CoroutineScope, rh: Long?, userServers: List) {
val userServersState = mutableStateOf(userServers)
val serverErrors = mutableStateOf(listOf())
- showAddServerDialog(scope, userServersState, serverErrors, null)
+ showAddServerDialog(scope, userServersState, serverErrors, rh)
}
/**
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt
index 646004db68..a7badd03a4 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt
@@ -3464,6 +3464,9 @@ sealed class CC {
class ResetAgentServersStats(): CC()
class GetAgentSubsTotal(val userId: Long): CC()
class GetAgentServersSummary(val userId: Long): CC()
+ class APIRegisterWebPush(val endpoint: String, val auth: String, val p256dh: String): CC()
+ class APIVerifySavedNtf(val code: String): CC()
+ class APIDeleteSavedNtf(): CC()
val cmdString: String get() = when (this) {
is Console -> cmd
@@ -3656,6 +3659,9 @@ sealed class CC {
is ResetAgentServersStats -> "/reset servers stats"
is GetAgentSubsTotal -> "/get subs total $userId"
is GetAgentServersSummary -> "/get servers summary $userId"
+ is APIRegisterWebPush -> "/_ntf register webpush $endpoint $auth $p256dh INSTANT"
+ is APIDeleteSavedNtf -> "/_ntf delete saved"
+ is APIVerifySavedNtf -> "/_ntf verify $code"
}
val cmdType: String get() = when (this) {
@@ -3814,6 +3820,9 @@ sealed class CC {
is ResetAgentServersStats -> "resetAgentServersStats"
is GetAgentSubsTotal -> "getAgentSubsTotal"
is GetAgentServersSummary -> "getAgentServersSummary"
+ is APIRegisterWebPush -> "apiRegisterWebPush"
+ is APIDeleteSavedNtf -> "apiDeleteSavedNtf"
+ is APIVerifySavedNtf -> "apiVerifySavedNtf"
}
data class ItemRange(val from: Long, val to: Long)
@@ -6952,7 +6961,7 @@ sealed class AgentErrorType {
is CMD -> "CMD ${cmdErr.string} $errContext"
is CONN -> "CONN ${connErr.string}"
is SMP -> "SMP ${smpErr.string}"
- // is NTF -> "NTF ${ntfErr.string}"
+ is NTF -> "NTF ${ntfErr.string}"
is XFTP -> "XFTP ${xftpErr.string}"
is PROXY -> "PROXY $proxyServer $relayServer ${proxyErr.string}"
is RCP -> "RCP ${rcpErr.string}"
@@ -6965,7 +6974,7 @@ sealed class AgentErrorType {
@Serializable @SerialName("CMD") class CMD(val cmdErr: CommandErrorType, val errContext: String): AgentErrorType()
@Serializable @SerialName("CONN") class CONN(val connErr: ConnectionErrorType): AgentErrorType()
@Serializable @SerialName("SMP") class SMP(val serverAddress: String, val smpErr: SMPErrorType): AgentErrorType()
- // @Serializable @SerialName("NTF") class NTF(val ntfErr: SMPErrorType): AgentErrorType()
+ @Serializable @SerialName("NTF") class NTF(val ntfErr: SMPErrorType): AgentErrorType()
@Serializable @SerialName("XFTP") class XFTP(val xftpErr: XFTPErrorType): AgentErrorType()
@Serializable @SerialName("PROXY") class PROXY(val proxyServer: String, val relayServer: String, val proxyErr: ProxyClientError): AgentErrorType()
@Serializable @SerialName("RCP") class RCP(val rcpErr: RCErrorType): AgentErrorType()
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/OperatorView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/OperatorView.kt
index baa667bb8d..3220f66868 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/OperatorView.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/OperatorView.kt
@@ -101,27 +101,26 @@ fun navigateToProtocolView(
userServers = userServers,
serverErrors = serverErrors,
onDelete = {
- if (protocol == ServerProtocol.SMP) {
- deleteSMPServer(userServers, operatorIndex, serverIndex)
- } else {
- deleteXFTPServer(userServers, operatorIndex, serverIndex)
+ when (protocol) {
+ ServerProtocol.NTF -> deleteNTFServer(userServers, operatorIndex, serverIndex)
+ ServerProtocol.SMP -> deleteSMPServer(userServers, operatorIndex, serverIndex)
+ ServerProtocol.XFTP -> deleteXFTPServer(userServers, operatorIndex, serverIndex)
}
close()
},
onUpdate = { updatedServer ->
userServers.value = userServers.value.toMutableList().apply {
- this[operatorIndex] = this[operatorIndex].copy(
- smpServers = if (protocol == ServerProtocol.SMP) {
- this[operatorIndex].smpServers.toMutableList().apply {
- this[serverIndex] = updatedServer
- }
- } else this[operatorIndex].smpServers,
- xftpServers = if (protocol == ServerProtocol.XFTP) {
- this[operatorIndex].xftpServers.toMutableList().apply {
- this[serverIndex] = updatedServer
- }
- } else this[operatorIndex].xftpServers
- )
+ if (platform.supportsPushNotifications && protocol == ServerProtocol.NTF && updatedServer.enabled) {
+ // We keep a single ntf server, if the updatedServer is enabled, we disable all other ntf servers first
+ this.replaceAll { op ->
+ op.copy(ntfServers = op.ntfServers.map { server -> server.copy(enabled = false).also { s -> Log.d(TAG, "ntf: $s")} })
+ }
+ }
+ this[operatorIndex] = when (protocol) {
+ ServerProtocol.NTF -> this[operatorIndex].copy(ntfServers = this[operatorIndex].ntfServers.toMutableList().apply { this[serverIndex] = updatedServer })
+ ServerProtocol.SMP -> this[operatorIndex].copy(smpServers = this[operatorIndex].smpServers.toMutableList().apply { this[serverIndex] = updatedServer })
+ ServerProtocol.XFTP -> this[operatorIndex].copy(xftpServers = this[operatorIndex].xftpServers.toMutableList().apply { this[serverIndex] = updatedServer })
+ }
}
},
close = close,
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/ProtocolServerView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/ProtocolServerView.kt
index bebc96a28c..9f5ebb34b2 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/ProtocolServerView.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/ProtocolServerView.kt
@@ -115,7 +115,11 @@ private fun ProtocolServerLayout(
onDelete: () -> Unit,
) {
ColumnWithScrollBar {
- AppBarTitle(stringResource(if (serverProtocol == ServerProtocol.XFTP) MR.strings.xftp_server else MR.strings.smp_server))
+ AppBarTitle(stringResource(when (serverProtocol) {
+ ServerProtocol.NTF -> MR.strings.ntf_server
+ ServerProtocol.XFTP -> MR.strings.xftp_server
+ ServerProtocol.SMP -> MR.strings.smp_server
+ }))
if (server.value.preset) {
PresetServer(server, testing, testServer)
diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/ProtocolServersView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/ProtocolServersView.kt
index 4276df28a6..9d3d307fc8 100644
--- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/ProtocolServersView.kt
+++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/ProtocolServersView.kt
@@ -389,6 +389,32 @@ private suspend fun runServersTest(servers: List, m: ChatModel, onUp
return fs
}
+fun deleteNTFServer(
+ userServers: MutableState>,
+ operatorServersIndex: Int,
+ serverIndex: Int
+) {
+ val serverIsSaved = userServers.value[operatorServersIndex].ntfServers[serverIndex].serverId != null
+
+ if (serverIsSaved) {
+ userServers.value = userServers.value.toMutableList().apply {
+ this[operatorServersIndex] = this[operatorServersIndex].copy(
+ ntfServers = this[operatorServersIndex].ntfServers.toMutableList().apply {
+ this[serverIndex] = this[serverIndex].copy(deleted = true)
+ }
+ )
+ }
+ } else {
+ userServers.value = userServers.value.toMutableList().apply {
+ this[operatorServersIndex] = this[operatorServersIndex].copy(
+ ntfServers = this[operatorServersIndex].ntfServers.toMutableList().apply {
+ this.removeAt(serverIndex)
+ }
+ )
+ }
+ }
+}
+
fun deleteXFTPServer(
userServers: MutableState>,
operatorServersIndex: Int,
diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml
index cbd701f907..39daacf355 100644
--- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml
+++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml
@@ -2642,6 +2642,7 @@
Starting from %s.
SMP server
XFTP server
+ NTF server
Reconnect
attempts
Sent directly
diff --git a/cabal.project b/cabal.project
index 48b75a86cd..9b82856f8e 100644
--- a/cabal.project
+++ b/cabal.project
@@ -11,8 +11,13 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package
type: git
- location: https://github.com/simplex-chat/simplexmq.git
- tag: d352d518c2b3a42bc7a298954dde799422e1457f
+ location: https://codeberg.org/s1m/sxmq.git
+ tag: f5720a254104d70b33ac1479ffa9d24ba9988b59
+
+-- source-repository-package
+-- type: git
+-- location: https://github.com/simplex-chat/simplexmq.git
+-- tag: d352d518c2b3a42bc7a298954dde799422e1457f
source-repository-package
type: git
diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix
index 68a6054ef0..ffdf834552 100644
--- a/scripts/nix/sha256map.nix
+++ b/scripts/nix/sha256map.nix
@@ -1,4 +1,5 @@
{
+ "https://codeberg.org/s1m/sxmq.git"."f5720a254104d70b33ac1479ffa9d24ba9988b59" = "cf9a74de0d05afa60a74aa8aa54205c718286b9c16c5f0f398a24d7fa7f7b1ff";
"https://github.com/simplex-chat/simplexmq.git"."d352d518c2b3a42bc7a298954dde799422e1457f" = "1rha84pfpaqx3mf218szkfra334vhijqf17hanxqmp1sicfbf1x3";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";