Model and presets for multiple intervals

release
Laurent 3 years ago
parent f2656068d0
commit de5452f859
  1. 2
      LaunchWidget/SingleTimerView.swift
  2. 80
      LeCountdown.xcodeproj/project.pbxproj
  3. 4
      LeCountdown/CountdownScheduler.swift
  4. 3
      LeCountdown/Model/Generation/Countdown+CoreDataProperties.swift
  5. 15
      LeCountdown/Model/Generation/Interval+CoreDataClass.swift
  6. 27
      LeCountdown/Model/Generation/Interval+CoreDataProperties.swift
  7. 15
      LeCountdown/Model/Generation/IntervalGroup+CoreDataClass.swift
  8. 27
      LeCountdown/Model/Generation/IntervalGroup+CoreDataProperties.swift
  9. 2
      LeCountdown/Model/LeCountdown.xcdatamodeld/.xccurrentversion
  10. 48
      LeCountdown/Model/LeCountdown.xcdatamodeld/LeCountdown.0.6.xcdatamodel/contents
  11. 162
      LeCountdown/Views/ContentView.swift
  12. 139
      LeCountdown/Views/PresetsView.swift

@ -73,7 +73,7 @@ struct LockScreenCountdownView: View {
case .systemSmall, .systemMedium, .systemLarge, .systemExtraLarge:
return .body
case .accessoryCircular:
return .footnote
return Font.system(.callout, weight: .medium)
default:
return .body
}

@ -88,6 +88,25 @@
C4BA2AFD299A3A3700CB4FBA /* AppleMusicPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AFC299A3A3700CB4FBA /* AppleMusicPlayer.swift */; };
C4BA2B04299A42EF00CB4FBA /* NewDataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B03299A42EF00CB4FBA /* NewDataView.swift */; };
C4BA2B06299A8F8D00CB4FBA /* PresetsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B05299A8F8D00CB4FBA /* PresetsView.swift */; };
C4BA2B0F299BE61E00CB4FBA /* Interval+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0A299BE61E00CB4FBA /* Interval+CoreDataClass.swift */; };
C4BA2B10299BE61E00CB4FBA /* Interval+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0B299BE61E00CB4FBA /* Interval+CoreDataProperties.swift */; };
C4BA2B11299BE61E00CB4FBA /* IntervalGroup+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0C299BE61E00CB4FBA /* IntervalGroup+CoreDataClass.swift */; };
C4BA2B12299BE61E00CB4FBA /* IntervalGroup+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0D299BE61E00CB4FBA /* IntervalGroup+CoreDataProperties.swift */; };
C4BA2B13299BE61E00CB4FBA /* Countdown+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0E299BE61E00CB4FBA /* Countdown+CoreDataProperties.swift */; };
C4BA2B14299BE6A000CB4FBA /* IntervalGroup+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0D299BE61E00CB4FBA /* IntervalGroup+CoreDataProperties.swift */; };
C4BA2B15299BE6A000CB4FBA /* Interval+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0B299BE61E00CB4FBA /* Interval+CoreDataProperties.swift */; };
C4BA2B16299BE6A000CB4FBA /* IntervalGroup+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0C299BE61E00CB4FBA /* IntervalGroup+CoreDataClass.swift */; };
C4BA2B17299BE6A000CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AEC2996A11900CB4FBA /* Stopwatch+CoreDataProperties.swift */; };
C4BA2B18299BE6A000CB4FBA /* Interval+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0A299BE61E00CB4FBA /* Interval+CoreDataClass.swift */; };
C4BA2B19299BE6A000CB4FBA /* Countdown+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0E299BE61E00CB4FBA /* Countdown+CoreDataProperties.swift */; };
C4BA2B1A299BE6A100CB4FBA /* IntervalGroup+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0D299BE61E00CB4FBA /* IntervalGroup+CoreDataProperties.swift */; };
C4BA2B1B299BE6A100CB4FBA /* Interval+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0B299BE61E00CB4FBA /* Interval+CoreDataProperties.swift */; };
C4BA2B1C299BE6A100CB4FBA /* IntervalGroup+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0C299BE61E00CB4FBA /* IntervalGroup+CoreDataClass.swift */; };
C4BA2B1D299BE6A100CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AEC2996A11900CB4FBA /* Stopwatch+CoreDataProperties.swift */; };
C4BA2B1E299BE6A100CB4FBA /* Interval+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0A299BE61E00CB4FBA /* Interval+CoreDataClass.swift */; };
C4BA2B1F299BE6A100CB4FBA /* Countdown+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B0E299BE61E00CB4FBA /* Countdown+CoreDataProperties.swift */; };
C4BA2B22299BE82E00CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AEF2996A11900CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift */; };
C4BA2B23299BE82E00CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AEF2996A11900CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift */; };
C4F8B1532987FE6F005C86A5 /* LaunchWidgetLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7D72981216200BF3EF9 /* LaunchWidgetLiveActivity.swift */; };
C4F8B15729891271005C86A5 /* Conductor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B15629891271005C86A5 /* Conductor.swift */; };
C4F8B15929891528005C86A5 /* forest_stream.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C4F8B15829891528005C86A5 /* forest_stream.mp3 */; };
@ -103,7 +122,6 @@
C4F8B17D298AC234005C86A5 /* Record+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B16F298AC234005C86A5 /* Record+CoreDataProperties.swift */; };
C4F8B17E298AC234005C86A5 /* Alarm+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B170298AC234005C86A5 /* Alarm+CoreDataClass.swift */; };
C4F8B182298AC234005C86A5 /* Stopwatch+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B174298AC234005C86A5 /* Stopwatch+CoreDataClass.swift */; };
C4F8B183298AC234005C86A5 /* Stopwatch+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B175298AC234005C86A5 /* Stopwatch+CoreDataProperties.swift */; };
C4F8B184298AC234005C86A5 /* AbstractTimer+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B176298AC234005C86A5 /* AbstractTimer+CoreDataClass.swift */; };
C4F8B185298AC234005C86A5 /* AbstractTimer+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B177298AC234005C86A5 /* AbstractTimer+CoreDataProperties.swift */; };
C4F8B186298AC234005C86A5 /* Activity+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B178298AC234005C86A5 /* Activity+CoreDataClass.swift */; };
@ -113,7 +131,6 @@
C4F8B18D298AC288005C86A5 /* Stopwatch+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B174298AC234005C86A5 /* Stopwatch+CoreDataClass.swift */; };
C4F8B18E298AC288005C86A5 /* AbstractTimer+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B177298AC234005C86A5 /* AbstractTimer+CoreDataProperties.swift */; };
C4F8B18F298AC288005C86A5 /* AbstractTimer+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B176298AC234005C86A5 /* AbstractTimer+CoreDataClass.swift */; };
C4F8B190298AC288005C86A5 /* Stopwatch+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B175298AC234005C86A5 /* Stopwatch+CoreDataProperties.swift */; };
C4F8B192298AC288005C86A5 /* Activity+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B179298AC234005C86A5 /* Activity+CoreDataProperties.swift */; };
C4F8B193298AC288005C86A5 /* Countdown+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B16C298AC234005C86A5 /* Countdown+CoreDataClass.swift */; };
C4F8B194298AC288005C86A5 /* Record+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B16F298AC234005C86A5 /* Record+CoreDataProperties.swift */; };
@ -123,23 +140,16 @@
C4F8B19B298AC288005C86A5 /* Stopwatch+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B174298AC234005C86A5 /* Stopwatch+CoreDataClass.swift */; };
C4F8B19C298AC288005C86A5 /* AbstractTimer+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B177298AC234005C86A5 /* AbstractTimer+CoreDataProperties.swift */; };
C4F8B19D298AC288005C86A5 /* AbstractTimer+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B176298AC234005C86A5 /* AbstractTimer+CoreDataClass.swift */; };
C4F8B19E298AC288005C86A5 /* Stopwatch+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B175298AC234005C86A5 /* Stopwatch+CoreDataProperties.swift */; };
C4F8B1A0298AC288005C86A5 /* Activity+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B179298AC234005C86A5 /* Activity+CoreDataProperties.swift */; };
C4F8B1A1298AC288005C86A5 /* Countdown+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B16C298AC234005C86A5 /* Countdown+CoreDataClass.swift */; };
C4F8B1A2298AC288005C86A5 /* Record+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B16F298AC234005C86A5 /* Record+CoreDataProperties.swift */; };
C4F8B1A3298AC288005C86A5 /* Activity+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B178298AC234005C86A5 /* Activity+CoreDataClass.swift */; };
C4F8B1A7298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1A5298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataClass.swift */; };
C4F8B1A8298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1A6298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataProperties.swift */; };
C4F8B1AB298AC3A0005C86A5 /* Countdown+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1A9298AC3A0005C86A5 /* Countdown+CoreDataProperties.swift */; };
C4F8B1AC298AC3A0005C86A5 /* Alarm+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1AA298AC3A0005C86A5 /* Alarm+CoreDataProperties.swift */; };
C4F8B1AD298AC451005C86A5 /* AbstractSoundTimer+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1A5298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataClass.swift */; };
C4F8B1AE298AC451005C86A5 /* Alarm+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1AA298AC3A0005C86A5 /* Alarm+CoreDataProperties.swift */; };
C4F8B1AF298AC451005C86A5 /* Countdown+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1A9298AC3A0005C86A5 /* Countdown+CoreDataProperties.swift */; };
C4F8B1B0298AC451005C86A5 /* AbstractSoundTimer+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1A6298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataProperties.swift */; };
C4F8B1B1298AC451005C86A5 /* AbstractSoundTimer+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1A5298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataClass.swift */; };
C4F8B1B2298AC451005C86A5 /* Alarm+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1AA298AC3A0005C86A5 /* Alarm+CoreDataProperties.swift */; };
C4F8B1B3298AC451005C86A5 /* Countdown+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1A9298AC3A0005C86A5 /* Countdown+CoreDataProperties.swift */; };
C4F8B1B4298AC451005C86A5 /* AbstractSoundTimer+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1A6298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataProperties.swift */; };
C4F8B1B8298AC81D005C86A5 /* CountdownDialView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1B7298AC81D005C86A5 /* CountdownDialView.swift */; };
C4F8B1BD298AC8DE005C86A5 /* AlarmDialView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1BC298AC8DE005C86A5 /* AlarmDialView.swift */; };
C4F8B1BF298ACA0B005C86A5 /* StopwatchDialView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1BE298ACA0B005C86A5 /* StopwatchDialView.swift */; };
@ -271,6 +281,12 @@
C4BA2AFE299A3A9E00CB4FBA /* MusicKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MusicKit.framework; path = System/Library/Frameworks/MusicKit.framework; sourceTree = SDKROOT; };
C4BA2B03299A42EF00CB4FBA /* NewDataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewDataView.swift; sourceTree = "<group>"; };
C4BA2B05299A8F8D00CB4FBA /* PresetsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetsView.swift; sourceTree = "<group>"; };
C4BA2B07299BDAE000CB4FBA /* LeCountdown.0.6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.6.xcdatamodel; sourceTree = "<group>"; };
C4BA2B0A299BE61E00CB4FBA /* Interval+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Interval+CoreDataClass.swift"; sourceTree = "<group>"; };
C4BA2B0B299BE61E00CB4FBA /* Interval+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Interval+CoreDataProperties.swift"; sourceTree = "<group>"; };
C4BA2B0C299BE61E00CB4FBA /* IntervalGroup+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IntervalGroup+CoreDataClass.swift"; sourceTree = "<group>"; };
C4BA2B0D299BE61E00CB4FBA /* IntervalGroup+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IntervalGroup+CoreDataProperties.swift"; sourceTree = "<group>"; };
C4BA2B0E299BE61E00CB4FBA /* Countdown+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Countdown+CoreDataProperties.swift"; sourceTree = "<group>"; };
C4F8B15629891271005C86A5 /* Conductor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conductor.swift; sourceTree = "<group>"; };
C4F8B15829891528005C86A5 /* forest_stream.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = forest_stream.mp3; sourceTree = "<group>"; };
C4F8B15E298961A7005C86A5 /* ReorderableForEach.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReorderableForEach.swift; sourceTree = "<group>"; };
@ -285,14 +301,11 @@
C4F8B16F298AC234005C86A5 /* Record+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Record+CoreDataProperties.swift"; sourceTree = "<group>"; };
C4F8B170298AC234005C86A5 /* Alarm+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Alarm+CoreDataClass.swift"; sourceTree = "<group>"; };
C4F8B174298AC234005C86A5 /* Stopwatch+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Stopwatch+CoreDataClass.swift"; sourceTree = "<group>"; };
C4F8B175298AC234005C86A5 /* Stopwatch+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Stopwatch+CoreDataProperties.swift"; sourceTree = "<group>"; };
C4F8B176298AC234005C86A5 /* AbstractTimer+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AbstractTimer+CoreDataClass.swift"; sourceTree = "<group>"; };
C4F8B177298AC234005C86A5 /* AbstractTimer+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AbstractTimer+CoreDataProperties.swift"; sourceTree = "<group>"; };
C4F8B178298AC234005C86A5 /* Activity+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Activity+CoreDataClass.swift"; sourceTree = "<group>"; };
C4F8B179298AC234005C86A5 /* Activity+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Activity+CoreDataProperties.swift"; sourceTree = "<group>"; };
C4F8B1A5298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AbstractSoundTimer+CoreDataClass.swift"; sourceTree = "<group>"; };
C4F8B1A6298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AbstractSoundTimer+CoreDataProperties.swift"; sourceTree = "<group>"; };
C4F8B1A9298AC3A0005C86A5 /* Countdown+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Countdown+CoreDataProperties.swift"; sourceTree = "<group>"; };
C4F8B1AA298AC3A0005C86A5 /* Alarm+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Alarm+CoreDataProperties.swift"; sourceTree = "<group>"; };
C4F8B1B7298AC81D005C86A5 /* CountdownDialView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountdownDialView.swift; sourceTree = "<group>"; };
C4F8B1BC298AC8DE005C86A5 /* AlarmDialView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmDialView.swift; sourceTree = "<group>"; };
@ -476,12 +489,12 @@
C438C80A2981DE1A00BF3EF9 /* Utils */ = {
isa = PBXGroup;
children = (
C4BA2AFC299A3A3700CB4FBA /* AppleMusicPlayer.swift */,
C4742B5629840F6400D5D950 /* CoolPic.swift */,
C438C81029829EAF00BF3EF9 /* PropertyWrappers.swift */,
C40FDB612992985C0042A390 /* TextToSpeechRecorder.swift */,
C4060DF4297AE9A7003FAB80 /* TimeInterval+Extensions.swift */,
C4742B5E2984205000D5D950 /* ViewModifiers.swift */,
C40FDB612992985C0042A390 /* TextToSpeechRecorder.swift */,
C4BA2AFC299A3A3700CB4FBA /* AppleMusicPlayer.swift */,
);
path = Utils;
sourceTree = "<group>";
@ -550,15 +563,17 @@
C4F8B170298AC234005C86A5 /* Alarm+CoreDataClass.swift */,
C4F8B1AA298AC3A0005C86A5 /* Alarm+CoreDataProperties.swift */,
C4F8B16C298AC234005C86A5 /* Countdown+CoreDataClass.swift */,
C4F8B1A9298AC3A0005C86A5 /* Countdown+CoreDataProperties.swift */,
C4BA2B0E299BE61E00CB4FBA /* Countdown+CoreDataProperties.swift */,
C4BA2AED2996A11900CB4FBA /* CustomSound+CoreDataClass.swift */,
C4BA2AEE2996A11900CB4FBA /* CustomSound+CoreDataProperties.swift */,
C4BA2B0A299BE61E00CB4FBA /* Interval+CoreDataClass.swift */,
C4BA2B0B299BE61E00CB4FBA /* Interval+CoreDataProperties.swift */,
C4BA2B0C299BE61E00CB4FBA /* IntervalGroup+CoreDataClass.swift */,
C4BA2B0D299BE61E00CB4FBA /* IntervalGroup+CoreDataProperties.swift */,
C4F8B16E298AC234005C86A5 /* Record+CoreDataClass.swift */,
C4F8B16F298AC234005C86A5 /* Record+CoreDataProperties.swift */,
C4F8B174298AC234005C86A5 /* Stopwatch+CoreDataClass.swift */,
C4BA2AEC2996A11900CB4FBA /* Stopwatch+CoreDataProperties.swift */,
C4F8B1A6298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataProperties.swift */,
C4F8B175298AC234005C86A5 /* Stopwatch+CoreDataProperties.swift */,
);
path = Generation;
sourceTree = "<group>";
@ -809,12 +824,12 @@
C4F8B1D2298BF646005C86A5 /* PermissionAlertView.swift in Sources */,
C4060DC9297AE73D003FAB80 /* Persistence.swift in Sources */,
C498E5A1298D543900E90DE0 /* LiveTimer.swift in Sources */,
C4F8B1AB298AC3A0005C86A5 /* Countdown+CoreDataProperties.swift in Sources */,
C438C80F29828B8600BF3EF9 /* RecordsView.swift in Sources */,
C4F8B187298AC234005C86A5 /* Activity+CoreDataProperties.swift in Sources */,
C438C80D2982847300BF3EF9 /* CoreDataRequests.swift in Sources */,
C4742B5F2984205000D5D950 /* ViewModifiers.swift in Sources */,
C4F8B1D8298C0727005C86A5 /* TimerRouter.swift in Sources */,
C4BA2B13299BE61E00CB4FBA /* Countdown+CoreDataProperties.swift in Sources */,
C4F8B186298AC234005C86A5 /* Activity+CoreDataClass.swift in Sources */,
C4F8B15729891271005C86A5 /* Conductor.swift in Sources */,
C4F8B17E298AC234005C86A5 /* Alarm+CoreDataClass.swift in Sources */,
@ -823,14 +838,14 @@
C438C807298195E600BF3EF9 /* Model+Extensions.swift in Sources */,
C4BA2B04299A42EF00CB4FBA /* NewDataView.swift in Sources */,
C438C7FF2981300500BF3EF9 /* IntentDataProvider.swift in Sources */,
C4F8B183298AC234005C86A5 /* Stopwatch+CoreDataProperties.swift in Sources */,
C4BA2AD62993F62700CB4FBA /* SoundSelectionView.swift in Sources */,
C4F8B1A8298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataProperties.swift in Sources */,
C498E5A5299152B400E90DE0 /* GreenCheckmarkView.swift in Sources */,
C4F8B1B8298AC81D005C86A5 /* CountdownDialView.swift in Sources */,
C445FA922987CC8A0054D761 /* Sound.swift in Sources */,
C4F8B1D0298BF2E2005C86A5 /* DialView.swift in Sources */,
C4BA2B11299BE61E00CB4FBA /* IntervalGroup+CoreDataClass.swift in Sources */,
C4BA2AFD299A3A3700CB4FBA /* AppleMusicPlayer.swift in Sources */,
C4BA2B12299BE61E00CB4FBA /* IntervalGroup+CoreDataProperties.swift in Sources */,
C4BA2AF32996A11900CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift in Sources */,
C4742B59298411E800D5D950 /* CountdownFormView.swift in Sources */,
C4BA2AF12996A11900CB4FBA /* CustomSound+CoreDataClass.swift in Sources */,
@ -855,6 +870,7 @@
C4F8B17D298AC234005C86A5 /* Record+CoreDataProperties.swift in Sources */,
C4F8B184298AC234005C86A5 /* AbstractTimer+CoreDataClass.swift in Sources */,
C4F8B17A298AC234005C86A5 /* Countdown+CoreDataClass.swift in Sources */,
C4BA2B0F299BE61E00CB4FBA /* Interval+CoreDataClass.swift in Sources */,
C4F8B164298A9A92005C86A5 /* AlarmFormView.swift in Sources */,
C40FDB622992985C0042A390 /* TextToSpeechRecorder.swift in Sources */,
C4060DF7297AFEF2003FAB80 /* NewCountdownView.swift in Sources */,
@ -864,6 +880,7 @@
C4742B5B298414B000D5D950 /* ImageSelectionView.swift in Sources */,
C438C7C5298024E900BF3EF9 /* NSManagedContext+Extensions.swift in Sources */,
C4F8B15F298961A7005C86A5 /* ReorderableForEach.swift in Sources */,
C4BA2B10299BE61E00CB4FBA /* Interval+CoreDataProperties.swift in Sources */,
C4F8B1AC298AC3A0005C86A5 /* Alarm+CoreDataProperties.swift in Sources */,
C498E59F298D4DEA00E90DE0 /* LiveTimerListView.swift in Sources */,
C4060DC0297AE73B003FAB80 /* LeCountdownApp.swift in Sources */,
@ -893,36 +910,40 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C4F8B1AF298AC451005C86A5 /* Countdown+CoreDataProperties.swift in Sources */,
C4BA2B15299BE6A000CB4FBA /* Interval+CoreDataProperties.swift in Sources */,
C445FA932987CF280054D761 /* Sound.swift in Sources */,
C498E5A6299152C600E90DE0 /* GreenCheckmarkView.swift in Sources */,
C438C7EB2981266F00BF3EF9 /* SingleTimerView.swift in Sources */,
C438C7D62981216200BF3EF9 /* LaunchWidgetBundle.swift in Sources */,
C4BA2B16299BE6A000CB4FBA /* IntervalGroup+CoreDataClass.swift in Sources */,
C4F8B18B298AC288005C86A5 /* Record+CoreDataClass.swift in Sources */,
C4F8B195298AC288005C86A5 /* Activity+CoreDataClass.swift in Sources */,
C4BA2B22299BE82E00CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift in Sources */,
C4F8B193298AC288005C86A5 /* Countdown+CoreDataClass.swift in Sources */,
C4BA2B17299BE6A000CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */,
C4BA2AF72996A4EF00CB4FBA /* CustomSound+CoreDataClass.swift in Sources */,
C4F8B1AD298AC451005C86A5 /* AbstractSoundTimer+CoreDataClass.swift in Sources */,
C445FA87298448730054D761 /* CoolPic.swift in Sources */,
C438C8172982BE9C00BF3EF9 /* Model+Extensions.swift in Sources */,
C438C8162982BE1E00BF3EF9 /* LeCountdown.xcdatamodeld in Sources */,
C4F8B194298AC288005C86A5 /* Record+CoreDataProperties.swift in Sources */,
C4F8B190298AC288005C86A5 /* Stopwatch+CoreDataProperties.swift in Sources */,
C438C8152982BD9000BF3EF9 /* IntentDataProvider.swift in Sources */,
C438C7DF2981216300BF3EF9 /* LaunchWidget.intentdefinition in Sources */,
C4F8B1B0298AC451005C86A5 /* AbstractSoundTimer+CoreDataProperties.swift in Sources */,
C4F8B15B29892D40005C86A5 /* SoundPlayer.swift in Sources */,
C438C7E82981255D00BF3EF9 /* TimeInterval+Extensions.swift in Sources */,
C4F8B18F298AC288005C86A5 /* AbstractTimer+CoreDataClass.swift in Sources */,
C4BA2B19299BE6A000CB4FBA /* Countdown+CoreDataProperties.swift in Sources */,
C438C7D82981216200BF3EF9 /* LaunchWidgetLiveActivity.swift in Sources */,
C4F8B18C298AC288005C86A5 /* Alarm+CoreDataClass.swift in Sources */,
C438C8192982BFDB00BF3EF9 /* NSManagedContext+Extensions.swift in Sources */,
C438C7DA2981216200BF3EF9 /* LaunchWidget.swift in Sources */,
C4BA2AF62996A4EF00CB4FBA /* CustomSound+CoreDataProperties.swift in Sources */,
C4F8B192298AC288005C86A5 /* Activity+CoreDataProperties.swift in Sources */,
C4BA2B18299BE6A000CB4FBA /* Interval+CoreDataClass.swift in Sources */,
C4F8B18E298AC288005C86A5 /* AbstractTimer+CoreDataProperties.swift in Sources */,
C4F8B1AE298AC451005C86A5 /* Alarm+CoreDataProperties.swift in Sources */,
C4F8B18D298AC288005C86A5 /* Stopwatch+CoreDataClass.swift in Sources */,
C4BA2B14299BE6A000CB4FBA /* IntervalGroup+CoreDataProperties.swift in Sources */,
C438C8182982BFC100BF3EF9 /* Persistence.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -938,25 +959,29 @@
C4F8B1A1298AC288005C86A5 /* Countdown+CoreDataClass.swift in Sources */,
C438C7F529812BB200BF3EF9 /* IntentHandler.swift in Sources */,
C4F8B19C298AC288005C86A5 /* AbstractTimer+CoreDataProperties.swift in Sources */,
C4BA2B1C299BE6A100CB4FBA /* IntervalGroup+CoreDataClass.swift in Sources */,
C4F8B1C0298ACA61005C86A5 /* Model+Extensions.swift in Sources */,
C4BA2B1F299BE6A100CB4FBA /* Countdown+CoreDataProperties.swift in Sources */,
C4F8B1A3298AC288005C86A5 /* Activity+CoreDataClass.swift in Sources */,
C4F8B1C3298ACBDB005C86A5 /* Sound.swift in Sources */,
C4F8B1B3298AC451005C86A5 /* Countdown+CoreDataProperties.swift in Sources */,
C4F8B1B4298AC451005C86A5 /* AbstractSoundTimer+CoreDataProperties.swift in Sources */,
C4F8B19A298AC288005C86A5 /* Alarm+CoreDataClass.swift in Sources */,
C438C80529813FB400BF3EF9 /* TimeInterval+Extensions.swift in Sources */,
C4BA2B1B299BE6A100CB4FBA /* Interval+CoreDataProperties.swift in Sources */,
C438C802298132B900BF3EF9 /* LeCountdown.xcdatamodeld in Sources */,
C4F8B19B298AC288005C86A5 /* Stopwatch+CoreDataClass.swift in Sources */,
C4F8B1A0298AC288005C86A5 /* Activity+CoreDataProperties.swift in Sources */,
C438C81A2982BFF100BF3EF9 /* NSManagedContext+Extensions.swift in Sources */,
C4F8B19D298AC288005C86A5 /* AbstractTimer+CoreDataClass.swift in Sources */,
C4BA2AF82996A4F000CB4FBA /* CustomSound+CoreDataProperties.swift in Sources */,
C4F8B19E298AC288005C86A5 /* Stopwatch+CoreDataProperties.swift in Sources */,
C4F8B199298AC288005C86A5 /* Record+CoreDataClass.swift in Sources */,
C438C8012981327600BF3EF9 /* Persistence.swift in Sources */,
C438C7FD29812BF700BF3EF9 /* LaunchWidget.intentdefinition in Sources */,
C4F8B1B1298AC451005C86A5 /* AbstractSoundTimer+CoreDataClass.swift in Sources */,
C4BA2B1E299BE6A100CB4FBA /* Interval+CoreDataClass.swift in Sources */,
C4BA2B1A299BE6A100CB4FBA /* IntervalGroup+CoreDataProperties.swift in Sources */,
C4BA2B1D299BE6A100CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */,
C445FA86298448720054D761 /* CoolPic.swift in Sources */,
C4BA2B23299BE82E00CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift in Sources */,
C438C800298130E900BF3EF9 /* IntentDataProvider.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -1412,6 +1437,7 @@
C4060DCA297AE73D003FAB80 /* LeCountdown.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
C4BA2B07299BDAE000CB4FBA /* LeCountdown.0.6.xcdatamodel */,
C4BA2AEB2996A09600CB4FBA /* LeCountdown.0.5.1.xcdatamodel */,
C4BA2AD72993F7D200CB4FBA /* LeCountdown.0.5.xcdatamodel */,
C40FDB672993D5E80042A390 /* LeCountdown.0.4.xcdatamodel */,
@ -1420,7 +1446,7 @@
C418A14F298428CB00C22230 /* LeCountdown.0.1.xcdatamodel */,
C4060DCB297AE73D003FAB80 /* LeCountdown.xcdatamodel */,
);
currentVersion = C4BA2AEB2996A09600CB4FBA /* LeCountdown.0.5.1.xcdatamodel */;
currentVersion = C4BA2B07299BDAE000CB4FBA /* LeCountdown.0.6.xcdatamodel */;
path = LeCountdown.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;

@ -45,7 +45,9 @@ class CountdownScheduler {
let sound = countdown.soundName
print("Selected sound = \(sound)")
content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: sound))
// content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: sound))
content.sound = UNNotificationSound.criticalSoundNamed(UNNotificationSoundName(rawValue: sound), withAudioVolume: 1.0)
content.interruptionLevel = .critical
content.relevanceScore = 1.0

@ -2,7 +2,7 @@
// Countdown+CoreDataProperties.swift
// LeCountdown
//
// Created by Laurent Morvillier on 01/02/2023.
// Created by Laurent Morvillier on 14/02/2023.
//
//
@ -17,5 +17,6 @@ extension Countdown {
}
@NSManaged public var duration: Double
@NSManaged public var group: IntervalGroup?
}

@ -0,0 +1,15 @@
//
// Interval+CoreDataClass.swift
// LeCountdown
//
// Created by Laurent Morvillier on 14/02/2023.
//
//
import Foundation
import CoreData
@objc(Interval)
public class Interval: NSManagedObject {
}

@ -0,0 +1,27 @@
//
// Interval+CoreDataProperties.swift
// LeCountdown
//
// Created by Laurent Morvillier on 14/02/2023.
//
//
import Foundation
import CoreData
extension Interval {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Interval> {
return NSFetchRequest<Interval>(entityName: "Interval")
}
@NSManaged public var duration: Double
@NSManaged public var soundList: String?
@NSManaged public var group: IntervalGroup?
}
extension Interval : Identifiable {
}

@ -0,0 +1,15 @@
//
// IntervalGroup+CoreDataClass.swift
// LeCountdown
//
// Created by Laurent Morvillier on 14/02/2023.
//
//
import Foundation
import CoreData
@objc(IntervalGroup)
public class IntervalGroup: NSManagedObject {
}

@ -0,0 +1,27 @@
//
// IntervalGroup+CoreDataProperties.swift
// LeCountdown
//
// Created by Laurent Morvillier on 14/02/2023.
//
//
import Foundation
import CoreData
extension IntervalGroup {
@nonobjc public class func fetchRequest() -> NSFetchRequest<IntervalGroup> {
return NSFetchRequest<IntervalGroup>(entityName: "IntervalGroup")
}
@NSManaged public var repeatCount: Int16
@NSManaged public var intervals: Interval?
@NSManaged public var countdown: Countdown?
}
extension IntervalGroup : Identifiable {
}

@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>LeCountdown.0.5.1.xcdatamodel</string>
<string>LeCountdown.0.6.xcdatamodel</string>
</dict>
</plist>

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22A400" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
<entity name="AbstractSoundTimer" representedClassName="AbstractSoundTimer" isAbstract="YES" parentEntity="AbstractTimer" syncable="YES">
<attribute name="repeatCount" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="soundList" optional="YES" attributeType="String"/>
</entity>
<entity name="AbstractTimer" representedClassName="AbstractTimer" isAbstract="YES" syncable="YES">
<attribute name="image" optional="YES" attributeType="String"/>
<attribute name="order" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="activity" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Activity" inverseName="timers" inverseEntity="Activity"/>
</entity>
<entity name="Activity" representedClassName="Activity" syncable="YES">
<attribute name="name" attributeType="String" defaultValueString=""/>
<relationship name="records" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Record" inverseName="activity" inverseEntity="Record"/>
<relationship name="timers" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="AbstractTimer" inverseName="activity" inverseEntity="AbstractTimer"/>
</entity>
<entity name="Alarm" representedClassName="Alarm" parentEntity="AbstractSoundTimer" syncable="YES">
<attribute name="fireDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
</entity>
<entity name="Countdown" representedClassName="Countdown" parentEntity="AbstractSoundTimer" syncable="YES">
<attribute name="duration" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="group" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="IntervalGroup" inverseName="countdown" inverseEntity="IntervalGroup"/>
</entity>
<entity name="CustomSound" representedClassName="CustomSound" syncable="YES">
<attribute name="file" optional="YES" attributeType="String"/>
<attribute name="text" optional="YES" attributeType="String"/>
</entity>
<entity name="Interval" representedClassName="Interval" syncable="YES">
<attribute name="duration" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="soundList" optional="YES" attributeType="String"/>
<relationship name="group" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="IntervalGroup" inverseName="intervals" inverseEntity="IntervalGroup"/>
</entity>
<entity name="IntervalGroup" representedClassName="IntervalGroup" syncable="YES">
<attribute name="repeatCount" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="countdown" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Countdown" inverseName="group" inverseEntity="Countdown"/>
<relationship name="intervals" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Interval" inverseName="group" inverseEntity="Interval"/>
</entity>
<entity name="Record" representedClassName="Record" syncable="YES">
<attribute name="end" attributeType="Date" defaultDateTimeInterval="696425400" usesScalarValueType="NO"/>
<attribute name="start" attributeType="Date" defaultDateTimeInterval="696425400" usesScalarValueType="NO"/>
<relationship name="activity" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Activity" inverseName="records" inverseEntity="Activity"/>
</entity>
<entity name="Stopwatch" representedClassName="Stopwatch" parentEntity="AbstractSoundTimer" syncable="YES">
<attribute name="end" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="sound" optional="YES" attributeType="Integer 16" usesScalarValueType="YES"/>
<attribute name="start" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
</entity>
</model>

@ -86,106 +86,106 @@ struct ContentView<T : AbstractTimer>: View {
var body: some View {
GeometryReader { reader in
let width: CGFloat = reader.size.width / 2 - 15.0
GeometryReader { reader in
let width: CGFloat = reader.size.width / 2 - 15.0
VStack {
VStack {
ScrollView {
ScrollView {
LazyVGrid(
columns: columns,
spacing: itemSpacing
) {
LazyVGrid(
columns: columns,
spacing: itemSpacing
) {
if !self.isEditing {
if !self.isEditing {
ForEach(self.model.spots) { spot in
if let timer = spot.timer {
DialView(timer: timer, isEditingBinding: self.$isEditing, frameSize: width)
.environment(\.managedObjectContext, viewContext)
.environmentObject(Conductor.maestro)
.environmentObject(boringContext)
} else {
Color.clear
.frame(width: width, height: 80.0)
.cornerRadius(20.0)
}
ForEach(self.model.spots) { spot in
if let timer = spot.timer {
DialView(timer: timer, isEditingBinding: self.$isEditing, frameSize: width)
.environment(\.managedObjectContext, viewContext)
.environmentObject(Conductor.maestro)
.environmentObject(boringContext)
} else {
Color.clear
.frame(width: width, height: 80.0)
.cornerRadius(20.0)
}
}
} else {
ReorderableForEach(items: self.model.spots) { spot in
} else {
ReorderableForEach(items: self.model.spots) { spot in
if let timer = spot.timer {
DialView(timer: timer, isEditingBinding: self.$isEditing, frameSize: width)
.environment(\.managedObjectContext, viewContext)
.environmentObject(Conductor.maestro)
.environmentObject(boringContext)
if let timer = spot.timer {
DialView(timer: timer, isEditingBinding: self.$isEditing, frameSize: width)
.environment(\.managedObjectContext, viewContext)
.environmentObject(Conductor.maestro)
.environmentObject(boringContext)
} else {
Color(white: 0.9)
.frame(width: width, height: 80.0)
.cornerRadius(20.0)
}
} else {
} moveAction: { from, to in
self._reorderSpots(from: from, to: to)
Color(white: 0.9)
.frame(width: width, height: 80.0)
.cornerRadius(20.0)
}
} moveAction: { from, to in
self._reorderSpots(from: from, to: to)
}
}
}.padding(.horizontal, itemSpacing)
if !conductor.liveTimers.isEmpty {
LiveTimerListView()
.environment(\.managedObjectContext, viewContext)
.environmentObject(conductor)
.background(Color(white: 0.9))
.padding(.bottom, 40.0)
.cornerRadius(16.0, corners: [.topRight, .topLeft])
}
}.padding(.horizontal, itemSpacing)
if !conductor.liveTimers.isEmpty {
LiveTimerListView()
.environment(\.managedObjectContext, viewContext)
.environmentObject(conductor)
.background(Color(white: 0.9))
.padding(.bottom, 40.0)
.cornerRadius(16.0, corners: [.topRight, .topLeft])
}
}
.navigationTitle("Home")
.alert(boringContext.error?.localizedDescription ?? "missing error", isPresented: $boringContext.showDefaultAlert) {
Button("OK", role: .cancel) { }
}
.alert("You need to accept notifications, please check your settings", isPresented: $boringContext.showPermissionAlert) {
PermissionAlertView()
}
.sheet(isPresented: $boringContext.isShowingNewData, content: {
self._newView(isPresented: $boringContext.isShowingNewData)
.environment(\.managedObjectContext, viewContext)
})
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
self.boringContext.isShowingNewData = true
} label: {
HStack {
Image(systemName: "plus")
}
}
.navigationTitle("Home")
.alert(boringContext.error?.localizedDescription ?? "missing error", isPresented: $boringContext.showDefaultAlert) {
Button("OK", role: .cancel) { }
}
.alert("You need to accept notifications, please check your settings", isPresented: $boringContext.showPermissionAlert) {
PermissionAlertView()
}
.sheet(isPresented: $boringContext.isShowingNewData, content: {
self._newView(isPresented: $boringContext.isShowingNewData)
.environment(\.managedObjectContext, viewContext)
})
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
self.boringContext.isShowingNewData = true
} label: {
HStack {
Image(systemName: "plus")
}
}
ToolbarItem(placement: .navigationBarLeading) {
Button {
withAnimation {
self.isEditing.toggle()
}
} label: {
Text(self.isEditing ? "Done" : "Edit")
}
ToolbarItem(placement: .navigationBarLeading) {
Button {
withAnimation {
self.isEditing.toggle()
}
} label: {
Text(self.isEditing ? "Done" : "Edit")
}
}
.onAppear {
self._buildItemsList()
self._askPermissions()
}
.onOpenURL { url in
self._performActionIfPossible(url: url)
}
}
.onAppear {
self._buildItemsList()
self._askPermissions()
}
.onOpenURL { url in
self._performActionIfPossible(url: url)
}
}

@ -10,15 +10,15 @@ import SwiftUI
enum PresetSection: Int, Identifiable, CaseIterable {
var id: Int { return self.rawValue }
case cooking
case workout
case meditation
case chill
case cooking
var presets: [Preset] {
switch self {
case .cooking: return [.softBoiled, .mediumBoiledEggs, .hardBoiledEggs]
case .workout: return []
case .meditation: return []
case .workout: return [.runningSplits]
case .chill: return [.nap, .meditation]
}
}
@ -26,23 +26,51 @@ enum PresetSection: Int, Identifiable, CaseIterable {
switch self {
case .cooking: return NSLocalizedString("Cooking", comment: "")
case .workout: return NSLocalizedString("Workout", comment: "")
case .meditation: return NSLocalizedString("Meditation", comment: "")
case .chill: return NSLocalizedString("Chill", comment: "")
}
}
}
struct CountdownIntervalGroup {
var repeatCount: Int
var intervals: [CountdownInterval]
}
struct CountdownInterval {
var duration: TimeInterval
var sound: Sound?
}
enum Preset: Int, Identifiable, CaseIterable {
var id: Int { return self.rawValue }
case softBoiled
case mediumBoiledEggs
case hardBoiledEggs
case meditation
case nap
case runningSplits
var localizedName: String {
switch self {
case .hardBoiledEggs: return NSLocalizedString("Hard boiled eggs", comment: "")
case .softBoiled: return NSLocalizedString("Soft boiled eggs", comment: "")
case .mediumBoiledEggs: return NSLocalizedString("Medium boiled eggs", comment: "")
case .meditation: return NSLocalizedString("Meditation", comment: "")
case .nap: return NSLocalizedString("Nap", comment: "")
case .runningSplits: return NSLocalizedString("Running splits", comment: "")
}
}
var intervalGroup: CountdownIntervalGroup? {
switch self {
case .runningSplits:
let runInterval = CountdownInterval(duration: 30.0, sound: Sound.sbArpeggio_Loop_River)
let breakInterval = CountdownInterval(duration: 30.0, sound: Sound.sbLoop_ToneSD_Boavista)
return CountdownIntervalGroup(repeatCount: 8, intervals: [runInterval, breakInterval])
default:
return nil
}
}
@ -51,16 +79,38 @@ enum Preset: Int, Identifiable, CaseIterable {
case .softBoiled: return 3 * 60
case .mediumBoiledEggs: return 6 * 60
case .hardBoiledEggs: return 10 * 60
case .meditation: return 15 * 60
case .nap: return 20 * 60
case .runningSplits: return 13
}
}
var formattedDuration: String {
if let group = self.intervalGroup {
let count = group.repeatCount.formatted()
let durations = group.intervals.map { $0.duration.formatted() }
let formattedIntervals = durations.joined(separator: "/")
return "\(count) * [\(formattedIntervals)]"
} else {
return self.duration.minuteSecond
}
}
var sound: Set<Sound> {
switch self {
case .softBoiled: return []
case .mediumBoiledEggs: return []
case .hardBoiledEggs: return []
case .meditation: return []
case .nap: return []
case .runningSplits: return []
}
}
var soundTitle: String {
return "Great sound"
}
}
@ -80,24 +130,50 @@ struct PresetsView: View {
var tabSelection: Binding<Int>
private let columns: [GridItem] = [
GridItem(spacing: 10.0),
GridItem(spacing: 10.0),
]
var body: some View {
List {
ForEach(PresetSection.allCases) { section in
Section(header: Text(section.localizedName)) {
ForEach(section.presets) { preset in
Button {
self.model.selectedPreset = preset
self.isPresented = true
} label: {
Text(preset.localizedName)
VStack {
Text("You can edit the duration, sound and label before adding")
.padding()
.font(.callout)
LazyVGrid(
columns: columns,
spacing: 10.0
) {
ForEach(PresetSection.allCases) { section in
Section {
ForEach(section.presets) { preset in
Button {
self.model.selectedPreset = preset
self.isPresented = true
} label: {
TimerItemView(name: preset.localizedName, duration: preset.formattedDuration, sound: preset.soundTitle)
}
}
} header: {
HStack {
Text(section.localizedName.uppercased())
Spacer()
}
}
}
}
}.padding(.horizontal)
Spacer()
}
.sheet(isPresented: $isPresented, content: {
CountdownEditView(isPresented: $isPresented, preset: self.model.selectedPreset, tabSelection: self.tabSelection)
.environment(\.managedObjectContext, viewContext)
@ -107,8 +183,37 @@ struct PresetsView: View {
}
}
struct TimerItemView: View {
var name: String
var duration: String
var sound: String
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(name.uppercased()).multilineTextAlignment(.leading)
Text(duration)
Text(sound.uppercased()).foregroundColor(Color(white: 0.7))
}.padding()
Spacer()
}.background(Color(white: 0.15))
.cornerRadius(16.0)
.monospaced()
.font(Font.system(size: 16.0, weight: .semibold))
.foregroundColor(Color.white)
}
}
struct PresetsView_Previews: PreviewProvider {
static var previews: some View {
PresetsView(tabSelection: .constant(0))
}
}
struct TimerItemView_Previews: PreviewProvider {
static var previews: some View {
TimerItemView(name: "Hard boiled eggs", duration: "10:00", sound: "Stephan Bodzin").frame(width: UIScreen.main.bounds.width / 2.0)
}
}

Loading…
Cancel
Save