diff --git a/docs/FEATURE_PARITY.md b/docs/FEATURE_PARITY.md index 9892604..601c8ce 100644 --- a/docs/FEATURE_PARITY.md +++ b/docs/FEATURE_PARITY.md @@ -12,7 +12,7 @@ This document is to keep track of the feature parity between the iOS and Android | Pause notifications | :x: | :white_check_mark: | Will likely require [Filtering](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_usernotifications_filtering) to prevent displaying notifications while still receiving them | | Send test notification | :white_check_mark: | :white_check_mark: | Not fully implemented | | Unsubscribe from topic | :white_check_mark: | :white_check_mark: | -| Delete notifications | :x: | :white_check_mark: | Not yet implemented | +| Delete notifications | :warning: | :white_check_mark: | Implemented, but needs improvement | | Notification priority | :warning: | :white_check_mark: | Displays an exclamation mark in notification row for high.max priority, no changes to the actual push notification (sounds, vibrations), no prioirty filtering | | Tags and emojis | :white_check_mark: | :white_check_mark: | | Click action | :x: | :white_check_mark: | Not yet implemented | diff --git a/ntfy-ios/Models/NtfyNotification.swift b/ntfy-ios/Models/NtfyNotification.swift index b75332d..ec072a3 100644 --- a/ntfy-ios/Models/NtfyNotification.swift +++ b/ntfy-ios/Models/NtfyNotification.swift @@ -7,7 +7,7 @@ import Foundation -class NtfyNotification: Identifiable, Decodable { +class NtfyNotification: Identifiable, Decodable, Hashable { // Database Properties var id: String! @@ -56,6 +56,14 @@ class NtfyNotification: Identifiable, Decodable { self.setTags() } + static func == (lhs: NtfyNotification, rhs: NtfyNotification) -> Bool { + return lhs.id == rhs.id + } + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + func save() -> NtfyNotification { return Database.current.addNotification(notification: self) } diff --git a/ntfy-ios/Views/SubscriptionDetail.swift b/ntfy-ios/Views/SubscriptionDetail.swift index a36a864..ba7a490 100644 --- a/ntfy-ios/Views/SubscriptionDetail.swift +++ b/ntfy-ios/Views/SubscriptionDetail.swift @@ -7,42 +7,119 @@ import SwiftUI +enum ActiveAlert { + case clear, unsubscribe, selected +} + struct SubscriptionDetail: View { var subscription: NtfySubscription + @State private var editMode = EditMode.inactive + @State private var selection = Set() + + @State private var showAlert = false + @State private var activeAlert: ActiveAlert = .clear + + @Environment(\.presentationMode) var presentationMode + var body: some View { - let notifications = Database.current.getNotifications(subscription: subscription) + var notifications = Database.current.getNotifications(subscription: subscription) let user = Database.current.findUser(baseUrl: subscription.baseUrl) NavigationView { - List(notifications) { notification in - NotificationRow(notification: notification) + List(selection: $selection) { + ForEach(notifications, id: \.self) { notification in + NotificationRow(notification: notification) + } } } .listStyle(PlainListStyle()) .navigationBarTitleDisplayMode(.inline) + .environment(\.editMode, self.$editMode) + .navigationBarBackButtonHidden(self.editMode == .active) .toolbar { ToolbarItem(placement: .principal) { Text(subscription.displayName()).font(.headline) } ToolbarItem(placement: .navigationBarTrailing) { - Menu("Edit") { - Button("Send Test Notification") { - let possibleTags = ["warning", "skull", "success", "triangular_flag_on_post", "de", "us", "dog", "cat", "rotating_light", "bike", "backup", "rsync", "this-s-a-tag", "ios"] - let priority = Int.random(in: 1..<6) - let tags = Array(possibleTags.shuffled().prefix(Int.random(in: 0..<4))) - ApiService.shared.publish( - subscription: subscription, - message: "This is a test notification from the Ntfy iOS app. It has a priority of \(priority). If you send another one, it may look different.", - title: "Test: You can set a title if you like", - priority: priority, - tags: tags, - user: user - ) { _,_ in - print("Success") + if (self.editMode == .active) { + editButton + } else { + Menu("Edit") { + editButton + Button("Send Test Notification") { + let possibleTags = ["warning", "skull", "success", "triangular_flag_on_post", "de", "us", "dog", "cat", "rotating_light", "bike", "backup", "rsync", "this-s-a-tag", "ios"] + let priority = Int.random(in: 1..<6) + let tags = Array(possibleTags.shuffled().prefix(Int.random(in: 0..<4))) + ApiService.shared.publish( + subscription: subscription, + message: "This is a test notification from the Ntfy iOS app. It has a priority of \(priority). If you send another one, it may look different.", + title: "Test: You can set a title if you like", + priority: priority, + tags: tags, + user: user + ) { _,_ in + print("Success") + } } + Button("Clear All Notifications") { + self.showAlert = true + self.activeAlert = .clear + } + Button("Unsubscribe") { + self.showAlert = true + self.activeAlert = .unsubscribe + } + } } } + ToolbarItem(placement: .navigationBarLeading) { + if (self.editMode == .active) { + Button(action: { + self.showAlert = true + self.activeAlert = .selected + }) { + Text("Delete") + .foregroundColor(.red) + } + } + } + } + .alert(isPresented: $showAlert) { + switch activeAlert { + case .clear: + return Alert( + title: Text("Clear Notifications"), + message: Text("Do you really want to delete all of the notifications in this topic?"), + primaryButton: .destructive( + Text("Permanently Delete"), + action: { + Database.current.deleteNotificationsForSubscription(subscription: subscription) + notifications = Database.current.getNotifications(subscription: subscription) + }), + secondaryButton: .cancel()) + case .unsubscribe: + return Alert( + title: Text("Unsubscribe"), + message: Text("Do you really want to unsubscribe from this topic and delete all of the notifications you received?"), + primaryButton: .destructive( + Text("Unsubscribe"), + action: { + Database.current.deleteSubscription(subscription: subscription) + self.presentationMode.wrappedValue.dismiss() + }), + secondaryButton: .cancel()) + case .selected: + return Alert( + title: Text("Delete"), + message: Text("Do you really want to delete these selected notifications?"), + primaryButton: .destructive( + Text("Delete"), + action: { + deleteSelectedNotifications(notifications: notifications) + }), + secondaryButton: .cancel()) + } } .overlay(Group { if notifications.isEmpty { @@ -62,4 +139,32 @@ struct SubscriptionDetail: View { // TODO: Refresh view with updated notifications list } } + + private var editButton: some View { + if editMode == .inactive { + return Button(action: { + self.editMode = .active + self.selection = Set() + }) { + Text("Select Messages") + } + } else { + return Button(action: { + self.editMode = .inactive + self.selection = Set() + }) { + Text("Done") + } + } + } + + private func deleteSelectedNotifications(notifications: [NtfyNotification]) { + for id in selection { + if let index = notifications.lastIndex(where: { $0 == id }) { + //notifications.remove(at: index) + Database.current.deleteNotification(notification: notifications[index]) + } + } + selection = Set() + } }