From a3501a76ffb30b2fb7fac2d6441689c7906c56e5 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 9 Dec 2022 15:24:13 +0800 Subject: [PATCH] WIP: ios push notification permission UX --- .../App/Views/Profile/ProfileView.swift | 41 ++++++---- .../Profile/PushNotificationSettings.swift | 80 +++++++++++++++++++ .../Profile/RecommendationGroupsView.swift | 31 ++----- .../Sources/App/Views/RootView/RootView.swift | 6 +- .../App/Views/RootView/RootViewModel.swift | 24 ------ .../Sources/Utils/UserDefaultKeys.swift | 1 + apple/Sources/AppDelegate.swift | 1 + 7 files changed, 115 insertions(+), 69 deletions(-) create mode 100644 apple/OmnivoreKit/Sources/App/Views/Profile/PushNotificationSettings.swift diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift index ffa30831d..f7d504e1f 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift @@ -76,6 +76,26 @@ struct ProfileView: View { #endif } + private var accountSection: some View { + Section { + NavigationLink(destination: LabelsView()) { + Text("Labels") + } + + NavigationLink(destination: NewsletterEmailsView()) { + Text("Emails") + } + + NavigationLink(destination: SubscriptionsView()) { + Text("Subscriptions") + } + + NavigationLink(destination: GroupsView()) { + Text("Recommendation Groups") + } + } + } + private var innerBody: some View { Group { Section { @@ -85,26 +105,13 @@ struct ProfileView: View { } } - Section { - NavigationLink(destination: LabelsView()) { - Text("Labels") - } - - NavigationLink(destination: NewsletterEmailsView()) { - Text("Emails") - } - - NavigationLink(destination: SubscriptionsView()) { - Text("Subscriptions") - } - - NavigationLink(destination: GroupsView()) { - Text("Recommendation Groups") - } - } + accountSection #if os(iOS) Section { + NavigationLink(destination: PushNotificationSettingsView()) { + Text("Push Notifications") + } NavigationLink(destination: TextToSpeechView()) { Text("Text to Speech") } diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/PushNotificationSettings.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/PushNotificationSettings.swift new file mode 100644 index 000000000..db9bddddb --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/PushNotificationSettings.swift @@ -0,0 +1,80 @@ +import Models +import Services +import SwiftUI +import Utils +import Views + +@MainActor final class PushNotificationSettingsViewModel: ObservableObject { + @Published var isLoading = false + @Published var emails = [NewsletterEmail]() + @Published var desiredNotificationsEnabled = false + @AppStorage(UserDefaultKey.notificationsEnabled.rawValue) var notificationsEnabled = false + + func checkPushNotificationsStatus() { + UNUserNotificationCenter.current().getNotificationSettings { settings in + DispatchQueue.main.async { + self.desiredNotificationsEnabled = settings.alertSetting == UNNotificationSetting.enabled + } + } + } + + func tryUpdateToDesired() { + print("trying to update to desired state: ", desiredNotificationsEnabled) + if desiredNotificationsEnabled { + UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { granted, error in + print("notification status: ", granted, "error: ", error) + DispatchQueue.main.async { + self.desiredNotificationsEnabled = granted + } + } + } else { + // UNUserNotificationCenter.current().r + UIApplication.shared.openURL(URL(string:"prefs:root=NOTIFICATIONS_ID")!) + + } + } +} + +struct PushNotificationSettingsView: View { + @EnvironmentObject var dataService: DataService + @StateObject var viewModel = PushNotificationSettingsViewModel() + @State var desiredNotificationsEnabled: Bool = false + + var body: some View { + Group { + #if os(iOS) + Form { + innerBody + } + #elseif os(macOS) + List { + innerBody + } + .listStyle(InsetListStyle()) + #endif + } + .task { viewModel.checkPushNotificationsStatus() } + } + + private var innerBody: some View { + Group { + Section { + Toggle(isOn: $viewModel.desiredNotificationsEnabled, label: { Text("Notifications Enabled") }) + }.onChange(of: viewModel.desiredNotificationsEnabled) { _ in + viewModel.tryUpdateToDesired() + } + + Section { + Text(""" + Enabling push notifications gives Omnivore device permission to send notifications, \ + but you are in charge of which notifications are sent. + + Push notifications are triggered using your \ + [account rules](https://omnivore.app/settings/rules) which you can edit online. + """) + .accentColor(.blue) + } + } + .navigationTitle("Push Notifications") + } +} diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/RecommendationGroupsView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/RecommendationGroupsView.swift index a9d64bd1f..570072b41 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/RecommendationGroupsView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/RecommendationGroupsView.swift @@ -6,7 +6,7 @@ import Views @MainActor final class RecommendationsGroupsViewModel: ObservableObject { @Published var isLoading = false @Published var isCreating = false - @Published var networkError = false + @Published var networkError = true @Published var recommendationGroups = [InternalRecommendationGroup]() @Published var showCreateSheet = false @@ -130,31 +130,14 @@ struct GroupsView: View { .disabled(viewModel.isLoading) } - if viewModel.recommendationGroups.count > 0 { - Section(header: Text("Your recommendation groups")) { - ForEach(viewModel.recommendationGroups) { recommendationGroup in - NavigationLink( - destination: RecommendationGroupView(viewModel: RecommendationsGroupViewModel(recommendationGroup: recommendationGroup)) - ) { - Text(recommendationGroup.name) - } + Section(header: Text("Your recommendation groups")) { + ForEach(viewModel.recommendationGroups) { recommendationGroup in + NavigationLink( + destination: RecommendationGroupView(viewModel: RecommendationsGroupViewModel(recommendationGroup: recommendationGroup)) + ) { + Text(recommendationGroup.name) } } - } else if !viewModel.isLoading { - Section { - Text(""" - You are not a member of any groups. - Create a new group and send the invite link to your friends get started. - - During the beta you are limited to creating three groups, and each group - can have a maximum of twelve users. - - [Learn more about groups](https://blog.omnivore.app/p/dca38ba4-8a74-42cc-90ca-d5ffa5d075cc) - """) - .accentColor(.blue) - } - } else { - ProgressView() } } .navigationTitle("Recommendation Groups") diff --git a/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift b/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift index fcf3d0f37..a5b0bfd79 100644 --- a/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift @@ -49,12 +49,10 @@ struct InnerRootView: View { if authenticator.isLoggedIn { GeometryReader { geo in PrimaryContentView() - .onAppear { - viewModel.triggerPushNotificationRequestIfNeeded() - } #if os(iOS) .miniPlayer() - .formSheet(isPresented: $viewModel.showNewFeaturePrimer, modalSize: CGSize(width: geo.size.width * 0.66, height: geo.size.width * 0.66)) { + .formSheet(isPresented: $viewModel.showNewFeaturePrimer, + modalSize: CGSize(width: geo.size.width * 0.66, height: geo.size.width * 0.66)) { FeaturePrimer.recommendationsPrimer } .onAppear { diff --git a/apple/OmnivoreKit/Sources/App/Views/RootView/RootViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/RootView/RootViewModel.swift index 200093972..d1e4ca395 100644 --- a/apple/OmnivoreKit/Sources/App/Views/RootView/RootViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/RootView/RootViewModel.swift @@ -35,32 +35,8 @@ public final class RootViewModel: ObservableObject { #endif } - func triggerPushNotificationRequestIfNeeded() { -// guard FeatureFlag.enablePushNotifications else { return } -// -// if UserDefaults.standard.bool(forKey: UserDefaultKey.userHasDeniedPushPrimer.rawValue) { -// return -// } -// -// #if os(iOS) -// UNUserNotificationCenter.current().getNotificationSettings { [weak self] settings in -// switch settings.authorizationStatus { -// case .notDetermined: -// DispatchQueue.main.async { -// self?.showPushNotificationPrimer = true -// } -// case .authorized, .provisional, .ephemeral, .denied: -// return -// @unknown default: -// return -// } -// } -// #endif - } - #if os(iOS) func handlePushNotificationPrimerAcceptance() { -// showPushNotificationPrimer = false // UNUserNotificationCenter.current().requestAuth() } #endif diff --git a/apple/OmnivoreKit/Sources/Utils/UserDefaultKeys.swift b/apple/OmnivoreKit/Sources/Utils/UserDefaultKeys.swift index 3e7e7eb1a..d0b032f3a 100644 --- a/apple/OmnivoreKit/Sources/Utils/UserDefaultKeys.swift +++ b/apple/OmnivoreKit/Sources/Utils/UserDefaultKeys.swift @@ -24,4 +24,5 @@ public enum UserDefaultKey: String { case audioPlayerExpanded case themeName case shouldShowNewFeaturePrimer + case notificationsEnabled } diff --git a/apple/Sources/AppDelegate.swift b/apple/Sources/AppDelegate.swift index c1c7875aa..58911808b 100644 --- a/apple/Sources/AppDelegate.swift +++ b/apple/Sources/AppDelegate.swift @@ -47,6 +47,7 @@ private let logger = Logger(subsystem: "app.omnivore", category: "app-delegate") Services.registerBackgroundFetch() configurePushNotifications() + return true } }