ios: show notification token status in UI (#552)
* ios: show notification token status in UI * show notification token status
This commit is contained in:
committed by
GitHub
parent
8257842914
commit
0091e9f162
@@ -19,12 +19,13 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
||||
let token = deviceToken.map { String(format: "%02hhx", $0) }.joined()
|
||||
logger.debug("AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken \(token)")
|
||||
ChatModel.shared.deviceToken = token
|
||||
let m = ChatModel.shared
|
||||
m.deviceToken = token
|
||||
let useNotifications = UserDefaults.standard.bool(forKey: "useNotifications")
|
||||
if useNotifications {
|
||||
Task {
|
||||
do {
|
||||
try await apiRegisterToken(token: token)
|
||||
m.tokenStatus = try await apiRegisterToken(token: token)
|
||||
} catch {
|
||||
logger.error("apiRegisterToken error: \(responseError(error))")
|
||||
}
|
||||
@@ -47,10 +48,16 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
if let token = ChatModel.shared.deviceToken {
|
||||
logger.debug("AppDelegate: didReceiveRemoteNotification: verification, confirming \(verification)")
|
||||
Task {
|
||||
let m = ChatModel.shared
|
||||
do {
|
||||
if case .active = m.tokenStatus {} else { m.tokenStatus = .confirmed }
|
||||
try await apiVerifyToken(token: token, code: verification, nonce: nonce)
|
||||
m.tokenStatus = .active
|
||||
try await apiIntervalNofication(token: token, interval: 20)
|
||||
} catch {
|
||||
if let cr = error as? ChatResponse, case .chatCmdError(.errorAgent(.NTF(.AUTH))) = cr {
|
||||
m.tokenStatus = .expired
|
||||
}
|
||||
logger.error("AppDelegate: didReceiveRemoteNotification: apiVerifyToken or apiIntervalNofication error: \(responseError(error))")
|
||||
}
|
||||
completionHandler(.newData)
|
||||
|
||||
@@ -24,6 +24,7 @@ final class ChatModel: ObservableObject {
|
||||
@Published var userSMPServers: [String]?
|
||||
@Published var appOpenUrl: URL?
|
||||
@Published var deviceToken: String?
|
||||
@Published var tokenStatus = NtfTknStatus.new
|
||||
|
||||
var messageDelivery: Dictionary<Int64, () -> Void> = [:]
|
||||
|
||||
@@ -930,3 +931,12 @@ struct LinkPreview: Codable {
|
||||
var description: String = ""
|
||||
var image: String
|
||||
}
|
||||
|
||||
enum NtfTknStatus: String, Decodable {
|
||||
case new = "NEW"
|
||||
case registered = "REGISTERED"
|
||||
case invalid = "INVALID"
|
||||
case confirmed = "CONFIRMED"
|
||||
case active = "ACTIVE"
|
||||
case expired = "EXPIRED"
|
||||
}
|
||||
|
||||
@@ -173,6 +173,7 @@ enum ChatResponse: Decodable, Error {
|
||||
case chatItemDeleted(deletedChatItem: AChatItem, toChatItem: AChatItem)
|
||||
case rcvFileAccepted
|
||||
case rcvFileComplete(chatItem: AChatItem)
|
||||
case ntfTokenStatus(status: NtfTknStatus)
|
||||
case cmdOk
|
||||
case chatCmdError(chatError: ChatError)
|
||||
case chatError(chatError: ChatError)
|
||||
@@ -218,6 +219,7 @@ enum ChatResponse: Decodable, Error {
|
||||
case .chatItemDeleted: return "chatItemDeleted"
|
||||
case .rcvFileAccepted: return "rcvFileAccepted"
|
||||
case .rcvFileComplete: return "rcvFileComplete"
|
||||
case .ntfTokenStatus: return "ntfTokenStatus"
|
||||
case .cmdOk: return "cmdOk"
|
||||
case .chatCmdError: return "chatCmdError"
|
||||
case .chatError: return "chatError"
|
||||
@@ -266,6 +268,7 @@ enum ChatResponse: Decodable, Error {
|
||||
case let .chatItemDeleted(deletedChatItem, toChatItem): return "deletedChatItem:\n\(String(describing: deletedChatItem))\ntoChatItem:\n\(String(describing: toChatItem))"
|
||||
case .rcvFileAccepted: return noDetails
|
||||
case let .rcvFileComplete(chatItem): return String(describing: chatItem)
|
||||
case let .ntfTokenStatus(status): return String(describing: status)
|
||||
case .cmdOk: return noDetails
|
||||
case let .chatCmdError(chatError): return String(describing: chatError)
|
||||
case let .chatError(chatError): return String(describing: chatError)
|
||||
@@ -459,8 +462,10 @@ func apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteM
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiRegisterToken(token: String) async throws {
|
||||
try await sendCommandOkResp(.apiRegisterToken(token: token))
|
||||
func apiRegisterToken(token: String) async throws -> NtfTknStatus {
|
||||
let r = await chatSendCmd(.apiRegisterToken(token: token))
|
||||
if case let .ntfTokenStatus(status) = r { return status }
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiVerifyToken(token: String, code: String, nonce: String) async throws {
|
||||
@@ -959,7 +964,8 @@ enum StoreError: Decodable {
|
||||
enum AgentErrorType: Decodable {
|
||||
case CMD(cmdErr: CommandErrorType)
|
||||
case CONN(connErr: ConnectionErrorType)
|
||||
case SMP(smpErr: SMPErrorType)
|
||||
case SMP(smpErr: ProtocolErrorType)
|
||||
case NTF(ntfErr: ProtocolErrorType)
|
||||
case BROKER(brokerErr: BrokerErrorType)
|
||||
case AGENT(agentErr: SMPAgentError)
|
||||
case INTERNAL(internalErr: String)
|
||||
@@ -982,17 +988,17 @@ enum ConnectionErrorType: Decodable {
|
||||
}
|
||||
|
||||
enum BrokerErrorType: Decodable {
|
||||
case RESPONSE(smpErr: SMPErrorType)
|
||||
case RESPONSE(smpErr: ProtocolErrorType)
|
||||
case UNEXPECTED
|
||||
case NETWORK
|
||||
case TRANSPORT(transportErr: SMPTransportError)
|
||||
case TRANSPORT(transportErr: ProtocolTransportError)
|
||||
case TIMEOUT
|
||||
}
|
||||
|
||||
enum SMPErrorType: Decodable {
|
||||
enum ProtocolErrorType: Decodable {
|
||||
case BLOCK
|
||||
case SESSION
|
||||
case CMD(cmdErr: SMPCommandError)
|
||||
case CMD(cmdErr: ProtocolCommandError)
|
||||
case AUTH
|
||||
case QUOTA
|
||||
case NO_MSG
|
||||
@@ -1000,15 +1006,15 @@ enum SMPErrorType: Decodable {
|
||||
case INTERNAL
|
||||
}
|
||||
|
||||
enum SMPCommandError: Decodable {
|
||||
enum ProtocolCommandError: Decodable {
|
||||
case UNKNOWN
|
||||
case SYNTAX
|
||||
case NO_AUTH
|
||||
case HAS_AUTH
|
||||
case NO_QUEUE
|
||||
case NO_ENTITY
|
||||
}
|
||||
|
||||
enum SMPTransportError: Decodable {
|
||||
enum ProtocolTransportError: Decodable {
|
||||
case badBlock
|
||||
case largeMsg
|
||||
case badSession
|
||||
|
||||
@@ -133,9 +133,8 @@ struct SettingsView: View {
|
||||
}
|
||||
if let token = chatModel.deviceToken {
|
||||
HStack {
|
||||
Image(systemName: "bolt.fill")
|
||||
.padding(.trailing, 4)
|
||||
NotificationsToggle(token)
|
||||
notificationsIcon()
|
||||
notificationsToggle(token)
|
||||
}
|
||||
}
|
||||
Text("v\(appVersion ?? "?") (\(appBuild ?? "?"))")
|
||||
@@ -150,7 +149,35 @@ struct SettingsView: View {
|
||||
case error(LocalizedStringKey, String)
|
||||
}
|
||||
|
||||
private func NotificationsToggle(_ token: String) -> some View {
|
||||
private func notificationsIcon() -> some View {
|
||||
let icon: String
|
||||
let color: Color
|
||||
switch (chatModel.tokenStatus) {
|
||||
case .new:
|
||||
icon = "bolt"
|
||||
color = .primary
|
||||
case .registered:
|
||||
icon = "bolt.fill"
|
||||
color = .primary
|
||||
case .invalid:
|
||||
icon = "bolt.slash"
|
||||
color = .primary
|
||||
case .confirmed:
|
||||
icon = "bolt.fill"
|
||||
color = .yellow
|
||||
case .active:
|
||||
icon = "bolt.fill"
|
||||
color = .green
|
||||
case .expired:
|
||||
icon = "bolt.slash.fill"
|
||||
color = .primary
|
||||
}
|
||||
return Image(systemName: icon)
|
||||
.padding(.trailing, 9)
|
||||
.foregroundColor(color)
|
||||
}
|
||||
|
||||
private func notificationsToggle(_ token: String) -> some View {
|
||||
Toggle("Check messages", isOn: $useNotifications)
|
||||
.onChange(of: useNotifications) { enable in
|
||||
if enable {
|
||||
@@ -160,6 +187,7 @@ struct SettingsView: View {
|
||||
Task {
|
||||
do {
|
||||
try await apiDeleteToken(token: token)
|
||||
chatModel.tokenStatus = .new
|
||||
}
|
||||
catch {
|
||||
DispatchQueue.main.async {
|
||||
@@ -191,7 +219,7 @@ struct SettingsView: View {
|
||||
primaryButton: .destructive(Text("Confirm")) {
|
||||
Task {
|
||||
do {
|
||||
try await apiRegisterToken(token: token)
|
||||
chatModel.tokenStatus = try await apiRegisterToken(token: token)
|
||||
} catch {
|
||||
DispatchQueue.main.async {
|
||||
useNotifications = false
|
||||
|
||||
Reference in New Issue
Block a user