diff --git a/TheNoiseClock/Features/Alarms/Views/AlarmView.swift b/TheNoiseClock/Features/Alarms/Views/AlarmView.swift index 07d9be8..136f07b 100644 --- a/TheNoiseClock/Features/Alarms/Views/AlarmView.swift +++ b/TheNoiseClock/Features/Alarms/Views/AlarmView.swift @@ -24,60 +24,63 @@ struct AlarmView: View { ZStack { AppSurface.primary.ignoresSafeArea() - Group { - if viewModel.alarms.isEmpty { - VStack(spacing: Design.Spacing.large) { - if !isKeepAwakeEnabled { - AlarmLimitationsBanner() + GeometryReader { geometry in + let isLandscape = geometry.size.width > geometry.size.height + let maxWidth = isLandscape ? Design.Size.maxContentWidthLandscape : Design.Size.maxContentWidthPortrait + + Group { + if viewModel.alarms.isEmpty { + VStack(spacing: Design.Spacing.large) { + if !isKeepAwakeEnabled { + AlarmLimitationsBanner() + } + + EmptyAlarmsView { + showAddAlarm = true + } + .contentShape(Rectangle()) + .onTapGesture { + showAddAlarm = true + } } - - EmptyAlarmsView { - showAddAlarm = true - } - .contentShape(Rectangle()) - .onTapGesture { - showAddAlarm = true - } - } - .frame(maxWidth: Design.Size.maxContentWidthPortrait) - .frame(maxWidth: .infinity, alignment: .center) - } else { - List { - if !isKeepAwakeEnabled { - AlarmLimitationsBanner() - .listRowInsets(EdgeInsets(top: Design.Spacing.large, leading: Design.Spacing.large, bottom: Design.Spacing.small, trailing: Design.Spacing.large)) + } else { + List { + if !isKeepAwakeEnabled { + 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 + AlarmRowView( + alarm: alarm, + onToggle: { + Task { + await viewModel.toggleAlarm(id: alarm.id) + } + }, + onEdit: { + 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) + } } - - ForEach(viewModel.alarms) { alarm in - AlarmRowView( - alarm: alarm, - onToggle: { - Task { - await viewModel.toggleAlarm(id: alarm.id) - } - }, - onEdit: { - 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) + .scrollContentBackground(.hidden) + .background(AppSurface.primary.ignoresSafeArea()) } - .listStyle(.plain) - .scrollContentBackground(.hidden) - .background(AppSurface.primary.ignoresSafeArea()) - .frame(maxWidth: Design.Size.maxContentWidthPortrait) - .frame(maxWidth: .infinity, alignment: .center) } + .frame(maxWidth: maxWidth) + .frame(maxWidth: .infinity, alignment: .center) } } .navigationTitle(isPad ? "" : "Alarms") diff --git a/TheNoiseClock/Features/Noise/Views/NoiseView.swift b/TheNoiseClock/Features/Noise/Views/NoiseView.swift index 673879f..7cbdbae 100644 --- a/TheNoiseClock/Features/Noise/Views/NoiseView.swift +++ b/TheNoiseClock/Features/Noise/Views/NoiseView.swift @@ -37,25 +37,50 @@ struct NoiseView: View { let isLandscape = geometry.size.width > geometry.size.height let maxWidth = isLandscape ? Design.Size.maxContentWidthLandscape : Design.Size.maxContentWidthPortrait - Group { - if isLandscape { - // Landscape layout: Player on left, sounds on right - landscapeLayout - } else { - // Portrait layout: Stacked vertically - portraitLayout + 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 { + if isLandscape { + // Landscape layout: Player on left, sounds on right + landscapeLayout + } else { + // Portrait layout: Stacked vertically + portraitLayout + } + } + .frame(maxWidth: maxWidth) } - .frame(maxWidth: maxWidth) .frame(maxWidth: .infinity, alignment: .center) } } + .navigationTitle("Noise") + .navigationBarTitleDisplayMode(.inline) .animation(.easeInOut(duration: 0.3), value: selectedSound) - .searchable( - text: $searchText, - placement: .navigationBarDrawer(displayMode: .automatic), - prompt: "Search sounds" - ) } // MARK: - Computed Properties @@ -82,6 +107,7 @@ struct NoiseView: View { if selectedSound != nil { soundControlView .centered() + .padding(.bottom, Design.Spacing.medium) } else { // Placeholder when no sound selected - Enhanced for CRO VStack(spacing: Design.Spacing.medium) { @@ -108,27 +134,31 @@ struct NoiseView: View { } } .frame(maxWidth: .infinity) - .padding(.vertical, Design.Spacing.xLarge) + .padding(.vertical, Design.Spacing.large) .background(AppSurface.overlay, in: RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge)) .overlay( RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge) .stroke(AppBorder.subtle, lineWidth: Design.LineWidth.thin) ) + .padding(.bottom, Design.Spacing.medium) } } - .contentPadding(horizontal: Design.Spacing.large) - .padding(.top, Design.Spacing.large) + .padding(.horizontal, Design.Spacing.large) + .padding(.top, Design.Spacing.small) .background(AppSurface.primary) // Scrollable sound selection - ScrollView { + List { SoundCategoryView( sounds: viewModel.availableSounds, selectedSound: $selectedSound, searchText: $searchText ) - .contentPadding(horizontal: Design.Spacing.large, vertical: Design.Spacing.large) + .listRowInsets(EdgeInsets(top: 0, leading: Design.Spacing.large, bottom: Design.Spacing.large, trailing: Design.Spacing.large)) + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) } + .listStyle(.plain) .scrollContentBackground(.hidden) } } diff --git a/TheNoiseClock/Features/Onboarding/Views/OnboardingView.swift b/TheNoiseClock/Features/Onboarding/Views/OnboardingView.swift index de072af..fd13441 100644 --- a/TheNoiseClock/Features/Onboarding/Views/OnboardingView.swift +++ b/TheNoiseClock/Features/Onboarding/Views/OnboardingView.swift @@ -58,6 +58,8 @@ struct OnboardingView: View { .padding(.horizontal, Design.Spacing.xLarge) .padding(.bottom, Design.Spacing.xxLarge) } + .frame(maxWidth: Design.Size.maxContentWidthPortrait) + .frame(maxWidth: .infinity, alignment: .center) } } @@ -113,9 +115,9 @@ struct OnboardingView: View { Text(text) .typography(.body) .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) } @@ -219,9 +221,9 @@ struct OnboardingView: View { Text(text) .typography(.body) .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) } @@ -311,6 +313,8 @@ struct OnboardingView: View { .typography(.callout) .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) }