TheNoiseClock/TheNoiseClock/Features/Alarms/Views/AlarmView.swift

128 lines
3.9 KiB
Swift

//
// AlarmView.swift
// TheNoiseClock
//
// Created by Matt Bruce on 9/7/25.
//
import SwiftUI
import Bedrock
import Foundation
/// Main alarm management view
struct AlarmView: View {
// MARK: - Properties
@Bindable var viewModel: AlarmViewModel
@State private var showAddAlarm = false
@State private var selectedAlarmForEdit: Alarm?
@AppStorage(ClockStyle.appStorageKey) private var clockStyleData: Data = Data()
// MARK: - Body
var body: some View {
let isPad = UIDevice.current.userInterfaceIdiom == .pad
Group {
if viewModel.alarms.isEmpty {
VStack(spacing: Design.Spacing.large) {
if !isKeepAwakeEnabled {
AlarmLimitationsBanner()
}
EmptyAlarmsView {
showAddAlarm = true
}
.contentShape(Rectangle())
.onTapGesture {
showAddAlarm = true
}
}
.frame(maxWidth: Design.Size.maxContentWidthPortrait)
.frame(maxWidth: .infinity, alignment: .center)
} else {
List {
if !isKeepAwakeEnabled {
Section {
AlarmLimitationsBanner()
.listRowInsets(EdgeInsets())
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
}
}
ForEach(viewModel.alarms) { alarm in
AlarmRowView(
alarm: alarm,
onToggle: {
Task {
await viewModel.toggleAlarm(id: alarm.id)
}
},
onEdit: {
selectedAlarmForEdit = alarm
}
)
}
.onDelete(perform: deleteAlarm)
}
.listStyle(.insetGrouped)
.frame(maxWidth: Design.Size.maxContentWidthPortrait)
.frame(maxWidth: .infinity, alignment: .center)
}
}
.navigationTitle(isPad ? "" : "Alarms")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
showAddAlarm = true
} label: {
Image(systemName: "plus")
.font(.title2)
}
}
}
.onAppear {
Task {
// Request AlarmKit authorization when the alarms tab appears
await viewModel.requestAlarmKitAuthorization()
}
}
.sheet(isPresented: $showAddAlarm) {
AddAlarmView(
viewModel: viewModel,
isPresented: $showAddAlarm
)
}
.sheet(item: $selectedAlarmForEdit) { alarm in
EditAlarmView(
viewModel: viewModel,
alarm: alarm
)
}
}
// MARK: - Private Methods
private func deleteAlarm(at offsets: IndexSet) {
Task {
for index in offsets {
let alarm = viewModel.alarms[index]
await viewModel.deleteAlarm(id: alarm.id)
}
}
}
private var isKeepAwakeEnabled: Bool {
guard let decoded = try? JSONDecoder().decode(ClockStyle.self, from: clockStyleData) else {
return ClockStyle().keepAwake
}
return decoded.keepAwake
}
}
// MARK: - Preview
#Preview {
NavigationStack {
AlarmView(viewModel: AlarmViewModel())
}
}