Work on onboarding

main
Laurent 3 years ago
parent afbcd4b5d5
commit e76429b7cb
  1. 4
      LeCountdown.xcodeproj/project.pbxproj
  2. 3
      LeCountdown/LeCountdownApp.swift
  3. 4
      LeCountdown/Views/ContentView.swift
  4. 69
      LeCountdown/Views/Countdown/CountdownFormView.swift
  5. 20
      LeCountdown/Views/Countdown/NewCountdownView.swift
  6. 10
      LeCountdown/Views/HomeView.swift
  7. 7
      LeCountdown/Views/NewDataView.swift
  8. 91
      LeCountdown/Views/PresetsView.swift
  9. 4
      LeCountdown/Views/Reusable/TimerModel.swift
  10. 283
      LeCountdown/Views/StartView.swift
  11. 15
      LeCountdown/Views/Stopwatch/NewStopwatchView.swift
  12. 9
      LeCountdown/fr.lproj/Localizable.strings

@ -46,6 +46,7 @@
C4286EAE2A17753A0070D075 /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4286E952A14EC4E0070D075 /* AppError.swift */; };
C4286EB02A1B75AB0070D075 /* BoringContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4286EAF2A1B75AB0070D075 /* BoringContext.swift */; };
C4286EB22A1B75C60070D075 /* BoringContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4286EAF2A1B75AB0070D075 /* BoringContext.swift */; };
C4286EB72A1B98420070D075 /* StartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4286EB62A1B98420070D075 /* StartView.swift */; };
C42E96FB29E59E72005B1B8C /* BackgroundBlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E96FA29E59E72005B1B8C /* BackgroundBlurView.swift */; };
C42E96FD29E5B06D005B1B8C /* ActivityCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E96FC29E5B06D005B1B8C /* ActivityCalendarView.swift */; };
C42E96FE29E5B5CD005B1B8C /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B6429A3C37D00CB4FBA /* Filter.swift */; };
@ -380,6 +381,7 @@
C4286E9F2A1502FD0070D075 /* Stopwatch+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Stopwatch+CoreDataClass.swift"; sourceTree = "<group>"; };
C4286EA52A150A7E0070D075 /* TimePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimePickerView.swift; sourceTree = "<group>"; };
C4286EAF2A1B75AB0070D075 /* BoringContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoringContext.swift; sourceTree = "<group>"; };
C4286EB62A1B98420070D075 /* StartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartView.swift; sourceTree = "<group>"; };
C42E96FA29E59E72005B1B8C /* BackgroundBlurView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundBlurView.swift; sourceTree = "<group>"; };
C42E96FC29E5B06D005B1B8C /* ActivityCalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityCalendarView.swift; sourceTree = "<group>"; };
C42E970129E6B32B005B1B8C /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = "<group>"; };
@ -790,6 +792,7 @@
C4BA2B03299A42EF00CB4FBA /* NewDataView.swift */,
C4BA2B05299A8F8D00CB4FBA /* PresetsView.swift */,
C4E5D68929BB7953008E7465 /* SettingsView.swift */,
C4286EB62A1B98420070D075 /* StartView.swift */,
C4E5D68529BB369E008E7465 /* TimersView.swift */,
);
path = Views;
@ -1219,6 +1222,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C4286EB72A1B98420070D075 /* StartView.swift in Sources */,
C4F8B1D2298BF646005C86A5 /* PermissionAlertView.swift in Sources */,
C4BA2B7329A60CF000CB4FBA /* Shortcut.swift in Sources */,
C4060DC9297AE73D003FAB80 /* Persistence.swift in Sources */,

@ -35,6 +35,9 @@ struct LeCountdownApp: App {
WindowGroup {
ZStack {
// StartView()
CompactHomeView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}

@ -239,10 +239,10 @@ struct MainToolbarView: ToolbarContent {
}
}
.sheet(isPresented: self.$showStopwatchSheet, content: {
NewStopwatchView(isPresented: $showStopwatchSheet, tabSelection: .constant(0))
NewStopwatchView(isPresented: $showStopwatchSheet)
})
.sheet(isPresented: self.$showTimerSheet, content: {
NewCountdownView(isPresented: $showTimerSheet, tabSelection: .constant(0))
NewCountdownView(isPresented: $showTimerSheet)
})
}

@ -21,13 +21,7 @@ struct CountdownFormView : View {
@EnvironmentObject var model: TimerModel
var nameBinding: Binding<String>
// var secondsBinding: Binding<String>
// var minutesBinding: Binding<String>
// var hoursBinding: Binding<String>
var durationBinding: Binding<TimeInterval>
var imageBinding: Binding<CoolPic>
var repeatCountBinding: Binding<Int16>
@ -45,49 +39,8 @@ struct CountdownFormView : View {
}
}
// Section {
//// self.focusedField = nil
//
// TextField("Name", text: nameBinding)
// .focused($focusedField, equals: .name)
// .onSubmit {
// self.focusedField = nil
//
//// self.focusNextField($focusedField)
// }
// } header: {
// Text("Name")
// } footer: {
// if !self.nameBinding.wrappedValue.isEmpty {
// Text("Ask Siri: \(Bundle.main.applicationName) \(self.nameBinding.wrappedValue)!")
// }
// }
Section {
TimePickerView(duration: self.durationBinding)
// TextField("Hours", text: hoursBinding)
// .keyboardType(.numberPad)
// .focused($focusedField, equals: .hours)
// .onSubmit {
// self.focusNextField($focusedField)
// }
// TextField("Minutes", text: minutesBinding)
// .keyboardType(.numberPad)
// .focused($focusedField, equals: .minutes)
// .onSubmit {
// self.focusNextField($focusedField)
// }
// TextField("Seconds", text: secondsBinding)
// .keyboardType(.numberPad)
// .focused($focusedField, equals: .seconds)
// .onSubmit {
// self.focusedField = nil
// }
} header: {
LabeledContent("Duration", value: self.duration().hourMinuteSecond).font(.footnote)
}
@ -102,22 +55,6 @@ struct CountdownFormView : View {
func duration() -> TimeInterval {
return self.durationBinding.wrappedValue
// let formatter = NumberFormatter()
// var hours: Int = 0
// var minutes: Int = 0
// var seconds: Int = 0
// if let h = formatter.number(from: hoursBinding.wrappedValue) {
// hours = h.intValue
// }
// if let m = formatter.number(from: minutesBinding.wrappedValue) {
// minutes = m.intValue
// }
// if let s = formatter.number(from: secondsBinding.wrappedValue) {
// seconds = s.intValue
// }
// return Double(seconds) + 60 * Double(minutes) + 60 * 60 * Double(hours)
}
}
@ -127,18 +64,12 @@ struct CountdownFormView_Previews: PreviewProvider {
@FocusState static var textFieldIsFocused: Bool
static var previews: some View {
Form {
CountdownFormView(
nameBinding: .constant(""),
// secondsBinding: .constant(""),
// minutesBinding: .constant(""),
// hoursBinding: .constant(""),
durationBinding: .constant(0.0),
imageBinding: .constant(.pic3),
repeatCountBinding: .constant(2),
intervalRepeatBinding: .constant(2))
.environmentObject(TimerModel())
}
}
}

@ -15,12 +15,10 @@ struct NewCountdownView : View {
@Environment(\.managedObjectContext) private var viewContext
@Binding var isPresented: Bool
var tabSelection: Binding<Int>
var userActivity: NSUserActivity
init(isPresented: Binding<Bool>, tabSelection: Binding<Int>) {
init(isPresented: Binding<Bool>) {
_isPresented = isPresented
self.tabSelection = tabSelection
self.userActivity = Shortcut.newCountdown.userActivity
// let shortcut = INShortcut(userActivity: self.userActivity)
@ -28,7 +26,7 @@ struct NewCountdownView : View {
var body: some View {
NavigationStack {
CountdownEditView(isPresented: $isPresented, tabSelection: self.tabSelection)
CountdownEditView(isPresented: $isPresented)
.environment(\.managedObjectContext, viewContext)
.onAppear {
self.userActivity.becomeCurrent()
@ -71,8 +69,6 @@ struct CountdownEditView : View {
@State var errorShown: Bool = false
@State var error: Error? = nil
var tabSelection: Binding<Int>? = nil
@State var _isNewCountdown: Bool = false // false if editing an existing countdown
@State var _hasLoaded = false
@ -82,16 +78,14 @@ struct CountdownEditView : View {
@FocusState private var focusedField: CountdownField?
init(isPresented: Binding<Bool>, countdown: Countdown? = nil, tabSelection: Binding<Int>? = nil) {
init(isPresented: Binding<Bool>, countdown: Countdown? = nil) {
_isPresented = isPresented
self.countdown = countdown
self.tabSelection = tabSelection
}
init(isPresented: Binding<Bool>, preset: Preset, tabSelection: Binding<Int>) {
init(isPresented: Binding<Bool>, preset: Preset) {
_isPresented = isPresented
self.preset = preset
self.tabSelection = tabSelection
}
fileprivate var _formId = "formId"
@ -375,9 +369,6 @@ struct CountdownEditView : View {
} else {
dismiss()
}
self.tabSelection?.wrappedValue = 1
}
fileprivate func _delete() {
@ -411,8 +402,7 @@ struct CountdownEditView : View {
struct NewCountdownView_Previews: PreviewProvider {
static var previews: some View {
NewCountdownView(isPresented: .constant(true),
tabSelection: .constant(0))
NewCountdownView(isPresented: .constant(true))
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}

@ -18,8 +18,6 @@ struct CompactHomeView: View {
animation: .default)
private var timers: FetchedResults<AbstractTimer>
@State private var tabSelection: Int = 0
var body: some View {
NavigationStack {
@ -27,14 +25,6 @@ struct CompactHomeView: View {
.environment(\.managedObjectContext, viewContext)
.environmentObject(Conductor.maestro)
}
.onAppear {
if self.timers.count > 0 {
self.tabSelection = 1
}
}
.onOpenURL { _ in
self.tabSelection = 1
}
}
}

@ -28,7 +28,6 @@ struct NewDataView: View {
@Environment(\.managedObjectContext) private var viewContext
@Binding var isPresented: Bool
var tabSelection: Binding<Int>
@State var selection: Int = 0
@ -47,10 +46,10 @@ struct NewDataView: View {
.padding(.horizontal)
TabView(selection: $selection) {
NewCountdownView(isPresented: $isPresented, tabSelection: self.tabSelection)
NewCountdownView(isPresented: $isPresented)
.tag(0)
.environment(\.managedObjectContext, viewContext)
NewStopwatchView(isPresented: $isPresented, tabSelection: self.tabSelection)
NewStopwatchView(isPresented: $isPresented)
.tag(1)
.environment(\.managedObjectContext, viewContext)
}.tabViewStyle(.page(indexDisplayMode: .never))
@ -62,7 +61,7 @@ struct NewDataView: View {
struct NewDataView_Previews: PreviewProvider {
static var previews: some View {
NewDataView(isPresented: .constant(true), tabSelection: .constant(0))
NewDataView(isPresented: .constant(true))
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}

@ -60,8 +60,6 @@ struct PresetsView: View {
@State var isShowingNewCountdown = false
@State var isShowingNewStopwatch = false
var tabSelection: Binding<Int>
fileprivate func _columnCount() -> Int {
return 2
@ -135,15 +133,15 @@ struct PresetsView: View {
Spacer()
}
.sheet(isPresented: $isShowingNewStopwatch, content: {
NewStopwatchView(isPresented: $isShowingNewStopwatch, tabSelection: self.tabSelection)
NewStopwatchView(isPresented: $isShowingNewStopwatch)
.environment(\.managedObjectContext, viewContext)
})
.sheet(isPresented: $isShowingNewCountdown, content: {
NewCountdownView(isPresented: $isShowingNewCountdown, tabSelection: self.tabSelection)
NewCountdownView(isPresented: $isShowingNewCountdown)
.environment(\.managedObjectContext, viewContext)
})
.sheet(isPresented: $isPresented, content: {
CountdownEditView(isPresented: $isPresented, preset: self.model.selectedPreset, tabSelection: self.tabSelection)
CountdownEditView(isPresented: $isPresented, preset: self.model.selectedPreset)
.environment(\.managedObjectContext, viewContext)
})
.sheet(isPresented: $isShowingSubscription, content: {
@ -174,12 +172,21 @@ struct TimerItemView: View {
HStack {
VStack(alignment: .leading) {
Text(self.name.uppercased()).foregroundColor(colorScheme == .dark ? .white : .black)
HStack(spacing: 4.0) {
Group {
Image(systemName: "timer")
Text(self.duration)
}
.foregroundColor(Color.accentColor)
Text(self.sound.uppercased()).foregroundColor(Color(white: 0.7))
Group {
Image(systemName: "music.note")
Text(self.sound.uppercased())
}
.foregroundColor(Color(white: 0.25))
}.font(.footnote)
}//.padding()
.multilineTextAlignment(.leading)
Spacer()
// Spacer()
}//.background(Color(white: 0.1))
//.cornerRadius(16.0)
.monospaced()
@ -193,18 +200,18 @@ enum PresetSection: Int, Identifiable, CaseIterable {
var id: Int { return self.rawValue }
// case workout
case chill
case mindfullness
case cooking
case tea
case other
case move
var presets: [Preset] {
switch self {
case .cooking: return [.pasta, .rice, .softBoiled, .mediumBoiledEggs, .hardBoiledEggs]
case .tea: return [.greenTea, .blackTea]
// case .workout: return [.runningSplits]
case .chill: return [.nap, .meditation]
case .other: return [.toothbrushing]
case .mindfullness: return [.nap, .meditation, .writing, .reading]
case .move: return [.workout, .stretching, .toothbrushing, .cleaning]
}
}
@ -212,8 +219,8 @@ enum PresetSection: Int, Identifiable, CaseIterable {
switch self {
case .cooking: return NSLocalizedString("Cooking", comment: "")
// case .workout: return NSLocalizedString("Workout", comment: "")
case .chill: return NSLocalizedString("Chill", comment: "")
case .other: return NSLocalizedString("Other", comment: "")
case .mindfullness: return NSLocalizedString("Self", comment: "")
case .move: return NSLocalizedString("Move", comment: "")
case .tea: return NSLocalizedString("Tea", comment: "")
}
}
@ -232,17 +239,22 @@ struct CountdownInterval {
enum Preset: Int, Identifiable, CaseIterable {
var id: Int { return self.rawValue }
case softBoiled
case mediumBoiledEggs
case hardBoiledEggs
case meditation
case nap
case runningSplits
case toothbrushing
case blackTea
case greenTea
case workout
// case runningSplits
case pasta
case rice
case blackTea
case greenTea
case toothbrushing
case softBoiled
case mediumBoiledEggs
case hardBoiledEggs
case writing
case reading
case stretching
case cleaning
var localizedName: String {
switch self {
@ -251,24 +263,29 @@ enum Preset: Int, Identifiable, CaseIterable {
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: "")
// case .runningSplits: return NSLocalizedString("Running splits", comment: "")
case .toothbrushing: return NSLocalizedString("Tooth brushing", comment: "")
case .blackTea: return NSLocalizedString("Black tea", comment: "")
case .greenTea: return NSLocalizedString("Green tea", comment: "")
case .pasta: return NSLocalizedString("Pasta", comment: "")
case .rice: return NSLocalizedString("Rice", comment: "")
case .workout: return NSLocalizedString("Workout", comment: "")
case .writing: return NSLocalizedString("Writing", comment: "")
case .reading: return NSLocalizedString("Reading", comment: "")
case .stretching: return NSLocalizedString("Stretching", comment: "")
case .cleaning: return NSLocalizedString("Cleaning", 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:
// 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 CountdownIntervalGroup(repeatCount: 0, intervals: [CountdownInterval(duration: self.duration)])
}
// }
}
var duration: TimeInterval {
@ -278,22 +295,27 @@ enum Preset: Int, Identifiable, CaseIterable {
case .hardBoiledEggs: return 10 * 60
case .meditation: return 15 * 60
case .nap: return 20 * 60
case .runningSplits: return 0.0
// case .runningSplits: return 0.0
case .toothbrushing: return 2 * 60.0
case .greenTea: return 3 * 60.0
case .blackTea: return 4 * 60.0
case .pasta: return 10 * 60.0
case .rice: return 10 * 60.0
case .workout: return 30 * 60.0
case .stretching: return 3 * 60.0
case .writing: return 30 * 60.0
case .reading: return 20 * 60.0
case .cleaning: return 60 * 60.0
}
}
var playlist: Playlist {
switch self {
case .softBoiled, .mediumBoiledEggs, .hardBoiledEggs, .pasta, .rice, .runningSplits, .toothbrushing:
case .softBoiled, .mediumBoiledEggs, .hardBoiledEggs, .pasta, .rice, .toothbrushing, .workout, .stretching:
return .stephanBodzin
case .meditation, .blackTea, .greenTea:
case .meditation, .blackTea, .greenTea, .writing, .reading:
return .relax
case .nap:
case .nap, .cleaning:
return .nature
}
}
@ -305,7 +327,7 @@ enum Preset: Int, Identifiable, CaseIterable {
var formattedDuration: String {
let group = self.intervalGroup
let count = group.repeatCount.formatted()
let durations = group.intervals.map { $0.duration.minuteSecond }
let durations = group.intervals.map { $0.duration.hourMinuteSecond }
let formattedIntervals = durations.joined(separator: "/")
if group.repeatCount > 1 {
@ -326,7 +348,7 @@ enum Preset: Int, Identifiable, CaseIterable {
struct PresetsView_Previews: PreviewProvider {
static var previews: some View {
PresetsView(tabSelection: .constant(0))
PresetsView()
}
}
@ -335,6 +357,5 @@ struct TimerItemView_Previews: PreviewProvider {
TimerItemView(name: "Hard boiled eggs",
duration: "10:00",
sound: "Stephan Bodzin")
.frame(width: UIScreen.main.bounds.width / 2.0)
}
}

@ -7,6 +7,7 @@
import Foundation
import SwiftUI
import Combine
protocol SoundHolder {
func selectSound(_ sound: Sound, selected: Bool)
@ -52,6 +53,9 @@ class SoundModel: ObservableObject, SoundHolder {
}
var soundSelection: String {
if self.playlists.count == 1 {
return self.playlists.first!.localizedString
}
if !sounds.isEmpty {
if sounds.count == 1 {
return sounds.first!.localizedString

@ -0,0 +1,283 @@
//
// StartView.swift
// LeCountdown
//
// Created by Laurent Morvillier on 22/05/2023.
//
import SwiftUI
class Customization: ObservableObject {
var preset: Preset
@Published var added: Bool = false
@Published var duration: Double = 0.0 {
didSet {
self.added = true
}
}
@Published var timerModel: TimerModel = TimerModel()
init(preset: Preset) {
self.preset = preset
self.duration = preset.duration
self.timerModel.soundModel.selectPlaylist(preset.playlist, selected: true)
self.added = false
}
func toggleAdd() {
self.added = !self.added
Logger.log("ADDDED = \(self.added)")
}
func createTimer() -> AbstractTimer {
let context = PersistenceController.shared.container.viewContext
let countdown = Countdown(context: context)
countdown.duration = self.duration
countdown.playableIds = self.timerModel.soundModel.playableIds
return countdown
}
}
class PresetSelectionModel: ObservableObject {
// @Published var addedPresets: Set<Preset> = []
@Published var customizations: [Preset : Customization] = [:]
// @Published var expanded: Set<Preset> = []
// @Published var duration: [TimeInterval] = []
init() {
self.customizations = Preset.allCases.reduce(into: [Preset: Customization]()) { $0[$1] = Customization(preset: $1) }
}
}
struct DurationButtonView: View {
@StateObject var customization: Customization
@State var showDurationSheet: Bool = false
var body: some View {
Button {
self.showDurationSheet = true
} label: {
Image(systemName: "timer")
Text(customization.duration.hourMinuteSecond)
}
.sheet(isPresented: $showDurationSheet) {
TimePickerView(duration: self.$customization.duration)
.presentationDetents([.height(320.0)])
}
}
}
struct SoundButtonView: View {
@StateObject var soundModel: SoundModel
@State var showSoundSheet: Bool = false
var body: some View {
Button {
self.showSoundSheet = true
} label: {
Image(systemName: "music.note")
Text(self.soundModel.soundSelection)
}.sheet(isPresented: $showSoundSheet) {
PlaylistsView(model: self.soundModel,
catalog: .ring)
.presentationDetents([.height(320.0)])
}
}
}
struct PresetSelectionView: View {
@StateObject var model: PresetSelectionModel
var body: some View {
List {
ForEach(PresetSection.allCases) { section in
Section(section.localizedName.uppercased()) {
ForEach(section.presets.indices, id: \.self) { i in
let preset = section.presets[i]
let customization = self.model.customizations[preset]!
HStack {
Button {
self._addOrRemove(preset: preset)
} label: {
HStack {
let added = customization.added
let image = added ? "checkmark.circle.fill" : "circle"
Image(systemName: image)
.padding(.trailing, 8.0)
.font(.title2)
.foregroundColor(Color.accentColor)
Text(preset.localizedName)
Spacer()
VStack(alignment: .trailing) {
DurationButtonView(customization: customization)
SoundButtonView(soundModel: customization.timerModel.soundModel)
}.buttonStyle(.bordered)
}.font(.callout)
}
}
}
}
}
}.listStyle(.inset)
}
fileprivate func _addOrRemove(preset: Preset) {
self.model.customizations[preset]?.toggleAdd()
}
fileprivate func _added(preset: Preset) -> Bool {
return self.model.customizations[preset]?.added ?? false
}
fileprivate func _image(preset: Preset) -> String {
return self._added(preset: preset) ? "checkmark.circle.fill" : "circle"
}
}
struct StartView: View {
@StateObject var model: PresetSelectionModel = PresetSelectionModel()
@State var showAddScreen: Bool = false
var body: some View {
VStack(spacing: 0.5) {
PresetSelectionView(model: self.model).monospaced()
Button {
self.showAddScreen = true
} label: {
HStack {
Image(systemName: "plus.circle").font(.title)
Text("Create your own").font(.title3)
}
.padding()
.frame(maxWidth: .infinity)
.foregroundColor(.white)
.background(Color.accentColor)
}.sheet(isPresented: self.$showAddScreen) {
NewCountdownView(isPresented: $showAddScreen)
}
Button {
self._done()
} label: {
Text("Done")
.font(.title2).fontWeight(.semibold)
.padding()
.frame(maxWidth: .infinity)
// .foregroundColor(.white)
.background(.white)
}
}.background(.gray)
}
fileprivate func _done() {
let customizations = self.model.customizations.values.filter { $0.added }
for custo in customizations {
let _ = custo.createTimer()
}
let context = PersistenceController.shared.container.viewContext
do {
try context.save()
} catch {
Logger.error(error)
}
}
}
struct ConfigurationButtonView: View {
var preset: Preset
@Binding var duration: TimeInterval
@State var selectedPreset: Preset? = nil
var body: some View {
Button {
self.selectedPreset = preset
} label: {
Image(systemName: "gearshape.fill")
}
.buttonStyle(.plain)
// .foregroundColor(.gray)
.sheet(item: $selectedPreset) { preset in
ConfigurationView(preset: preset,
model: TimerModel(),
duration: self.$duration)
.presentationDetents([.height(320.0)])
}
}
}
struct ConfigurationView: View {
var preset: Preset
var model: TimerModel
@Binding var duration: TimeInterval
var body: some View {
List {
SoundLinkView(soundModel: self.model.soundModel,
catalog: .ring,
title: NSLocalizedString("Sound", comment: ""))
TimePickerView(duration: self.$duration)
}
.listStyle(.plain)
.onAppear {
self.model.soundModel.setPlayables([preset.playlist])
self.duration = preset.duration
}
}
}
struct StartView_Previews: PreviewProvider {
static var previews: some View {
StartView()
ConfigurationView(preset: Preset.blackTea,
model: TimerModel(),
duration: .constant(60.0))
}
}

@ -15,15 +15,13 @@ struct NewStopwatchView: View {
@Environment(\.managedObjectContext) private var viewContext
@Binding var isPresented: Bool
var tabSelection: Binding<Int>
init(isPresented: Binding<Bool>, tabSelection: Binding<Int>) {
init(isPresented: Binding<Bool>) {
_isPresented = isPresented
self.tabSelection = tabSelection
}
var body: some View {
StopwatchEditView(isPresented: $isPresented, tabSelection: self.tabSelection)
StopwatchEditView(isPresented: $isPresented)
.environment(\.managedObjectContext, viewContext)
.navigationTitle("New stopwatch")
}
@ -61,15 +59,12 @@ struct StopwatchEditView: View {
@Environment(\.isPresented) var envIsPresented
var tabSelection: Binding<Int>? = nil
@FetchRequest(sortDescriptors: [])
private var timers: FetchedResults<AbstractTimer>
init(isPresented: Binding<Bool>, stopwatch: Stopwatch? = nil, tabSelection: Binding<Int>? = nil) {
init(isPresented: Binding<Bool>, stopwatch: Stopwatch? = nil) {
_isPresented = isPresented
self.stopwatch = stopwatch
self.tabSelection = tabSelection
}
var body: some View {
@ -241,8 +236,6 @@ struct StopwatchEditView: View {
} else {
dismiss()
}
self.tabSelection?.wrappedValue = 1
}
fileprivate func _delete() {
@ -276,7 +269,7 @@ struct StopwatchEditView: View {
struct NewStopwatchView_Previews: PreviewProvider {
static var previews: some View {
NewStopwatchView(isPresented: .constant(true), tabSelection: .constant(0))
NewStopwatchView(isPresented: .constant(true))
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}

@ -267,3 +267,12 @@
"Calendar" = "Calendrier";
"Disclaimer" = "Ceci est une version beta d'Enchante.\n\nPour l'instant, veillez à ne pas dépendre de l'app pour des évènements trop critiques, on ne sait jamais :)\n\nSi vous avez des remarques ou un problème, merci de me contacter en allant dans les réglages.";
"Volume" = "Volume";
"Workout" = "Exercice";
"Move" = "Bouger";
"Self" = "Soi";
"Writing" = "Écriture";
"Reading" = "Lecture";
"Stretching" = "Étirements";
"Cleaning" = "Nettoyage";
"Done" = "OK";
"Create your own" = "Créez les vôtres";

Loading…
Cancel
Save