Merge branch 'master' into f/ios-connection-ui
This commit is contained in:
commit
8a4843f099
@ -605,27 +605,29 @@ func apiConnectPlan(connReq: String) async throws -> ConnectionPlan {
|
|||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiConnect(incognito: Bool, connReq: String) async -> ConnReqType? {
|
func apiConnect(incognito: Bool, connReq: String) async -> (ConnReqType, PendingContactConnection)? {
|
||||||
let (connReqType, alert) = await apiConnect_(incognito: incognito, connReq: connReq)
|
let (r, alert) = await apiConnect_(incognito: incognito, connReq: connReq)
|
||||||
if let alert = alert {
|
if let alert = alert {
|
||||||
AlertManager.shared.showAlert(alert)
|
AlertManager.shared.showAlert(alert)
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return connReqType
|
return r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiConnect_(incognito: Bool, connReq: String) async -> (ConnReqType?, Alert?) {
|
func apiConnect_(incognito: Bool, connReq: String) async -> ((ConnReqType, PendingContactConnection)?, Alert?) {
|
||||||
guard let userId = ChatModel.shared.currentUser?.userId else {
|
guard let userId = ChatModel.shared.currentUser?.userId else {
|
||||||
logger.error("apiConnect: no current user")
|
logger.error("apiConnect: no current user")
|
||||||
return (nil, nil)
|
return (nil, nil)
|
||||||
}
|
}
|
||||||
let r = await chatSendCmd(.apiConnect(userId: userId, incognito: incognito, connReq: connReq))
|
let r = await chatSendCmd(.apiConnect(userId: userId, incognito: incognito, connReq: connReq))
|
||||||
|
let m = ChatModel.shared
|
||||||
switch r {
|
switch r {
|
||||||
case .sentConfirmation: return (.invitation, nil)
|
case let .sentConfirmation(_, connection):
|
||||||
case .sentInvitation: return (.contact, nil)
|
return ((.invitation, connection), nil)
|
||||||
|
case let .sentInvitation(_, connection):
|
||||||
|
return ((.contact, connection), nil)
|
||||||
case let .contactAlreadyExists(_, contact):
|
case let .contactAlreadyExists(_, contact):
|
||||||
let m = ChatModel.shared
|
|
||||||
if let c = m.getContactChat(contact.contactId) {
|
if let c = m.getContactChat(contact.contactId) {
|
||||||
await MainActor.run { m.chatId = c.id }
|
await MainActor.run { m.chatId = c.id }
|
||||||
}
|
}
|
||||||
@ -1362,18 +1364,6 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
|||||||
let m = ChatModel.shared
|
let m = ChatModel.shared
|
||||||
logger.debug("processReceivedMsg: \(res.responseType)")
|
logger.debug("processReceivedMsg: \(res.responseType)")
|
||||||
switch res {
|
switch res {
|
||||||
case let .newContactConnection(user, connection):
|
|
||||||
if active(user) {
|
|
||||||
await MainActor.run {
|
|
||||||
m.updateContactConnection(connection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case let .contactConnectionDeleted(user, connection):
|
|
||||||
if active(user) {
|
|
||||||
await MainActor.run {
|
|
||||||
m.removeChat(connection.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case let .contactDeletedByContact(user, contact):
|
case let .contactDeletedByContact(user, contact):
|
||||||
if active(user) && contact.directOrUsed {
|
if active(user) && contact.directOrUsed {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
|
@ -74,6 +74,7 @@ struct CreateLinkView: View {
|
|||||||
let (r, _) = await apiAddContact(incognito: incognitoGroupDefault.get())
|
let (r, _) = await apiAddContact(incognito: incognitoGroupDefault.get())
|
||||||
if let (connReq, pcc) = r {
|
if let (connReq, pcc) = r {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
|
m.updateContactConnection(pcc)
|
||||||
connReqInvitation = connReq
|
connReqInvitation = connReq
|
||||||
contactConnection = pcc
|
contactConnection = pcc
|
||||||
m.connReqInv = connReq
|
m.connReqInv = connReq
|
||||||
|
@ -53,6 +53,9 @@ struct NewChatButton: View {
|
|||||||
Task {
|
Task {
|
||||||
let (r, _) = await apiAddContact(incognito: incognitoGroupDefault.get())
|
let (r, _) = await apiAddContact(incognito: incognitoGroupDefault.get())
|
||||||
if let (connReq, pcc) = r {
|
if let (connReq, pcc) = r {
|
||||||
|
await MainActor.run {
|
||||||
|
ChatModel.shared.updateContactConnection(pcc)
|
||||||
|
}
|
||||||
actionSheet = .createLink(link: connReq, connection: pcc)
|
actionSheet = .createLink(link: connReq, connection: pcc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -347,7 +350,10 @@ private func connectContactViaAddress_(_ contact: Contact, dismiss: Bool, incogn
|
|||||||
|
|
||||||
private func connectViaLink(_ connectionLink: String, connectionPlan: ConnectionPlan?, dismiss: Bool, incognito: Bool) {
|
private func connectViaLink(_ connectionLink: String, connectionPlan: ConnectionPlan?, dismiss: Bool, incognito: Bool) {
|
||||||
Task {
|
Task {
|
||||||
if let connReqType = await apiConnect(incognito: incognito, connReq: connectionLink) {
|
if let (connReqType, pcc) = await apiConnect(incognito: incognito, connReq: connectionLink) {
|
||||||
|
await MainActor.run {
|
||||||
|
ChatModel.shared.updateContactConnection(pcc)
|
||||||
|
}
|
||||||
let crt: ConnReqType
|
let crt: ConnReqType
|
||||||
if let plan = connectionPlan {
|
if let plan = connectionPlan {
|
||||||
crt = planToConnReqType(plan)
|
crt = planToConnReqType(plan)
|
||||||
|
@ -505,8 +505,8 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case invitation(user: UserRef, connReqInvitation: String, connection: PendingContactConnection)
|
case invitation(user: UserRef, connReqInvitation: String, connection: PendingContactConnection)
|
||||||
case connectionIncognitoUpdated(user: UserRef, toConnection: PendingContactConnection)
|
case connectionIncognitoUpdated(user: UserRef, toConnection: PendingContactConnection)
|
||||||
case connectionPlan(user: UserRef, connectionPlan: ConnectionPlan)
|
case connectionPlan(user: UserRef, connectionPlan: ConnectionPlan)
|
||||||
case sentConfirmation(user: UserRef)
|
case sentConfirmation(user: UserRef, connection: PendingContactConnection)
|
||||||
case sentInvitation(user: UserRef)
|
case sentInvitation(user: UserRef, connection: PendingContactConnection)
|
||||||
case sentInvitationToContact(user: UserRef, contact: Contact, customUserProfile: Profile?)
|
case sentInvitationToContact(user: UserRef, contact: Contact, customUserProfile: Profile?)
|
||||||
case contactAlreadyExists(user: UserRef, contact: Contact)
|
case contactAlreadyExists(user: UserRef, contact: Contact)
|
||||||
case contactRequestAlreadyAccepted(user: UserRef, contact: Contact)
|
case contactRequestAlreadyAccepted(user: UserRef, contact: Contact)
|
||||||
@ -605,7 +605,6 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case ntfTokenStatus(status: NtfTknStatus)
|
case ntfTokenStatus(status: NtfTknStatus)
|
||||||
case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode)
|
case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode)
|
||||||
case ntfMessages(user_: User?, connEntity: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
|
case ntfMessages(user_: User?, connEntity: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
|
||||||
case newContactConnection(user: UserRef, connection: PendingContactConnection)
|
|
||||||
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
|
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
|
||||||
// remote desktop responses/events
|
// remote desktop responses/events
|
||||||
case remoteCtrlList(remoteCtrls: [RemoteCtrlInfo])
|
case remoteCtrlList(remoteCtrls: [RemoteCtrlInfo])
|
||||||
@ -752,7 +751,6 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case .ntfTokenStatus: return "ntfTokenStatus"
|
case .ntfTokenStatus: return "ntfTokenStatus"
|
||||||
case .ntfToken: return "ntfToken"
|
case .ntfToken: return "ntfToken"
|
||||||
case .ntfMessages: return "ntfMessages"
|
case .ntfMessages: return "ntfMessages"
|
||||||
case .newContactConnection: return "newContactConnection"
|
|
||||||
case .contactConnectionDeleted: return "contactConnectionDeleted"
|
case .contactConnectionDeleted: return "contactConnectionDeleted"
|
||||||
case .remoteCtrlList: return "remoteCtrlList"
|
case .remoteCtrlList: return "remoteCtrlList"
|
||||||
case .remoteCtrlFound: return "remoteCtrlFound"
|
case .remoteCtrlFound: return "remoteCtrlFound"
|
||||||
@ -803,11 +801,11 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case let .contactCode(u, contact, connectionCode): return withUser(u, "contact: \(String(describing: contact))\nconnectionCode: \(connectionCode)")
|
case let .contactCode(u, contact, connectionCode): return withUser(u, "contact: \(String(describing: contact))\nconnectionCode: \(connectionCode)")
|
||||||
case let .groupMemberCode(u, groupInfo, member, connectionCode): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionCode: \(connectionCode)")
|
case let .groupMemberCode(u, groupInfo, member, connectionCode): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionCode: \(connectionCode)")
|
||||||
case let .connectionVerified(u, verified, expectedCode): return withUser(u, "verified: \(verified)\nconnectionCode: \(expectedCode)")
|
case let .connectionVerified(u, verified, expectedCode): return withUser(u, "verified: \(verified)\nconnectionCode: \(expectedCode)")
|
||||||
case let .invitation(u, connReqInvitation, _): return withUser(u, connReqInvitation)
|
case let .invitation(u, connReqInvitation, connection): return withUser(u, "connReqInvitation: \(connReqInvitation)\nconnection: \(connection)")
|
||||||
case let .connectionIncognitoUpdated(u, toConnection): return withUser(u, String(describing: toConnection))
|
case let .connectionIncognitoUpdated(u, toConnection): return withUser(u, String(describing: toConnection))
|
||||||
case let .connectionPlan(u, connectionPlan): return withUser(u, String(describing: connectionPlan))
|
case let .connectionPlan(u, connectionPlan): return withUser(u, String(describing: connectionPlan))
|
||||||
case .sentConfirmation: return noDetails
|
case let .sentConfirmation(u, connection): return withUser(u, String(describing: connection))
|
||||||
case .sentInvitation: return noDetails
|
case let .sentInvitation(u, connection): return withUser(u, String(describing: connection))
|
||||||
case let .sentInvitationToContact(u, contact, _): return withUser(u, String(describing: contact))
|
case let .sentInvitationToContact(u, contact, _): return withUser(u, String(describing: contact))
|
||||||
case let .contactAlreadyExists(u, contact): return withUser(u, String(describing: contact))
|
case let .contactAlreadyExists(u, contact): return withUser(u, String(describing: contact))
|
||||||
case let .contactRequestAlreadyAccepted(u, contact): return withUser(u, String(describing: contact))
|
case let .contactRequestAlreadyAccepted(u, contact): return withUser(u, String(describing: contact))
|
||||||
@ -900,7 +898,6 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case let .ntfTokenStatus(status): return String(describing: status)
|
case let .ntfTokenStatus(status): return String(describing: status)
|
||||||
case let .ntfToken(token, status, ntfMode): return "token: \(token)\nstatus: \(status.rawValue)\nntfMode: \(ntfMode.rawValue)"
|
case let .ntfToken(token, status, ntfMode): return "token: \(token)\nstatus: \(status.rawValue)\nntfMode: \(ntfMode.rawValue)"
|
||||||
case let .ntfMessages(u, connEntity, msgTs, ntfMessages): return withUser(u, "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))")
|
case let .ntfMessages(u, connEntity, msgTs, ntfMessages): return withUser(u, "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))")
|
||||||
case let .newContactConnection(u, connection): return withUser(u, String(describing: connection))
|
|
||||||
case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection))
|
case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection))
|
||||||
case let .remoteCtrlList(remoteCtrls): return String(describing: remoteCtrls)
|
case let .remoteCtrlList(remoteCtrls): return String(describing: remoteCtrls)
|
||||||
case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible): return "remoteCtrl:\n\(String(describing: remoteCtrl))\nctrlAppInfo_:\n\(String(describing: ctrlAppInfo_))\nappVersion: \(appVersion)\ncompatible: \(compatible)"
|
case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible): return "remoteCtrl:\n\(String(describing: remoteCtrl))\nctrlAppInfo_:\n\(String(describing: ctrlAppInfo_))\nappVersion: \(appVersion)\ncompatible: \(compatible)"
|
||||||
|
@ -173,6 +173,8 @@ class AppPreferences {
|
|||||||
val connectRemoteViaMulticastAuto = mkBoolPreference(SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST_AUTO, true)
|
val connectRemoteViaMulticastAuto = mkBoolPreference(SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST_AUTO, true)
|
||||||
val offerRemoteMulticast = mkBoolPreference(SHARED_PREFS_OFFER_REMOTE_MULTICAST, true)
|
val offerRemoteMulticast = mkBoolPreference(SHARED_PREFS_OFFER_REMOTE_MULTICAST, true)
|
||||||
|
|
||||||
|
val desktopWindowState = mkStrPreference(SHARED_PREFS_DESKTOP_WINDOW_STATE, null)
|
||||||
|
|
||||||
private fun mkIntPreference(prefName: String, default: Int) =
|
private fun mkIntPreference(prefName: String, default: Int) =
|
||||||
SharedPreference(
|
SharedPreference(
|
||||||
get = fun() = settings.getInt(prefName, default),
|
get = fun() = settings.getInt(prefName, default),
|
||||||
@ -317,6 +319,7 @@ class AppPreferences {
|
|||||||
private const val SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST = "ConnectRemoteViaMulticast"
|
private const val SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST = "ConnectRemoteViaMulticast"
|
||||||
private const val SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST_AUTO = "ConnectRemoteViaMulticastAuto"
|
private const val SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST_AUTO = "ConnectRemoteViaMulticastAuto"
|
||||||
private const val SHARED_PREFS_OFFER_REMOTE_MULTICAST = "OfferRemoteMulticast"
|
private const val SHARED_PREFS_OFFER_REMOTE_MULTICAST = "OfferRemoteMulticast"
|
||||||
|
private const val SHARED_PREFS_DESKTOP_WINDOW_STATE = "DesktopWindowState"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -891,20 +894,21 @@ object ChatController {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun apiConnect(rh: Long?, incognito: Boolean, connReq: String): Boolean {
|
suspend fun apiConnect(rh: Long?, incognito: Boolean, connReq: String): PendingContactConnection? {
|
||||||
val userId = chatModel.currentUser.value?.userId ?: run {
|
val userId = chatModel.currentUser.value?.userId ?: run {
|
||||||
Log.e(TAG, "apiConnect: no current user")
|
Log.e(TAG, "apiConnect: no current user")
|
||||||
return false
|
return null
|
||||||
}
|
}
|
||||||
val r = sendCmd(rh, CC.APIConnect(userId, incognito, connReq))
|
val r = sendCmd(rh, CC.APIConnect(userId, incognito, connReq))
|
||||||
when {
|
when {
|
||||||
r is CR.SentConfirmation || r is CR.SentInvitation -> return true
|
r is CR.SentConfirmation -> return r.connection
|
||||||
|
r is CR.SentInvitation -> return r.connection
|
||||||
r is CR.ContactAlreadyExists -> {
|
r is CR.ContactAlreadyExists -> {
|
||||||
AlertManager.shared.showAlertMsg(
|
AlertManager.shared.showAlertMsg(
|
||||||
generalGetString(MR.strings.contact_already_exists),
|
generalGetString(MR.strings.contact_already_exists),
|
||||||
String.format(generalGetString(MR.strings.you_are_already_connected_to_vName_via_this_link), r.contact.displayName)
|
String.format(generalGetString(MR.strings.you_are_already_connected_to_vName_via_this_link), r.contact.displayName)
|
||||||
)
|
)
|
||||||
return false
|
return null
|
||||||
}
|
}
|
||||||
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorChat
|
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorChat
|
||||||
&& r.chatError.errorType is ChatErrorType.InvalidConnReq -> {
|
&& r.chatError.errorType is ChatErrorType.InvalidConnReq -> {
|
||||||
@ -912,7 +916,7 @@ object ChatController {
|
|||||||
generalGetString(MR.strings.invalid_connection_link),
|
generalGetString(MR.strings.invalid_connection_link),
|
||||||
generalGetString(MR.strings.please_check_correct_link_and_maybe_ask_for_a_new_one)
|
generalGetString(MR.strings.please_check_correct_link_and_maybe_ask_for_a_new_one)
|
||||||
)
|
)
|
||||||
return false
|
return null
|
||||||
}
|
}
|
||||||
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorAgent
|
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorAgent
|
||||||
&& r.chatError.agentError is AgentErrorType.SMP
|
&& r.chatError.agentError is AgentErrorType.SMP
|
||||||
@ -921,13 +925,13 @@ object ChatController {
|
|||||||
generalGetString(MR.strings.connection_error_auth),
|
generalGetString(MR.strings.connection_error_auth),
|
||||||
generalGetString(MR.strings.connection_error_auth_desc)
|
generalGetString(MR.strings.connection_error_auth_desc)
|
||||||
)
|
)
|
||||||
return false
|
return null
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if (!(networkErrorAlert(r))) {
|
if (!(networkErrorAlert(r))) {
|
||||||
apiErrorAlert("apiConnect", generalGetString(MR.strings.connection_error), r)
|
apiErrorAlert("apiConnect", generalGetString(MR.strings.connection_error), r)
|
||||||
}
|
}
|
||||||
return false
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1526,16 +1530,6 @@ object ChatController {
|
|||||||
fun active(user: UserLike): Boolean = activeUser(rhId, user)
|
fun active(user: UserLike): Boolean = activeUser(rhId, user)
|
||||||
chatModel.addTerminalItem(TerminalItem.resp(rhId, r))
|
chatModel.addTerminalItem(TerminalItem.resp(rhId, r))
|
||||||
when (r) {
|
when (r) {
|
||||||
is CR.NewContactConnection -> {
|
|
||||||
if (active(r.user)) {
|
|
||||||
chatModel.updateContactConnection(rhId, r.connection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is CR.ContactConnectionDeleted -> {
|
|
||||||
if (active(r.user)) {
|
|
||||||
chatModel.removeChat(rhId, r.connection.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is CR.ContactDeletedByContact -> {
|
is CR.ContactDeletedByContact -> {
|
||||||
if (active(r.user) && r.contact.directOrUsed) {
|
if (active(r.user) && r.contact.directOrUsed) {
|
||||||
chatModel.updateContact(rhId, r.contact)
|
chatModel.updateContact(rhId, r.contact)
|
||||||
@ -3707,8 +3701,8 @@ sealed class CR {
|
|||||||
@Serializable @SerialName("invitation") class Invitation(val user: UserRef, val connReqInvitation: String, val connection: PendingContactConnection): CR()
|
@Serializable @SerialName("invitation") class Invitation(val user: UserRef, val connReqInvitation: String, val connection: PendingContactConnection): CR()
|
||||||
@Serializable @SerialName("connectionIncognitoUpdated") class ConnectionIncognitoUpdated(val user: UserRef, val toConnection: PendingContactConnection): CR()
|
@Serializable @SerialName("connectionIncognitoUpdated") class ConnectionIncognitoUpdated(val user: UserRef, val toConnection: PendingContactConnection): CR()
|
||||||
@Serializable @SerialName("connectionPlan") class CRConnectionPlan(val user: UserRef, val connectionPlan: ConnectionPlan): CR()
|
@Serializable @SerialName("connectionPlan") class CRConnectionPlan(val user: UserRef, val connectionPlan: ConnectionPlan): CR()
|
||||||
@Serializable @SerialName("sentConfirmation") class SentConfirmation(val user: UserRef): CR()
|
@Serializable @SerialName("sentConfirmation") class SentConfirmation(val user: UserRef, val connection: PendingContactConnection): CR()
|
||||||
@Serializable @SerialName("sentInvitation") class SentInvitation(val user: UserRef): CR()
|
@Serializable @SerialName("sentInvitation") class SentInvitation(val user: UserRef, val connection: PendingContactConnection): CR()
|
||||||
@Serializable @SerialName("sentInvitationToContact") class SentInvitationToContact(val user: UserRef, val contact: Contact, val customUserProfile: Profile?): CR()
|
@Serializable @SerialName("sentInvitationToContact") class SentInvitationToContact(val user: UserRef, val contact: Contact, val customUserProfile: Profile?): CR()
|
||||||
@Serializable @SerialName("contactAlreadyExists") class ContactAlreadyExists(val user: UserRef, val contact: Contact): CR()
|
@Serializable @SerialName("contactAlreadyExists") class ContactAlreadyExists(val user: UserRef, val contact: Contact): CR()
|
||||||
@Serializable @SerialName("contactRequestAlreadyAccepted") class ContactRequestAlreadyAccepted(val user: UserRef, val contact: Contact): CR()
|
@Serializable @SerialName("contactRequestAlreadyAccepted") class ContactRequestAlreadyAccepted(val user: UserRef, val contact: Contact): CR()
|
||||||
@ -3802,7 +3796,6 @@ sealed class CR {
|
|||||||
@Serializable @SerialName("callAnswer") class CallAnswer(val user: UserRef, val contact: Contact, val answer: WebRTCSession): CR()
|
@Serializable @SerialName("callAnswer") class CallAnswer(val user: UserRef, val contact: Contact, val answer: WebRTCSession): CR()
|
||||||
@Serializable @SerialName("callExtraInfo") class CallExtraInfo(val user: UserRef, val contact: Contact, val extraInfo: WebRTCExtraInfo): CR()
|
@Serializable @SerialName("callExtraInfo") class CallExtraInfo(val user: UserRef, val contact: Contact, val extraInfo: WebRTCExtraInfo): CR()
|
||||||
@Serializable @SerialName("callEnded") class CallEnded(val user: UserRef, val contact: Contact): CR()
|
@Serializable @SerialName("callEnded") class CallEnded(val user: UserRef, val contact: Contact): CR()
|
||||||
@Serializable @SerialName("newContactConnection") class NewContactConnection(val user: UserRef, val connection: PendingContactConnection): CR()
|
|
||||||
@Serializable @SerialName("contactConnectionDeleted") class ContactConnectionDeleted(val user: UserRef, val connection: PendingContactConnection): CR()
|
@Serializable @SerialName("contactConnectionDeleted") class ContactConnectionDeleted(val user: UserRef, val connection: PendingContactConnection): CR()
|
||||||
// remote events (desktop)
|
// remote events (desktop)
|
||||||
@Serializable @SerialName("remoteHostList") class RemoteHostList(val remoteHosts: List<RemoteHostInfo>): CR()
|
@Serializable @SerialName("remoteHostList") class RemoteHostList(val remoteHosts: List<RemoteHostInfo>): CR()
|
||||||
@ -3951,7 +3944,6 @@ sealed class CR {
|
|||||||
is CallAnswer -> "callAnswer"
|
is CallAnswer -> "callAnswer"
|
||||||
is CallExtraInfo -> "callExtraInfo"
|
is CallExtraInfo -> "callExtraInfo"
|
||||||
is CallEnded -> "callEnded"
|
is CallEnded -> "callEnded"
|
||||||
is NewContactConnection -> "newContactConnection"
|
|
||||||
is ContactConnectionDeleted -> "contactConnectionDeleted"
|
is ContactConnectionDeleted -> "contactConnectionDeleted"
|
||||||
is RemoteHostList -> "remoteHostList"
|
is RemoteHostList -> "remoteHostList"
|
||||||
is CurrentRemoteHost -> "currentRemoteHost"
|
is CurrentRemoteHost -> "currentRemoteHost"
|
||||||
@ -4006,11 +3998,11 @@ sealed class CR {
|
|||||||
is ContactCode -> withUser(user, "contact: ${json.encodeToString(contact)}\nconnectionCode: $connectionCode")
|
is ContactCode -> withUser(user, "contact: ${json.encodeToString(contact)}\nconnectionCode: $connectionCode")
|
||||||
is GroupMemberCode -> withUser(user, "groupInfo: ${json.encodeToString(groupInfo)}\nmember: ${json.encodeToString(member)}\nconnectionCode: $connectionCode")
|
is GroupMemberCode -> withUser(user, "groupInfo: ${json.encodeToString(groupInfo)}\nmember: ${json.encodeToString(member)}\nconnectionCode: $connectionCode")
|
||||||
is ConnectionVerified -> withUser(user, "verified: $verified\nconnectionCode: $expectedCode")
|
is ConnectionVerified -> withUser(user, "verified: $verified\nconnectionCode: $expectedCode")
|
||||||
is Invitation -> withUser(user, connReqInvitation)
|
is Invitation -> withUser(user, "connReqInvitation: $connReqInvitation\nconnection: $connection")
|
||||||
is ConnectionIncognitoUpdated -> withUser(user, json.encodeToString(toConnection))
|
is ConnectionIncognitoUpdated -> withUser(user, json.encodeToString(toConnection))
|
||||||
is CRConnectionPlan -> withUser(user, json.encodeToString(connectionPlan))
|
is CRConnectionPlan -> withUser(user, json.encodeToString(connectionPlan))
|
||||||
is SentConfirmation -> withUser(user, noDetails())
|
is SentConfirmation -> withUser(user, json.encodeToString(connection))
|
||||||
is SentInvitation -> withUser(user, noDetails())
|
is SentInvitation -> withUser(user, json.encodeToString(connection))
|
||||||
is SentInvitationToContact -> withUser(user, json.encodeToString(contact))
|
is SentInvitationToContact -> withUser(user, json.encodeToString(contact))
|
||||||
is ContactAlreadyExists -> withUser(user, json.encodeToString(contact))
|
is ContactAlreadyExists -> withUser(user, json.encodeToString(contact))
|
||||||
is ContactRequestAlreadyAccepted -> withUser(user, json.encodeToString(contact))
|
is ContactRequestAlreadyAccepted -> withUser(user, json.encodeToString(contact))
|
||||||
@ -4098,7 +4090,6 @@ sealed class CR {
|
|||||||
is CallAnswer -> withUser(user, "contact: ${contact.id}\nanswer: ${json.encodeToString(answer)}")
|
is CallAnswer -> withUser(user, "contact: ${contact.id}\nanswer: ${json.encodeToString(answer)}")
|
||||||
is CallExtraInfo -> withUser(user, "contact: ${contact.id}\nextraInfo: ${json.encodeToString(extraInfo)}")
|
is CallExtraInfo -> withUser(user, "contact: ${contact.id}\nextraInfo: ${json.encodeToString(extraInfo)}")
|
||||||
is CallEnded -> withUser(user, "contact: ${contact.id}")
|
is CallEnded -> withUser(user, "contact: ${contact.id}")
|
||||||
is NewContactConnection -> withUser(user, json.encodeToString(connection))
|
|
||||||
is ContactConnectionDeleted -> withUser(user, json.encodeToString(connection))
|
is ContactConnectionDeleted -> withUser(user, json.encodeToString(connection))
|
||||||
// remote events (mobile)
|
// remote events (mobile)
|
||||||
is RemoteHostList -> json.encodeToString(remoteHosts)
|
is RemoteHostList -> json.encodeToString(remoteHosts)
|
||||||
|
@ -108,6 +108,7 @@ private fun createInvitation(
|
|||||||
withApi {
|
withApi {
|
||||||
val r = m.controller.apiAddContact(rhId, incognito = m.controller.appPrefs.incognito.get())
|
val r = m.controller.apiAddContact(rhId, incognito = m.controller.appPrefs.incognito.get())
|
||||||
if (r != null) {
|
if (r != null) {
|
||||||
|
m.updateContactConnection(rhId, r.second)
|
||||||
connReqInvitation.value = r.first
|
connReqInvitation.value = r.first
|
||||||
contactConnection.value = r.second
|
contactConnection.value = r.second
|
||||||
} else {
|
} else {
|
||||||
|
@ -283,10 +283,11 @@ suspend fun connectViaUri(
|
|||||||
incognito: Boolean,
|
incognito: Boolean,
|
||||||
connectionPlan: ConnectionPlan?,
|
connectionPlan: ConnectionPlan?,
|
||||||
close: (() -> Unit)?
|
close: (() -> Unit)?
|
||||||
): Boolean {
|
) {
|
||||||
val r = chatModel.controller.apiConnect(rhId, incognito, uri.toString())
|
val pcc = chatModel.controller.apiConnect(rhId, incognito, uri.toString())
|
||||||
val connLinkType = if (connectionPlan != null) planToConnectionLinkType(connectionPlan) else ConnectionLinkType.INVITATION
|
val connLinkType = if (connectionPlan != null) planToConnectionLinkType(connectionPlan) else ConnectionLinkType.INVITATION
|
||||||
if (r) {
|
if (pcc != null) {
|
||||||
|
chatModel.updateContactConnection(rhId, pcc)
|
||||||
close?.invoke()
|
close?.invoke()
|
||||||
AlertManager.shared.showAlertMsg(
|
AlertManager.shared.showAlertMsg(
|
||||||
title = generalGetString(MR.strings.connection_request_sent),
|
title = generalGetString(MR.strings.connection_request_sent),
|
||||||
@ -299,7 +300,6 @@ suspend fun connectViaUri(
|
|||||||
hostDevice = hostDevice(rhId),
|
hostDevice = hostDevice(rhId),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun planToConnectionLinkType(connectionPlan: ConnectionPlan): ConnectionLinkType {
|
fun planToConnectionLinkType(connectionPlan: ConnectionPlan): ConnectionLinkType {
|
||||||
|
@ -15,7 +15,6 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.window.*
|
import androidx.compose.ui.window.*
|
||||||
import chat.simplex.common.model.ChatController
|
import chat.simplex.common.model.ChatController
|
||||||
import chat.simplex.common.model.ChatModel
|
import chat.simplex.common.model.ChatModel
|
||||||
import chat.simplex.common.platform.desktopPlatform
|
|
||||||
import chat.simplex.common.ui.theme.DEFAULT_START_MODAL_WIDTH
|
import chat.simplex.common.ui.theme.DEFAULT_START_MODAL_WIDTH
|
||||||
import chat.simplex.common.ui.theme.SimpleXTheme
|
import chat.simplex.common.ui.theme.SimpleXTheme
|
||||||
import chat.simplex.common.views.TerminalView
|
import chat.simplex.common.views.TerminalView
|
||||||
@ -31,10 +30,30 @@ import java.io.File
|
|||||||
val simplexWindowState = SimplexWindowState()
|
val simplexWindowState = SimplexWindowState()
|
||||||
|
|
||||||
fun showApp() = application {
|
fun showApp() = application {
|
||||||
// For some reason on Linux actual width will be 10.dp less after specifying it here. If we specify 1366,
|
// Creates file if not exists; comes with proper defaults
|
||||||
// it will show 1356. But after that we can still update it to 1366 by changing window state. Just making it +10 now here
|
val state = getStoredWindowState()
|
||||||
val width = if (desktopPlatform.isLinux()) 1376.dp else 1366.dp
|
|
||||||
val windowState = rememberWindowState(placement = WindowPlacement.Floating, width = width, height = 768.dp)
|
val windowState: WindowState = rememberWindowState(
|
||||||
|
placement = WindowPlacement.Floating,
|
||||||
|
width = state.width.dp,
|
||||||
|
height = state.height.dp,
|
||||||
|
position = WindowPosition(state.x.dp, state.y.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
LaunchedEffect(
|
||||||
|
windowState.position.x.value,
|
||||||
|
windowState.position.y.value,
|
||||||
|
windowState.size.width.value,
|
||||||
|
windowState.size.height.value
|
||||||
|
) {
|
||||||
|
storeWindowState(WindowPositionSize(
|
||||||
|
x = windowState.position.x.value.toInt(),
|
||||||
|
y = windowState.position.y.value.toInt(),
|
||||||
|
width = windowState.size.width.value.toInt(),
|
||||||
|
height = windowState.size.height.value.toInt()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
simplexWindowState.windowState = windowState
|
simplexWindowState.windowState = windowState
|
||||||
// Reload all strings in all @Composable's after language change at runtime
|
// Reload all strings in all @Composable's after language change at runtime
|
||||||
if (remember { ChatController.appPrefs.appLanguage.state }.value != "") {
|
if (remember { ChatController.appPrefs.appLanguage.state }.value != "") {
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
package chat.simplex.common
|
||||||
|
|
||||||
|
import chat.simplex.common.model.json
|
||||||
|
import chat.simplex.common.platform.appPreferences
|
||||||
|
import chat.simplex.common.platform.desktopPlatform
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class WindowPositionSize(
|
||||||
|
val width: Int = 1366,
|
||||||
|
val height: Int = 768,
|
||||||
|
val x: Int = 0,
|
||||||
|
val y: Int = 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getStoredWindowState(): WindowPositionSize =
|
||||||
|
try {
|
||||||
|
val str = appPreferences.desktopWindowState.get()
|
||||||
|
var state = if (str == null) {
|
||||||
|
WindowPositionSize()
|
||||||
|
} else {
|
||||||
|
json.decodeFromString(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For some reason on Linux actual width will be 10.dp less after specifying it here. If we specify 1366,
|
||||||
|
// it will show 1356. But after that we can still update it to 1366 by changing window state. Just making it +10 now here
|
||||||
|
if (desktopPlatform.isLinux() && state.width == 1366) {
|
||||||
|
state = state.copy(width = 1376)
|
||||||
|
}
|
||||||
|
state
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
WindowPositionSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun storeWindowState(state: WindowPositionSize) =
|
||||||
|
appPreferences.desktopWindowState.set(json.encodeToString(state))
|
@ -86,7 +86,6 @@ export type ChatResponse =
|
|||||||
| CRGroupUpdated
|
| CRGroupUpdated
|
||||||
| CRUserContactLinkSubscribed
|
| CRUserContactLinkSubscribed
|
||||||
| CRUserContactLinkSubError
|
| CRUserContactLinkSubError
|
||||||
| CRNewContactConnection
|
|
||||||
| CRContactConnectionDeleted
|
| CRContactConnectionDeleted
|
||||||
| CRMessageError
|
| CRMessageError
|
||||||
| CRChatCmdError
|
| CRChatCmdError
|
||||||
@ -731,12 +730,6 @@ export interface CRUserContactLinkSubError extends CR {
|
|||||||
chatError: ChatError
|
chatError: ChatError
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CRNewContactConnection extends CR {
|
|
||||||
type: "newContactConnection"
|
|
||||||
user: User
|
|
||||||
connection: PendingContactConnection
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CRContactConnectionDeleted extends CR {
|
export interface CRContactConnectionDeleted extends CR {
|
||||||
type: "contactConnectionDeleted"
|
type: "contactConnectionDeleted"
|
||||||
user: User
|
user: User
|
||||||
|
@ -1403,7 +1403,6 @@ processChatCommand = \case
|
|||||||
subMode <- chatReadVar subscriptionMode
|
subMode <- chatReadVar subscriptionMode
|
||||||
(connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing subMode
|
(connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing subMode
|
||||||
conn <- withStore' $ \db -> createDirectConnection db user connId cReq ConnNew incognitoProfile subMode
|
conn <- withStore' $ \db -> createDirectConnection db user connId cReq ConnNew incognitoProfile subMode
|
||||||
toView $ CRNewContactConnection user conn
|
|
||||||
pure $ CRInvitation user cReq conn
|
pure $ CRInvitation user cReq conn
|
||||||
AddContact incognito -> withUser $ \User {userId} ->
|
AddContact incognito -> withUser $ \User {userId} ->
|
||||||
processChatCommand $ APIAddContact userId incognito
|
processChatCommand $ APIAddContact userId incognito
|
||||||
@ -1433,8 +1432,7 @@ processChatCommand = \case
|
|||||||
dm <- directMessage $ XInfo profileToSend
|
dm <- directMessage $ XInfo profileToSend
|
||||||
connId <- withAgent $ \a -> joinConnection a (aUserId user) True cReq dm subMode
|
connId <- withAgent $ \a -> joinConnection a (aUserId user) True cReq dm subMode
|
||||||
conn <- withStore' $ \db -> createDirectConnection db user connId cReq ConnJoined (incognitoProfile $> profileToSend) subMode
|
conn <- withStore' $ \db -> createDirectConnection db user connId cReq ConnJoined (incognitoProfile $> profileToSend) subMode
|
||||||
toView $ CRNewContactConnection user conn
|
pure $ CRSentConfirmation user conn
|
||||||
pure $ CRSentConfirmation user
|
|
||||||
APIConnect userId incognito (Just (ACR SCMContact cReq)) -> withUserId userId $ \user -> connectViaContact user incognito cReq
|
APIConnect userId incognito (Just (ACR SCMContact cReq)) -> withUserId userId $ \user -> connectViaContact user incognito cReq
|
||||||
APIConnect _ _ Nothing -> throwChatError CEInvalidConnReq
|
APIConnect _ _ Nothing -> throwChatError CEInvalidConnReq
|
||||||
Connect incognito aCReqUri@(Just cReqUri) -> withUser $ \user@User {userId} -> do
|
Connect incognito aCReqUri@(Just cReqUri) -> withUser $ \user@User {userId} -> do
|
||||||
@ -2106,8 +2104,7 @@ processChatCommand = \case
|
|||||||
connect' groupLinkId cReqHash xContactId = do
|
connect' groupLinkId cReqHash xContactId = do
|
||||||
(connId, incognitoProfile, subMode) <- requestContact user incognito cReq xContactId
|
(connId, incognitoProfile, subMode) <- requestContact user incognito cReq xContactId
|
||||||
conn <- withStore' $ \db -> createConnReqConnection db userId connId cReqHash xContactId incognitoProfile groupLinkId subMode
|
conn <- withStore' $ \db -> createConnReqConnection db userId connId cReqHash xContactId incognitoProfile groupLinkId subMode
|
||||||
toView $ CRNewContactConnection user conn
|
pure $ CRSentInvitation user conn incognitoProfile
|
||||||
pure $ CRSentInvitation user incognitoProfile
|
|
||||||
connectContactViaAddress :: User -> IncognitoEnabled -> Contact -> ConnectionRequestUri 'CMContact -> m ChatResponse
|
connectContactViaAddress :: User -> IncognitoEnabled -> Contact -> ConnectionRequestUri 'CMContact -> m ChatResponse
|
||||||
connectContactViaAddress user incognito ct cReq =
|
connectContactViaAddress user incognito ct cReq =
|
||||||
withChatLock "connectViaContact" $ do
|
withChatLock "connectViaContact" $ do
|
||||||
|
@ -557,8 +557,8 @@ data ChatResponse
|
|||||||
| CRInvitation {user :: User, connReqInvitation :: ConnReqInvitation, connection :: PendingContactConnection}
|
| CRInvitation {user :: User, connReqInvitation :: ConnReqInvitation, connection :: PendingContactConnection}
|
||||||
| CRConnectionIncognitoUpdated {user :: User, toConnection :: PendingContactConnection}
|
| CRConnectionIncognitoUpdated {user :: User, toConnection :: PendingContactConnection}
|
||||||
| CRConnectionPlan {user :: User, connectionPlan :: ConnectionPlan}
|
| CRConnectionPlan {user :: User, connectionPlan :: ConnectionPlan}
|
||||||
| CRSentConfirmation {user :: User}
|
| CRSentConfirmation {user :: User, connection :: PendingContactConnection}
|
||||||
| CRSentInvitation {user :: User, customUserProfile :: Maybe Profile}
|
| CRSentInvitation {user :: User, connection :: PendingContactConnection, customUserProfile :: Maybe Profile}
|
||||||
| CRSentInvitationToContact {user :: User, contact :: Contact, customUserProfile :: Maybe Profile}
|
| CRSentInvitationToContact {user :: User, contact :: Contact, customUserProfile :: Maybe Profile}
|
||||||
| CRContactUpdated {user :: User, fromContact :: Contact, toContact :: Contact}
|
| CRContactUpdated {user :: User, fromContact :: Contact, toContact :: Contact}
|
||||||
| CRGroupMemberUpdated {user :: User, groupInfo :: GroupInfo, fromMember :: GroupMember, toMember :: GroupMember}
|
| CRGroupMemberUpdated {user :: User, groupInfo :: GroupInfo, fromMember :: GroupMember, toMember :: GroupMember}
|
||||||
@ -655,7 +655,6 @@ data ChatResponse
|
|||||||
| CRNtfTokenStatus {status :: NtfTknStatus}
|
| CRNtfTokenStatus {status :: NtfTknStatus}
|
||||||
| CRNtfToken {token :: DeviceToken, status :: NtfTknStatus, ntfMode :: NotificationsMode}
|
| CRNtfToken {token :: DeviceToken, status :: NtfTknStatus, ntfMode :: NotificationsMode}
|
||||||
| CRNtfMessages {user_ :: Maybe User, connEntity :: Maybe ConnectionEntity, msgTs :: Maybe UTCTime, ntfMessages :: [NtfMsgInfo]}
|
| CRNtfMessages {user_ :: Maybe User, connEntity :: Maybe ConnectionEntity, msgTs :: Maybe UTCTime, ntfMessages :: [NtfMsgInfo]}
|
||||||
| CRNewContactConnection {user :: User, connection :: PendingContactConnection}
|
|
||||||
| CRContactConnectionDeleted {user :: User, connection :: PendingContactConnection}
|
| CRContactConnectionDeleted {user :: User, connection :: PendingContactConnection}
|
||||||
| CRRemoteHostList {remoteHosts :: [RemoteHostInfo]}
|
| CRRemoteHostList {remoteHosts :: [RemoteHostInfo]}
|
||||||
| CRCurrentRemoteHost {remoteHost_ :: Maybe RemoteHostInfo}
|
| CRCurrentRemoteHost {remoteHost_ :: Maybe RemoteHostInfo}
|
||||||
|
@ -164,8 +164,8 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
|||||||
CRInvitation u cReq _ -> ttyUser u $ viewConnReqInvitation cReq
|
CRInvitation u cReq _ -> ttyUser u $ viewConnReqInvitation cReq
|
||||||
CRConnectionIncognitoUpdated u c -> ttyUser u $ viewConnectionIncognitoUpdated c
|
CRConnectionIncognitoUpdated u c -> ttyUser u $ viewConnectionIncognitoUpdated c
|
||||||
CRConnectionPlan u connectionPlan -> ttyUser u $ viewConnectionPlan connectionPlan
|
CRConnectionPlan u connectionPlan -> ttyUser u $ viewConnectionPlan connectionPlan
|
||||||
CRSentConfirmation u -> ttyUser u ["confirmation sent!"]
|
CRSentConfirmation u _ -> ttyUser u ["confirmation sent!"]
|
||||||
CRSentInvitation u customUserProfile -> ttyUser u $ viewSentInvitation customUserProfile testView
|
CRSentInvitation u _ customUserProfile -> ttyUser u $ viewSentInvitation customUserProfile testView
|
||||||
CRSentInvitationToContact u _c customUserProfile -> ttyUser u $ viewSentInvitation customUserProfile testView
|
CRSentInvitationToContact u _c customUserProfile -> ttyUser u $ viewSentInvitation customUserProfile testView
|
||||||
CRContactDeleted u c -> ttyUser u [ttyContact' c <> ": contact is deleted"]
|
CRContactDeleted u c -> ttyUser u [ttyContact' c <> ": contact is deleted"]
|
||||||
CRContactDeletedByContact u c -> ttyUser u [ttyFullContact c <> " deleted contact with you"]
|
CRContactDeletedByContact u c -> ttyUser u [ttyFullContact c <> " deleted contact with you"]
|
||||||
@ -275,7 +275,6 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
|||||||
CRCallInvitations _ -> []
|
CRCallInvitations _ -> []
|
||||||
CRUserContactLinkSubscribed -> ["Your address is active! To show: " <> highlight' "/sa"]
|
CRUserContactLinkSubscribed -> ["Your address is active! To show: " <> highlight' "/sa"]
|
||||||
CRUserContactLinkSubError e -> ["user address error: " <> sShow e, "to delete your address: " <> highlight' "/da"]
|
CRUserContactLinkSubError e -> ["user address error: " <> sShow e, "to delete your address: " <> highlight' "/da"]
|
||||||
CRNewContactConnection u _ -> ttyUser u []
|
|
||||||
CRContactConnectionDeleted u PendingContactConnection {pccConnId} -> ttyUser u ["connection :" <> sShow pccConnId <> " deleted"]
|
CRContactConnectionDeleted u PendingContactConnection {pccConnId} -> ttyUser u ["connection :" <> sShow pccConnId <> " deleted"]
|
||||||
CRNtfTokenStatus status -> ["device token status: " <> plain (smpEncode status)]
|
CRNtfTokenStatus status -> ["device token status: " <> plain (smpEncode status)]
|
||||||
CRNtfToken _ status mode -> ["device token status: " <> plain (smpEncode status) <> ", notifications mode: " <> plain (strEncode mode)]
|
CRNtfToken _ status mode -> ["device token status: " <> plain (smpEncode status) <> ", notifications mode: " <> plain (strEncode mode)]
|
||||||
|
Loading…
Reference in New Issue
Block a user