mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Allow viewing registered device tokens
This commit is contained in:
parent
0ae690f13f
commit
340623a57c
4 changed files with 153 additions and 1 deletions
|
|
@ -0,0 +1,82 @@
|
|||
|
||||
import Models
|
||||
import Services
|
||||
import SwiftUI
|
||||
import Utils
|
||||
import Views
|
||||
|
||||
@MainActor final class PushNotificationDevicesViewModel: ObservableObject {
|
||||
@Published var isLoading = false
|
||||
@Published var devices = [InternalDeviceToken]()
|
||||
|
||||
func loadDevices(dataService: DataService) {
|
||||
isLoading = true
|
||||
Task {
|
||||
self.devices = (try? await dataService.devices()) ?? []
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
func removeToken(dataService: DataService, tokenID: String) {
|
||||
if let idx = devices.firstIndex(where: { $0.id == tokenID }) {
|
||||
Task {
|
||||
try await dataService.syncDeviceToken(deviceTokenOperation: .deleteToken(tokenID: tokenID))
|
||||
devices.remove(at: idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PushNotificationDevicesView: View {
|
||||
@EnvironmentObject var dataService: DataService
|
||||
@StateObject var viewModel = PushNotificationDevicesViewModel()
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
#if os(iOS)
|
||||
Form {
|
||||
innerBody
|
||||
}
|
||||
#elseif os(macOS)
|
||||
List {
|
||||
innerBody
|
||||
}
|
||||
.listStyle(InsetListStyle())
|
||||
#endif
|
||||
}
|
||||
.task { viewModel.loadDevices(dataService: dataService) }
|
||||
}
|
||||
|
||||
func createdStr(_ device: InternalDeviceToken) -> String {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateStyle = .short
|
||||
dateFormatter.timeStyle = .short
|
||||
|
||||
if let createdAt = device.createdAt {
|
||||
return dateFormatter.string(from: createdAt)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
private var innerBody: some View {
|
||||
List {
|
||||
Section(header: Text("Registered device tokens (swipe to remove)")) {
|
||||
ForEach(viewModel.devices) { device in
|
||||
Text("Created: \(createdStr(device))")
|
||||
.swipeActions(edge: .trailing) {
|
||||
Button(
|
||||
role: .destructive,
|
||||
action: {
|
||||
viewModel.removeToken(dataService: dataService, tokenID: device.id)
|
||||
},
|
||||
label: {
|
||||
Image(systemName: "trash")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Devices")
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,8 @@ import Views
|
|||
self.desiredNotificationsEnabled = granted
|
||||
Task {
|
||||
if let savedToken = UserDefaults.standard.string(forKey: UserDefaultKey.firebasePushToken.rawValue) {
|
||||
try? await dataService.syncDeviceToken(deviceTokenOperation: DeviceTokenOperation.addToken(token: savedToken))
|
||||
try? await dataService.syncDeviceToken(
|
||||
deviceTokenOperation: DeviceTokenOperation.addToken(token: savedToken))
|
||||
}
|
||||
NotificationCenter.default.post(name: Notification.Name("ReconfigurePushNotifications"), object: nil)
|
||||
}
|
||||
|
|
@ -83,6 +84,12 @@ struct PushNotificationSettingsView: View {
|
|||
""")
|
||||
.accentColor(.blue)
|
||||
}
|
||||
|
||||
Section {
|
||||
NavigationLink("Devices") {
|
||||
PushNotificationDevicesView()
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Push Notifications")
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import CoreData
|
||||
import Foundation
|
||||
import Models
|
||||
import SwiftGraphQL
|
||||
|
||||
public extension DataService {
|
||||
func devices() async throws -> [InternalDeviceToken] {
|
||||
enum QueryResult {
|
||||
case success(result: [InternalDeviceToken])
|
||||
case error(error: String)
|
||||
}
|
||||
|
||||
let deviceTokensSelection = Selection.DeviceToken {
|
||||
InternalDeviceToken(
|
||||
id: try $0.id(),
|
||||
createdAt: try $0.createdAt().value
|
||||
)
|
||||
}
|
||||
|
||||
let selection = Selection<QueryResult, Unions.DeviceTokensResult> {
|
||||
try $0.on(
|
||||
deviceTokensError: .init {
|
||||
QueryResult.error(error: try $0.errorCodes().description)
|
||||
},
|
||||
deviceTokensSuccess: .init {
|
||||
QueryResult.success(result: try $0.deviceTokens(selection: deviceTokensSelection.list))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let query = Selection.Query {
|
||||
try $0.deviceTokens(selection: selection)
|
||||
}
|
||||
|
||||
let path = appEnvironment.graphqlPath
|
||||
let headers = networker.defaultHeaders
|
||||
let context = backgroundContext
|
||||
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
send(query, to: path, headers: headers) { queryResult in
|
||||
guard let payload = try? queryResult.get() else {
|
||||
continuation.resume(throwing: BasicError.message(messageText: "network request failed"))
|
||||
return
|
||||
}
|
||||
|
||||
switch payload.data {
|
||||
case let .success(result: result):
|
||||
continuation.resume(returning: result)
|
||||
case .error:
|
||||
continuation.resume(throwing: BasicError.message(messageText: "DeviceToken Email fetch error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import CoreData
|
||||
import Foundation
|
||||
import Models
|
||||
|
||||
public struct InternalDeviceToken: Identifiable {
|
||||
public let id: String
|
||||
public let createdAt: Date?
|
||||
}
|
||||
Loading…
Reference in a new issue