20 minute interval, selfhosted servers, works, whoa
This commit is contained in:
parent
fe75ad22c4
commit
a8236367c3
4 changed files with 76 additions and 3 deletions
|
|
@ -6,7 +6,8 @@ import FirebaseCore
|
|||
import CoreData
|
||||
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate, ObservableObject {
|
||||
let tag = "AppDelegate"
|
||||
private let tag = "AppDelegate"
|
||||
private let pollTimerTopic = "~poll" // See ntfy server if ever changed
|
||||
|
||||
// Implements navigation from notifications, see https://stackoverflow.com/a/70731861/1440785
|
||||
@Published var selectedBaseUrl: String? = nil
|
||||
|
|
@ -30,9 +31,49 @@ class AppDelegate: UIResponder, UIApplicationDelegate, ObservableObject {
|
|||
// Set self as messaging delegate
|
||||
Messaging.messaging().delegate = self
|
||||
|
||||
// Register to timerkeeper topic
|
||||
Messaging.messaging().subscribe(toTopic: pollTimerTopic)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/// Executed when a background notification arrives. This is used to trigger polling of local topics.
|
||||
/// See https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app
|
||||
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
||||
Log.d(tag, "Background notification received", userInfo)
|
||||
|
||||
// Exit out early if this message is not expected
|
||||
let topic = userInfo["topic"] as? String ?? ""
|
||||
if topic != pollTimerTopic {
|
||||
completionHandler(.noData)
|
||||
return
|
||||
}
|
||||
|
||||
// Poll and display new messages
|
||||
let store = Store.shared
|
||||
let center = UNUserNotificationCenter.current()
|
||||
let subscriptionManager = SubscriptionManager(store: store)
|
||||
|
||||
store.getSubscriptions()?.forEach { subscription in
|
||||
subscriptionManager.poll(subscription) { messages in
|
||||
messages.forEach { message in
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = message.title ?? ""
|
||||
content.body = message.message ?? ""
|
||||
content.sound = .default
|
||||
|
||||
let request = UNNotificationRequest(identifier: message.id, content: content, trigger: nil /* now */)
|
||||
center.add(request) { (error) in
|
||||
if let error = error {
|
||||
Log.e(self.tag, "Unable to create notification", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
completionHandler(.newData)
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
||||
let token = deviceToken.map { data in String(format: "%02.2hhx", data) }.joined()
|
||||
Messaging.messaging().apnsToken = deviceToken
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@ class Store: ObservableObject {
|
|||
return try? context.fetch(fetchRequest).first
|
||||
}
|
||||
|
||||
func getSubscriptions() -> [Subscription]? {
|
||||
return try? context.fetch(Subscription.fetchRequest())
|
||||
}
|
||||
|
||||
func delete(subscription: Subscription) {
|
||||
context.delete(subscription)
|
||||
try? context.save()
|
||||
|
|
|
|||
|
|
@ -25,20 +25,26 @@ struct SubscriptionManager {
|
|||
}
|
||||
|
||||
func poll(_ subscription: Subscription) {
|
||||
poll(subscription) { _ in }
|
||||
}
|
||||
|
||||
func poll(_ subscription: Subscription, completionHandler: @escaping ([Message]) -> Void) {
|
||||
Log.d(tag, "Polling from \(subscription.urlString())")
|
||||
ApiService.shared.poll(subscription: subscription) { messages, error in
|
||||
guard let messages = messages else {
|
||||
Log.e(tag, "Polling failed", error)
|
||||
completionHandler([])
|
||||
return
|
||||
}
|
||||
Log.d(tag, "Polling success, \(messages.count) new message(s)", messages)
|
||||
if !messages.isEmpty {
|
||||
DispatchQueue.main.async {
|
||||
DispatchQueue.main.sync {
|
||||
for message in messages {
|
||||
store.save(notificationFromMessage: message, withSubscription: subscription)
|
||||
}
|
||||
}
|
||||
}
|
||||
completionHandler(messages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ struct SubscriptionAddView: View {
|
|||
|
||||
@EnvironmentObject private var store: Store
|
||||
@State private var topic: String = ""
|
||||
@State private var useAnother: Bool = false
|
||||
@State private var baseUrl: String = ""
|
||||
|
||||
private var subscriptionManager: SubscriptionManager {
|
||||
return SubscriptionManager(store: store)
|
||||
|
|
@ -24,6 +26,14 @@ struct SubscriptionAddView: View {
|
|||
.disableAutocapitalization()
|
||||
.disableAutocorrection(true)
|
||||
}
|
||||
Section {
|
||||
Toggle("Use another server", isOn: $useAnother)
|
||||
if useAnother {
|
||||
TextField("Server URL, e.g. https://ntfy.example.com", text: $baseUrl)
|
||||
.disableAutocapitalization()
|
||||
.disableAutocorrection(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Add subscription")
|
||||
|
|
@ -61,8 +71,9 @@ struct SubscriptionAddView: View {
|
|||
}
|
||||
|
||||
private func subscribeAction() {
|
||||
let baseUrl = (useAnother) ? baseUrl : Config.appBaseUrl
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
subscriptionManager.subscribe(baseUrl: Config.appBaseUrl, topic: sanitize(topic: topic))
|
||||
subscriptionManager.subscribe(baseUrl: baseUrl, topic: sanitize(topic: topic))
|
||||
}
|
||||
isShowing = false
|
||||
}
|
||||
|
|
@ -71,3 +82,14 @@ struct SubscriptionAddView: View {
|
|||
isShowing = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct SubscriptionAddView_Previews: PreviewProvider {
|
||||
@State static var isShowing = true
|
||||
|
||||
static var previews: some View {
|
||||
let store = Store.preview
|
||||
SubscriptionAddView(isShowing: $isShowing)
|
||||
.environmentObject(store)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue