@ -70,14 +70,18 @@ struct CountdownEditView : View {
// @ F o c u s S t a t e p r i v a t e v a r t e x t F i e l d I s F o c u s e d : B o o l
@ FetchRequest ( sortDescriptors : [ ] )
private var timers : FetchedResults < AbstractTimer >
// @ F e t c h R e q u e s t ( s o r t D e s c r i p t o r s : [ ] )
// p r i v a t e v a r t i m e r s : F e t c h e d R e s u l t s < A b s t r a c t T i m e r >
@ State var _isNewCountdown : Bool = false // f a l s e i f e d i t i n g a n e x i s t i n g c o u n t d o w n
@ State var _hasLoaded = false
@ Environment ( \ . isPresented ) var envIsPresented
@ State var shouldScrollToTop : Bool = false
@ FocusState private var focusedField : CountdownField ?
init ( isPresented : Binding < Bool > , countdown : Countdown ? = nil , tabSelection : Binding < Int > ? = nil ) {
_isPresented = isPresented
self . countdown = countdown
@ -90,75 +94,118 @@ struct CountdownEditView : View {
self . tabSelection = tabSelection
}
fileprivate var _formId = " formId "
var body : some View {
NavigationStack {
Rectangle ( )
. frame ( width : 0.0 , height : 0.0 )
. onChange ( of : envIsPresented ) { newValue in
if ! newValue && ! self . _isNewCountdown {
self . _save ( ) // s a v e w h e n l e a v i n g a n e d i t s c r e e n
}
}
CountdownFormView (
secondsBinding : $ secondsString ,
minutesBinding : $ minutesString ,
nameBinding : $ nameString ,
imageBinding : $ image ,
repeatCountBinding : $ soundRepeatCount )
. environmentObject ( self . model )
. onAppear {
self . _onAppear ( )
}
. confirmationDialog ( " " , isPresented : $ deleteConfirmationShown , actions : {
Button ( " Yes " , role : . destructive ) {
withAnimation {
self . _delete ( )
ScrollViewReader { reader in
Rectangle ( )
. frame ( width : 0.0 , height : 0.0 )
. onChange ( of : envIsPresented ) { newValue in
if ! newValue && ! self . _isNewCountdown {
self . _save ( ) // s a v e w h e n l e a v i n g a n e d i t s c r e e n
}
}
} . keyboardShortcut ( . defaultAction )
Button ( " No " , role : . cancel ) { }
} , message : {
Text ( " Do you really want to delete? " )
} )
. confirmationDialog ( " " , isPresented : $ activityNameConfirmationShown , actions : {
Button ( " Rename " ) {
self . _rename = true
self . _save ( )
}
Button ( " New activity " ) {
self . _rename = false
self . _save ( )
}
} , message : {
Text ( " Do you wish to rename or create a new activity " )
} )
. alert ( " " , isPresented : $ errorShown , actions : { } , message : {
Text ( error ? . localizedDescription ? ? " error " )
} )
. toolbar {
if self . _isNewCountdown {
ToolbarItem ( placement : . navigationBarLeading ) {
Button ( " Cancel " ) {
self . _cancel ( )
}
Form {
EmptyView ( ) . id ( " anchor " )
CountdownFormView (
focusedField : _focusedField ,
secondsBinding : $ secondsString ,
minutesBinding : $ minutesString ,
nameBinding : $ nameString ,
imageBinding : $ image ,
repeatCountBinding : $ soundRepeatCount )
. environmentObject ( self . model )
BasePresetsView { preset in
self . _loadPreset ( preset )
}
ToolbarItem ( placement : . navigationBarTrailing ) {
Button ( " Save " ) {
self . _save ( )
} . toolbar {
ToolbarItemGroup ( placement : . keyboard ) {
Button {
self . focusedField = nil
} label : {
Image ( systemName : " keyboard.chevron.compact.down " )
}
Spacer ( )
Button {
self . focusPreviousField ( $ focusedField )
} label : {
Image ( systemName : " chevron.up " )
}
}
} else {
ToolbarItem ( placement : . navigationBarTrailing ) {
Button {
self . deleteConfirmationShown = true
self . focusNextField ( $ focusedField )
} label : {
Image ( systemName : " trash " )
Image ( systemName : " chevron.down " )
}
}
}
. onChange ( of : self . shouldScrollToTop ) { newValue in
withAnimation {
reader . scrollTo ( " anchor " )
}
}
}
. navigationTitle ( " Edit countdown " )
}
. onAppear {
self . _onAppear ( )
}
. confirmationDialog ( " " , isPresented : $ deleteConfirmationShown , actions : {
Button ( " Yes " , role : . destructive ) {
withAnimation {
self . _delete ( )
}
} . keyboardShortcut ( . defaultAction )
Button ( " No " , role : . cancel ) { }
} , message : {
Text ( " Do you really want to delete? " )
} )
. confirmationDialog ( " " , isPresented : $ activityNameConfirmationShown , actions : {
Button ( " Rename " ) {
self . _rename = true
self . _save ( )
}
Button ( " New activity " ) {
self . _rename = false
self . _save ( )
}
} , message : {
Text ( " Do you wish to rename or create a new activity " )
} )
. alert ( " " , isPresented : $ errorShown , actions : { } , message : {
Text ( error ? . localizedDescription ? ? " error " )
} )
. toolbar {
if self . _isNewCountdown {
ToolbarItem ( placement : . navigationBarLeading ) {
Button ( " Cancel " ) {
self . _cancel ( )
}
}
ToolbarItem ( placement : . navigationBarTrailing ) {
Button ( " Save " ) {
self . _save ( )
}
}
} else {
ToolbarItem ( placement : . navigationBarTrailing ) {
Button {
self . deleteConfirmationShown = true
} label : {
Image ( systemName : " trash " )
}
}
}
}
}
@ -185,16 +232,20 @@ struct CountdownEditView : View {
let minutes = Int ( preset . duration / 60.0 )
if minutes > 0 {
self . minutesString = nf . string ( from : NSNumber ( value : minutes ) ) ? ? " "
} else {
self . minutesString = " "
}
let seconds = Int ( preset . duration ) - minutes * 60
if seconds > 0 {
self . secondsString = nf . string ( from : NSNumber ( value : seconds ) ) ? ? " "
} else {
self . secondsString = " "
}
self . model . group = preset . intervalGroup
self . model . soundModel . sounds = preset . sound
self . model . soundModel . loadPreset ( preset )
self . shouldScrollToTop . toggle ( )
}
fileprivate func _loadCountdown ( _ countdown : Countdown ) {
@ -235,7 +286,7 @@ struct CountdownEditView : View {
}
fileprivate func _cancel ( ) {
viewContext . rollback ( )
self . viewContext . rollback ( )
self . isPresented = false
}
@ -251,9 +302,18 @@ struct CountdownEditView : View {
cd . duration = self . _minutes * 60.0 + self . _seconds
if self . _isNewCountdown {
let max : Int16
if let maxOrder = self . timers . map ( { $0 . order } ) . max ( ) {
max = maxOrder + 1
} else {
do {
let request = AbstractTimer . fetchRequest ( )
let timers = try viewContext . fetch ( request )
if let maxOrder = timers . map ( { $0 . order } ) . max ( ) {
max = maxOrder + 1
} else {
max = 0
}
} catch {
max = 0
}
cd . order = max
@ -303,9 +363,7 @@ struct CountdownEditView : View {
dismiss ( )
}
// i f s e l f . p r e s e t ! = n i l {
self . tabSelection ? . wrappedValue = 1
// }
self . tabSelection ? . wrappedValue = 1
}
@ -340,7 +398,8 @@ struct CountdownEditView : View {
struct NewCountdownView_Previews : PreviewProvider {
static var previews : some View {
NewCountdownView ( isPresented : . constant ( true ) , tabSelection : . constant ( 0 ) )
NewCountdownView ( isPresented : . constant ( true ) ,
tabSelection : . constant ( 0 ) )
. environment ( \ . managedObjectContext , PersistenceController . preview . container . viewContext )
}
}