2022-06-27 10:28:30 +01:00
//
// N o t i f i c a t i o n s V i e w . s w i f t
// S i m p l e X ( i O S )
//
// C r e a t e d b y E v g e n y o n 2 6 / 0 6 / 2 0 2 2 .
// C o p y r i g h t © 2 0 2 2 S i m p l e X C h a t . A l l r i g h t s r e s e r v e d .
//
import SwiftUI
import SimpleXChat
struct NotificationsView : View {
@ EnvironmentObject var m : ChatModel
2023-07-19 16:37:46 +01:00
@ State private var notificationMode : NotificationsMode = ChatModel . shared . notificationMode
2022-06-28 19:03:39 +01:00
@ State private var showAlert : NotificationAlert ?
2022-07-01 20:33:20 +01:00
@ State private var legacyDatabase = dbContainerGroupDefault . get ( ) = = . documents
2022-06-27 10:28:30 +01:00
var body : some View {
List {
Section {
NavigationLink {
List {
Section {
2022-07-01 09:49:30 +01:00
SelectionListView ( list : NotificationsMode . values , selection : $ notificationMode ) { mode in
2022-06-28 19:03:39 +01:00
showAlert = . setMode ( mode : mode )
2022-06-27 10:28:30 +01:00
}
} footer : {
VStack ( alignment : . leading ) {
2023-07-19 16:37:46 +01:00
Text ( ntfModeDescription ( notificationMode ) )
2022-06-27 10:28:30 +01:00
}
. font ( . callout )
. padding ( . top , 1 )
}
}
. navigationTitle ( " Send notifications " )
. navigationBarTitleDisplayMode ( . inline )
2022-06-28 19:03:39 +01:00
. alert ( item : $ showAlert ) { alert in
if let token = m . deviceToken {
return notificationAlert ( alert , token )
} else {
return Alert ( title : Text ( " No device token! " ) )
}
}
2022-06-27 10:28:30 +01:00
} label : {
HStack {
Text ( " Send notifications " )
Spacer ( )
Text ( m . notificationMode . label )
}
}
NavigationLink {
List {
Section {
2022-07-06 11:52:10 +01:00
SelectionListView ( list : NotificationPreviewMode . values , selection : $ m . notificationPreview ) { previewMode in
ntfPreviewModeGroupDefault . set ( previewMode )
m . notificationPreview = previewMode
}
2022-06-27 10:28:30 +01:00
} footer : {
2022-07-06 11:52:10 +01:00
VStack ( alignment : . leading , spacing : 1 ) {
Text ( " You can set lock screen notification preview via settings. " )
Button ( " Open Settings " ) {
DispatchQueue . main . async {
UIApplication . shared . open ( URL ( string : UIApplication . openSettingsURLString ) ! , options : [ : ] , completionHandler : nil )
}
}
}
2022-06-27 10:28:30 +01:00
}
}
. navigationTitle ( " Show preview " )
. navigationBarTitleDisplayMode ( . inline )
} label : {
HStack {
Text ( " Show preview " )
Spacer ( )
2023-07-19 16:37:46 +01:00
Text ( m . notificationPreview . label )
2022-06-27 10:28:30 +01:00
}
}
} header : {
2022-07-01 20:33:20 +01:00
Text ( " Push notifications " )
} footer : {
if legacyDatabase {
Text ( " Please restart the app and migrate the database to enable push notifications. " )
. font ( . callout )
. padding ( . top , 1 )
}
2022-06-27 10:28:30 +01:00
}
}
2023-07-19 16:37:46 +01:00
. disabled ( legacyDatabase )
2022-06-27 10:28:30 +01:00
}
2022-06-28 19:03:39 +01:00
private func notificationAlert ( _ alert : NotificationAlert , _ token : DeviceToken ) -> Alert {
switch alert {
case let . setMode ( mode ) :
2022-06-27 10:28:30 +01:00
return Alert (
2022-06-28 19:03:39 +01:00
title : Text ( ntfModeAlertTitle ( mode ) ) ,
2022-06-27 10:28:30 +01:00
message : Text ( ntfModeDescription ( mode ) ) ,
2022-06-28 19:03:39 +01:00
primaryButton : . default ( Text ( mode = = . off ? " Turn off " : " Enable " ) ) {
2022-07-03 19:53:07 +01:00
setNotificationsMode ( token , mode )
2022-06-27 10:28:30 +01:00
} ,
secondaryButton : . cancel ( ) {
notificationMode = m . notificationMode
}
)
2022-06-28 19:03:39 +01:00
case let . error ( title , error ) :
return Alert ( title : Text ( title ) , message : Text ( error ) )
}
}
2022-07-01 09:49:30 +01:00
private func ntfModeAlertTitle ( _ mode : NotificationsMode ) -> LocalizedStringKey {
2022-06-28 19:03:39 +01:00
switch mode {
2023-12-11 12:59:49 +00:00
case . off : return " Use only local notifications? "
2022-06-28 19:03:39 +01:00
case . periodic : return " Enable periodic notifications? "
case . instant : return " Enable instant notifications? "
}
}
2022-07-03 19:53:07 +01:00
private func setNotificationsMode ( _ token : DeviceToken , _ mode : NotificationsMode ) {
2022-06-28 19:03:39 +01:00
Task {
switch mode {
case . off :
do {
try await apiDeleteToken ( token : token )
2022-07-03 19:53:07 +01:00
await MainActor . run {
m . tokenStatus = . new
notificationMode = . off
m . notificationMode = . off
}
2022-07-01 09:49:30 +01:00
} catch let error {
2022-07-03 19:53:07 +01:00
await MainActor . run {
2022-06-28 19:03:39 +01:00
let err = responseError ( error )
logger . error ( " apiDeleteToken error: \( err ) " )
showAlert = . error ( title : " Error deleting token " , error : err )
}
2022-06-27 10:28:30 +01:00
}
2022-06-28 19:03:39 +01:00
default :
do {
2022-07-03 19:53:07 +01:00
let status = try await apiRegisterToken ( token : token , notificationMode : mode )
await MainActor . run {
m . tokenStatus = status
2022-06-28 19:03:39 +01:00
notificationMode = mode
m . notificationMode = mode
2022-07-03 19:53:07 +01:00
}
} catch let error {
await MainActor . run {
let err = responseError ( error )
logger . error ( " apiRegisterToken error: \( err ) " )
showAlert = . error ( title : " Error enabling notifications " , error : err )
2022-06-28 19:03:39 +01:00
}
}
}
2022-06-27 10:28:30 +01:00
}
}
}
2022-07-01 09:49:30 +01:00
func ntfModeDescription ( _ mode : NotificationsMode ) -> LocalizedStringKey {
2022-06-27 10:28:30 +01:00
switch mode {
2022-07-03 19:53:07 +01:00
case . off : return " **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). "
case . periodic : return " **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. "
2022-07-04 11:31:30 +01:00
case . instant : return " **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. "
2022-06-27 10:28:30 +01:00
}
}
struct SelectionListView < Item : SelectableItem > : View {
var list : [ Item ]
2023-07-19 16:37:46 +01:00
@ Binding var selection : Item
2022-06-27 10:28:30 +01:00
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 {
2022-07-03 19:53:07 +01:00
if selection = = item { return }
2022-06-27 10:28:30 +01:00
if let f = onSelection {
f ( item )
} else {
selection = item
}
}
. _onButtonGesture { down in
if down {
tapped = item
} else {
tapped = nil
}
} perform : { }
}
. environment ( \ . editMode , . constant ( . active ) )
}
}
2022-06-28 19:03:39 +01:00
enum NotificationAlert : Identifiable {
2022-07-01 09:49:30 +01:00
case setMode ( mode : NotificationsMode )
2022-06-28 19:03:39 +01:00
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 ) "
}
}
}
2022-06-27 10:28:30 +01:00
struct NotificationsView_Previews : PreviewProvider {
static var previews : some View {
NotificationsView ( )
}
}