Adds button to stop the repeating sound

release
Laurent 3 years ago
parent 4c20c83745
commit 7db4aecfa4
  1. 5
      LeCountdown/Conductor.swift
  2. 43
      LeCountdown/LeCountdownApp.swift
  3. 77
      LeCountdown/Views/ContentView.swift
  4. 42
      LeCountdown/Views/Countdown/CountdownDialView.swift
  5. 40
      LeCountdown/Views/DialView.swift

@ -13,7 +13,7 @@ class Conductor : ObservableObject {
static let maestro: Conductor = Conductor()
var soundPlayer: SoundPlayer? = nil
@Published var soundPlayer: SoundPlayer? = nil
@UserDefault(Key.dates.rawValue, defaultValue: [:]) static var savedDates: [String : DateInterval]
@ -99,8 +99,9 @@ class Conductor : ObservableObject {
}
func stopSoundIfNecessary() {
func stopSoundIfPossible() {
self.soundPlayer?.stop()
self.soundPlayer = nil
}
// MARK: - Live Activity

@ -18,6 +18,8 @@ struct LeCountdownApp: App {
let persistenceController = PersistenceController.shared
@State private var tabSelection = 1
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
init() {
@ -26,24 +28,38 @@ struct LeCountdownApp: App {
var body: some Scene {
WindowGroup {
TabView {
TabView(selection: $tabSelection) {
ContentView<Countdown>()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environmentObject(Conductor.maestro)
.tabItem { Label("Countdown", systemImage: "timer") }
.tag(1)
ContentView<Stopwatch>()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environmentObject(Conductor.maestro)
.tabItem { Label("Stopwatch", systemImage: "stopwatch") }
.tag(2)
ContentView<Alarm>()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environmentObject(Conductor.maestro)
.tabItem { Label("Alarm", systemImage: "alarm") }
.tag(3)
RecordsView().environment(\.managedObjectContext, persistenceController.container.viewContext)
.tabItem { Label("Stats", systemImage: "chart.bar.fill") }
.tag(4)
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
self._willEnterForegroundNotification()
}.onAppear {
}
.onAppear {
self._onAppear()
}
.onOpenURL { url in
print("open URL = \(url)")
self._performActionIfPossible(url: url)
}
}
}
@ -87,4 +103,27 @@ struct LeCountdownApp: App {
}
fileprivate func _performActionIfPossible(url: URL) {
let context = persistenceController.container.viewContext
let urlString = url.absoluteString
if let timer = context.object(stringId: urlString) as? AbstractTimer {
switch timer {
case is Countdown:
tabSelection = 1
case is Stopwatch:
tabSelection = 2
case is Alarm:
tabSelection = 3
default:
print("url not managed, object is \(timer)")
break
}
} else {
print("url not managed: \(url)")
}
}
}

@ -20,7 +20,8 @@ class BoringContext : ObservableObject {
struct ContentView<T : AbstractTimer>: View {
@StateObject var boringContext: BoringContext = BoringContext()
@EnvironmentObject var conductor: Conductor
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(
@ -45,22 +46,43 @@ struct ContentView<T : AbstractTimer>: View {
GeometryReader { reader in
let width: CGFloat = reader.size.width / 2 - 10.0
LazyVGrid(
columns: columns,
spacing: itemSpacing
) {
ZStack(alignment: .bottom) {
ReorderableForEach(items: timersArray) { timer in
ScrollView {
DialView(timer: timer, frameSize: width) .environment(\.managedObjectContext, viewContext)
.environmentObject(boringContext)
LazyVGrid(
columns: columns,
spacing: itemSpacing
) {
ReorderableForEach(items: timersArray) { timer in
DialView(timer: timer, frameSize: width) .environment(\.managedObjectContext, viewContext)
.environmentObject(Conductor.maestro)
.environmentObject(boringContext)
} moveAction: { from, to in
self._reorder(from: from, to: to)
} moveAction: { from, to in
self._reorder(from: from, to: to)
}
}
}
if Conductor.maestro.soundPlayer != nil {
Button {
Conductor.maestro.stopSoundIfPossible()
} label: {
Text("STOP")
.frame(minWidth: 0.0, maxWidth: .infinity, minHeight: 75.0, maxHeight: 75.0)
.foregroundColor(.white)
.background(.red)
.cornerRadius(16.0)
}.padding()
}
}
}.padding(itemSpacing)
}.padding(.horizontal, itemSpacing)
.navigationTitle("\(String(describing: T.self))")
.alert(boringContext.error?.localizedDescription ?? "missing error", isPresented: $boringContext.showDefaultAlert) {
Button("OK", role: .cancel) { }
@ -96,7 +118,6 @@ struct ContentView<T : AbstractTimer>: View {
self._askPermissions()
}
.onOpenURL { url in
print("open URL = \(url)")
self._performActionIfPossible(url: url)
}
}
@ -139,22 +160,29 @@ struct ContentView<T : AbstractTimer>: View {
fileprivate func _performActionIfPossible(url: URL) {
print("_performActionIfPossible")
let urlString = url.absoluteString
if let timer = viewContext.object(stringId: urlString) as? AbstractTimer {
TimerRouter.performAction(timer: timer) { result in
switch result {
case .success:
break
case .failure(let failure):
switch failure {
case TimerError.notificationAuthorizationMissing:
self.boringContext.showPermissionAlert = true
default:
self.boringContext.error = failure
self.boringContext.showDefaultAlert = true
if timer is T {
print("performAction")
TimerRouter.performAction(timer: timer) { result in
switch result {
case .success:
break
case .failure(let failure):
switch failure {
case TimerError.notificationAuthorizationMissing:
self.boringContext.showPermissionAlert = true
default:
self.boringContext.error = failure
self.boringContext.showDefaultAlert = true
}
}
}
}
// print("Start countdown: \(countdown.name ?? ""), \(countdown.duration)")
@ -222,6 +250,7 @@ fileprivate extension Countdown {
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView<Countdown>().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
ContentView<Countdown>().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) .environmentObject(Conductor.maestro)
}
}

@ -9,35 +9,37 @@ import SwiftUI
struct CountdownDialView: View {
@EnvironmentObject var conductor: Conductor
@ObservedObject var countdown: Countdown
var body: some View {
VStack {
HStack {
HStack {
VStack(alignment: .leading) {
Text(countdown.activity?.name?.uppercased() ?? "")
Spacer()
}
if let dateInterval = Conductor.maestro.notificationDates[countdown.stringId] {
Text(dateInterval.end, style: .timer)
Button {
CountdownScheduler.master.cancelCurrentNotifications(countdown: countdown)
} label: {
Text("Cancel")
// let dateInterval = DateInterval(start: Date(), end: Date())
if let dateInterval = conductor.notificationDates[countdown.stringId] {
Text(dateInterval.end, style: .timer)
Spacer()
HStack {
Spacer()
Button {
CountdownScheduler.master.cancelCurrentNotifications(countdown: countdown)
} label: {
Text("Cancel".uppercased())
}
.buttonStyle(.bordered)
.tint(.red)
}
} else {
HStack {
Spacer()
}
} else {
Text(countdown.duration.minuteSecond)
Spacer()
}
Spacer()
}
Spacer()
}
.padding(24.0)
.monospaced()
.font(Font.system(size: 16.0, weight: .semibold))
.foregroundColor(Color.white)
@ -47,6 +49,6 @@ struct CountdownDialView: View {
struct CountdownLiveView_Previews: PreviewProvider {
static var previews: some View {
CountdownDialView(countdown: Countdown.fake(context: PersistenceController.preview.container.viewContext))
CountdownDialView(countdown: Countdown.fake(context: PersistenceController.preview.container.viewContext)).background(.cyan).environmentObject(Conductor.maestro)
}
}

@ -12,31 +12,34 @@ struct DialView: View {
@Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var boringContext: BoringContext
@EnvironmentObject var conductor: Conductor
@State var timer: AbstractTimer
var frameSize: CGFloat
var body: some View {
ZStack(alignment: .topTrailing) {
ZStack(alignment: .topLeading) {
Image(timer.imageName).resizable()
Button {
self._launchTimer(timer)
} label: {
self._dialView(timer: timer)
self._dialView(timer: timer).padding()
}
NavigationLink {
self._editView(timer: timer, isPresented: $boringContext.isShowingNewData)
} label: {
Image(systemName: "gearshape.fill")
.font(.system(size: 24, weight: .light))
.padding()
.foregroundColor(Color.white)
HStack {
Spacer()
NavigationLink {
self._editView(timer: timer, isPresented: $boringContext.isShowingNewData)
} label: {
Image(systemName: "gearshape.fill")
.font(.system(size: 24, weight: .light))
.padding()
.foregroundColor(Color.white)
}
}
}
.frame(width: frameSize, height: frameSize)
.cornerRadius(40.0)
@ -46,11 +49,11 @@ struct DialView: View {
fileprivate func _dialView(timer: AbstractTimer) -> some View {
switch timer {
case let countdown as Countdown:
CountdownDialView(countdown: countdown)
CountdownDialView(countdown: countdown) .environmentObject(Conductor.maestro)
case let alarm as Alarm:
AlarmDialView(alarm: alarm)
AlarmDialView(alarm: alarm) .environmentObject(Conductor.maestro)
case let stopwatch as Stopwatch:
StopwatchDialView(stopwatch: stopwatch)
StopwatchDialView(stopwatch: stopwatch) .environmentObject(Conductor.maestro)
default:
Text("missing dial view")
}
@ -93,6 +96,13 @@ struct DialView: View {
struct DialView_Previews: PreviewProvider {
static var previews: some View {
DialView(timer: Countdown.fake(context: PersistenceController.preview.container.viewContext), frameSize: 150.0)
DialView(
timer: Countdown.fake(context: PersistenceController.preview.container.viewContext),
frameSize: 150.0)
.environmentObject(Conductor.maestro)
.environmentObject(BoringContext())
}
}

Loading…
Cancel
Save