Files
simplex-chat/apps/ios/Shared/Views/UserSettings/NotificationsView.swift
Evgeny Poberezkin 7226e5d37a ios: notifications UI (#758)
* ios: notifications UI

* Apply suggestions from code review

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-06-28 19:03:39 +01:00

206 lines
7.5 KiB
Swift

//
// NotificationsView.swift
// SimpleX (iOS)
//
// Created by Evgeny on 26/06/2022.
// Copyright © 2022 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
struct NotificationsView: View {
@EnvironmentObject var m: ChatModel
@State private var notificationMode: NotificationMode?
@State private var showAlert: NotificationAlert?
@AppStorage(DEFAULT_USE_NOTIFICATIONS) private var useNotifications = false
var body: some View {
List {
Section {
NavigationLink {
List {
Section {
SelectionListView(list: NotificationMode.values, selection: $notificationMode) { mode in
showAlert = .setMode(mode: mode)
}
} footer: {
VStack(alignment: .leading) {
if let mode = notificationMode {
Text(ntfModeDescription(mode))
}
}
.font(.callout)
.padding(.top, 1)
}
}
.navigationTitle("Send notifications")
.navigationBarTitleDisplayMode(.inline)
.alert(item: $showAlert) { alert in
if let token = m.deviceToken {
return notificationAlert(alert, token)
} else {
return Alert(title: Text("No device token!"))
}
}
.onAppear { notificationMode = m.notificationMode }
} label: {
HStack {
Text("Send notifications")
Spacer()
Text(m.notificationMode.label)
}
}
NavigationLink {
List {
Section {
SelectionListView(list: NotificationPreviewMode.values, selection: $m.notificationPreview)
} footer: {
}
}
.navigationTitle("Show preview")
.navigationBarTitleDisplayMode(.inline)
} label: {
HStack {
Text("Show preview")
Spacer()
Text(m.notificationPreview?.label ?? "")
}
}
} header: {
Text("Message notifications")
}
}
}
private func notificationAlert(_ alert: NotificationAlert, _ token: DeviceToken) -> Alert {
switch alert {
case let .setMode(mode):
return Alert(
title: Text(ntfModeAlertTitle(mode)),
message: Text(ntfModeDescription(mode)),
primaryButton: .default(Text(mode == .off ? "Turn off" : "Enable")) {
setNotificationsMode(mode, token)
},
secondaryButton: .cancel() {
notificationMode = m.notificationMode
}
)
case let .error(title, error):
return Alert(title: Text(title), message: Text(error))
}
}
private func ntfModeAlertTitle(_ mode: NotificationMode) -> LocalizedStringKey {
switch mode {
case .off: return "Turn off notifications?"
case .periodic: return "Enable periodic notifications?"
case .instant: return "Enable instant notifications?"
}
}
private func setNotificationsMode(_ mode: NotificationMode, _ token: DeviceToken) {
Task {
switch mode {
case .off:
do {
try await apiDeleteToken(token: token)
useNotifications = false
m.tokenStatus = .new
notificationMode = .off
m.notificationMode = .off
}
catch let error {
DispatchQueue.main.async {
let err = responseError(error)
logger.error("apiDeleteToken error: \(err)")
showAlert = .error(title: "Error deleting token", error: err)
}
}
default:
do {
do {
m.tokenStatus = try await apiRegisterToken(token: token, notificationMode: mode)
useNotifications = true
notificationMode = mode
m.notificationMode = mode
} catch let error {
DispatchQueue.main.async {
useNotifications = notificationMode != .off
let err = responseError(error)
logger.error("apiRegisterToken error: \(err)")
showAlert = .error(title: "Error enabling notifications", error: err)
}
}
}
}
}
}
}
func ntfModeDescription(_ mode: NotificationMode) -> LocalizedStringKey {
switch mode {
case .off: return "**Maximum privacy**: push notifications are off.\nNo meta-data is shared with SimpleX Chat notification server."
case .periodic: return "**High privacy**: new messages are checked every 20 minutes.\nYour device token is shared with SimpleX Chat notification server, but it cannot see how many connections you have or how many messages you receive."
case .instant: return "**Medium privacy** (recommended): notifications are sent instantly.\nYour device token and notifications are sent to SimpleX Chat notification server, but it cannot access the message content, size or who is it from."
}
}
struct SelectionListView<Item: SelectableItem>: View {
var list: [Item]
@Binding var selection: Item?
var onSelection: ((Item) -> Void)?
@State private var tapped: Item? = nil
var body: some View {
ForEach(list) { item in
HStack {
Text(item.label)
Spacer()
if selection == item {
Image(systemName: "checkmark")
.resizable().scaledToFit().frame(width: 16)
.foregroundColor(.accentColor)
}
}
.contentShape(Rectangle())
.listRowBackground(Color(uiColor: tapped == item ? .secondarySystemFill : .systemBackground))
.onTapGesture {
if let f = onSelection {
f(item)
} else {
selection = item
}
}
._onButtonGesture { down in
if down {
tapped = item
} else {
tapped = nil
}
} perform: {}
}
.environment(\.editMode, .constant(.active))
}
}
enum NotificationAlert: Identifiable {
case setMode(mode: NotificationMode)
case error(title: LocalizedStringKey, error: String)
var id: String {
switch self {
case let .setMode(mode): return "enable \(mode.rawValue)"
case let .error(title, error): return "error \(title): \(error)"
}
}
}
struct NotificationsView_Previews: PreviewProvider {
static var previews: some View {
NotificationsView()
}
}