Compare commits

..

No commits in common. "41578f09412b4207863be66fa0735610a85de136" and "6a154f00bc537156e27da4209f02122e22b28eaa" have entirely different histories.

5 changed files with 85 additions and 110 deletions

View File

@ -110,11 +110,7 @@ struct ContentView: View {
OnboardingView { OnboardingView {
onboardingState.completeWelcome() onboardingState.completeWelcome()
} }
.transition(.asymmetric( .transition(.opacity)
insertion: .opacity,
removal: .opacity.combined(with: .move(edge: .bottom)).combined(with: .scale(scale: 0.9))
))
.zIndex(1) // Ensure it stays on top during transition
} }
} }
.sheet(isPresented: $keepAwakePromptState.isPresented) { .sheet(isPresented: $keepAwakePromptState.isPresented) {
@ -141,7 +137,7 @@ struct ContentView: View {
guard shouldShowKeepAwakePromptForTab() else { return } guard shouldShowKeepAwakePromptForTab() else { return }
keepAwakePromptState.showIfNeeded(isKeepAwakeEnabled: clockViewModel.style.keepAwake) keepAwakePromptState.showIfNeeded(isKeepAwakeEnabled: clockViewModel.style.keepAwake)
} }
.animation(.spring(duration: 0.45, bounce: 0.2), value: onboardingState.hasCompletedWelcome) .animation(.easeInOut(duration: 0.3), value: onboardingState.hasCompletedWelcome)
} }
private func shouldShowKeepAwakePromptForTab() -> Bool { private func shouldShowKeepAwakePromptForTab() -> Bool {

View File

@ -24,10 +24,6 @@ struct AlarmView: View {
ZStack { ZStack {
AppSurface.primary.ignoresSafeArea() AppSurface.primary.ignoresSafeArea()
GeometryReader { geometry in
let isLandscape = geometry.size.width > geometry.size.height
let maxWidth = isLandscape ? Design.Size.maxContentWidthLandscape : Design.Size.maxContentWidthPortrait
Group { Group {
if viewModel.alarms.isEmpty { if viewModel.alarms.isEmpty {
VStack(spacing: Design.Spacing.large) { VStack(spacing: Design.Spacing.large) {
@ -43,13 +39,13 @@ struct AlarmView: View {
showAddAlarm = true showAddAlarm = true
} }
} }
.frame(maxWidth: Design.Size.maxContentWidthPortrait)
.frame(maxWidth: .infinity, alignment: .center)
} else { } else {
List { ScrollView {
VStack(spacing: Design.Spacing.medium) {
if !isKeepAwakeEnabled { if !isKeepAwakeEnabled {
AlarmLimitationsBanner() AlarmLimitationsBanner()
.listRowInsets(EdgeInsets(top: Design.Spacing.large, leading: Design.Spacing.large, bottom: Design.Spacing.small, trailing: Design.Spacing.large))
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
} }
ForEach(viewModel.alarms) { alarm in ForEach(viewModel.alarms) { alarm in
@ -62,27 +58,18 @@ struct AlarmView: View {
}, },
onEdit: { onEdit: {
selectedAlarmForEdit = alarm selectedAlarmForEdit = alarm
},
onDelete: {
Task {
await viewModel.deleteAlarm(id: alarm.id)
}
} }
) )
.listRowInsets(EdgeInsets(top: Design.Spacing.small, leading: Design.Spacing.large, bottom: Design.Spacing.small, trailing: Design.Spacing.large))
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
} }
} }
.listStyle(.plain) .padding(.horizontal, Design.Spacing.large)
.scrollContentBackground(.hidden) .padding(.top, Design.Spacing.large)
.background(AppSurface.primary.ignoresSafeArea())
} }
} .frame(maxWidth: Design.Size.maxContentWidthPortrait)
.frame(maxWidth: maxWidth)
.frame(maxWidth: .infinity, alignment: .center) .frame(maxWidth: .infinity, alignment: .center)
} }
} }
}
.navigationTitle(isPad ? "" : "Alarms") .navigationTitle(isPad ? "" : "Alarms")
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbar { .toolbar {

View File

@ -16,7 +16,6 @@ struct AlarmRowView: View {
let alarm: Alarm let alarm: Alarm
let onToggle: () -> Void let onToggle: () -> Void
let onEdit: () -> Void let onEdit: () -> Void
let onDelete: () -> Void
@AppStorage(ClockStyle.appStorageKey) private var clockStyleData: Data = Data() @AppStorage(ClockStyle.appStorageKey) private var clockStyleData: Data = Data()
// MARK: - Body // MARK: - Body
@ -66,13 +65,6 @@ struct AlarmRowView: View {
onEdit() onEdit()
} }
} }
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button(role: .destructive) {
onDelete()
} label: {
Label("Delete", systemImage: "trash")
}
}
} }
private var isKeepAwakeEnabled: Bool { private var isKeepAwakeEnabled: Bool {
@ -90,8 +82,7 @@ struct AlarmRowView: View {
AlarmRowView( AlarmRowView(
alarm: Alarm(time: Date()), alarm: Alarm(time: Date()),
onToggle: {}, onToggle: {},
onEdit: {}, onEdit: {}
onDelete: {}
) )
} }
} }

View File

@ -37,33 +37,6 @@ struct NoiseView: View {
let isLandscape = geometry.size.width > geometry.size.height let isLandscape = geometry.size.width > geometry.size.height
let maxWidth = isLandscape ? Design.Size.maxContentWidthLandscape : Design.Size.maxContentWidthPortrait let maxWidth = isLandscape ? Design.Size.maxContentWidthLandscape : Design.Size.maxContentWidthPortrait
VStack(spacing: 0) {
// Custom Search Bar - Constrained to maxWidth
HStack {
HStack(spacing: Design.Spacing.small) {
Image(systemName: "magnifyingglass")
.foregroundColor(AppTextColors.secondary)
TextField("Search sounds", text: $searchText)
.textFieldStyle(.plain)
.foregroundColor(AppTextColors.primary)
if !searchText.isEmpty {
Button(action: { searchText = "" }) {
Image(systemName: "xmark.circle.fill")
.foregroundColor(AppTextColors.tertiary)
}
}
}
.padding(Design.Spacing.small)
.background(AppSurface.overlay)
.cornerRadius(Design.CornerRadius.medium)
}
.padding(.horizontal, Design.Spacing.large)
.padding(.top, Design.Spacing.medium)
.padding(.bottom, Design.Spacing.small)
.frame(maxWidth: maxWidth)
Group { Group {
if isLandscape { if isLandscape {
// Landscape layout: Player on left, sounds on right // Landscape layout: Player on left, sounds on right
@ -74,13 +47,15 @@ struct NoiseView: View {
} }
} }
.frame(maxWidth: maxWidth) .frame(maxWidth: maxWidth)
}
.frame(maxWidth: .infinity, alignment: .center) .frame(maxWidth: .infinity, alignment: .center)
} }
} }
.navigationTitle("Noise")
.navigationBarTitleDisplayMode(.inline)
.animation(.easeInOut(duration: 0.3), value: selectedSound) .animation(.easeInOut(duration: 0.3), value: selectedSound)
.searchable(
text: $searchText,
placement: .navigationBarDrawer(displayMode: .automatic),
prompt: "Search sounds"
)
} }
// MARK: - Computed Properties // MARK: - Computed Properties
@ -107,7 +82,6 @@ struct NoiseView: View {
if selectedSound != nil { if selectedSound != nil {
soundControlView soundControlView
.centered() .centered()
.padding(.bottom, Design.Spacing.medium)
} else { } else {
// Placeholder when no sound selected - Enhanced for CRO // Placeholder when no sound selected - Enhanced for CRO
VStack(spacing: Design.Spacing.medium) { VStack(spacing: Design.Spacing.medium) {
@ -134,31 +108,27 @@ struct NoiseView: View {
} }
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.padding(.vertical, Design.Spacing.large) .padding(.vertical, Design.Spacing.xLarge)
.background(AppSurface.overlay, in: RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge)) .background(AppSurface.overlay, in: RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge))
.overlay( .overlay(
RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge) RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge)
.stroke(AppBorder.subtle, lineWidth: Design.LineWidth.thin) .stroke(AppBorder.subtle, lineWidth: Design.LineWidth.thin)
) )
.padding(.bottom, Design.Spacing.medium)
} }
} }
.padding(.horizontal, Design.Spacing.large) .contentPadding(horizontal: Design.Spacing.large)
.padding(.top, Design.Spacing.small) .padding(.top, Design.Spacing.large)
.background(AppSurface.primary) .background(AppSurface.primary)
// Scrollable sound selection // Scrollable sound selection
List { ScrollView {
SoundCategoryView( SoundCategoryView(
sounds: viewModel.availableSounds, sounds: viewModel.availableSounds,
selectedSound: $selectedSound, selectedSound: $selectedSound,
searchText: $searchText searchText: $searchText
) )
.listRowInsets(EdgeInsets(top: 0, leading: Design.Spacing.large, bottom: Design.Spacing.large, trailing: Design.Spacing.large)) .contentPadding(horizontal: Design.Spacing.large, vertical: Design.Spacing.large)
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
} }
.listStyle(.plain)
.scrollContentBackground(.hidden) .scrollContentBackground(.hidden)
} }
} }

View File

@ -24,6 +24,7 @@ struct OnboardingView: View {
@State private var currentPage = 0 @State private var currentPage = 0
@State private var alarmKitPermissionGranted = false @State private var alarmKitPermissionGranted = false
@State private var keepAwakeEnabled = false @State private var keepAwakeEnabled = false
@State private var showCelebration = false
private let totalPages = 4 private let totalPages = 4
@ -58,8 +59,11 @@ struct OnboardingView: View {
.padding(.horizontal, Design.Spacing.xLarge) .padding(.horizontal, Design.Spacing.xLarge)
.padding(.bottom, Design.Spacing.xxLarge) .padding(.bottom, Design.Spacing.xxLarge)
} }
.frame(maxWidth: Design.Size.maxContentWidthPortrait)
.frame(maxWidth: .infinity, alignment: .center) // Celebration overlay
if showCelebration {
celebrationOverlay
}
} }
} }
@ -115,9 +119,9 @@ struct OnboardingView: View {
Text(text) Text(text)
.typography(.body) .typography(.body)
.foregroundStyle(AppTextColors.secondary) .foregroundStyle(AppTextColors.secondary)
Spacer()
} }
.frame(maxWidth: 320, alignment: .leading) // Constrain width and align content to leading
.frame(maxWidth: .infinity, alignment: .center) // Center the constrained box in the parent
.padding(.horizontal, Design.Spacing.xxLarge) .padding(.horizontal, Design.Spacing.xxLarge)
} }
@ -221,9 +225,9 @@ struct OnboardingView: View {
Text(text) Text(text)
.typography(.body) .typography(.body)
.foregroundStyle(AppTextColors.secondary) .foregroundStyle(AppTextColors.secondary)
Spacer()
} }
.frame(maxWidth: 320, alignment: .leading) // Constrain width and align content to leading
.frame(maxWidth: .infinity, alignment: .center) // Center the constrained box in the parent
.padding(.horizontal, Design.Spacing.xxLarge) .padding(.horizontal, Design.Spacing.xxLarge)
} }
@ -313,8 +317,6 @@ struct OnboardingView: View {
.typography(.callout) .typography(.callout)
.foregroundStyle(AppTextColors.secondary) .foregroundStyle(AppTextColors.secondary)
} }
.frame(maxWidth: 320, alignment: .leading) // Constrain width and align content to leading
.frame(maxWidth: .infinity, alignment: .center) // Center the constrained box in the parent
.padding(.horizontal, Design.Spacing.xxLarge) .padding(.horizontal, Design.Spacing.xxLarge)
} }
@ -373,6 +375,30 @@ struct OnboardingView: View {
} }
} }
// MARK: - Celebration
private var celebrationOverlay: some View {
ZStack {
Color.black.opacity(0.3)
.ignoresSafeArea()
VStack(spacing: Design.Spacing.large) {
Image(systemName: "party.popper.fill")
.font(.system(size: 60))
.foregroundStyle(AppAccent.primary)
Text("Let's go!")
.typography(.heroBold)
.foregroundStyle(AppTextColors.primary)
}
.padding(Design.Spacing.xxxLarge)
.background(AppSurface.overlay)
.cornerRadius(Design.CornerRadius.xxLarge)
.shadow(radius: 20)
}
.transition(.opacity.combined(with: .scale))
}
// MARK: - Actions // MARK: - Actions
private func requestAlarmKitPermission() { private func requestAlarmKitPermission() {
@ -421,8 +447,13 @@ struct OnboardingView: View {
} }
private func triggerCelebration() { private func triggerCelebration() {
// Snappier transition for a better feel withAnimation(.spring(duration: 0.4)) {
withAnimation(.spring(duration: 0.45, bounce: 0.2)) { showCelebration = true
}
// Dismiss after short celebration
Task {
try? await Task.sleep(for: .milliseconds(1200))
onComplete() onComplete()
} }
} }