Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2026-02-01 09:23:27 -06:00
parent 51d6859209
commit c93d80e591
7 changed files with 35 additions and 35 deletions

3
PRD.md
View File

@ -324,6 +324,7 @@ These principles are fundamental to the project's long-term success and must be
- **Navigation destinations**: Deep linking for alarm editing - **Navigation destinations**: Deep linking for alarm editing
- **Toolbar integration**: Settings and add buttons in navigation bars - **Toolbar integration**: Settings and add buttons in navigation bars
- **Sheet presentations**: Modal settings and alarm creation - **Sheet presentations**: Modal settings and alarm creation
- **Title presentation**: Inline titles on iPhone; titles hidden on iPad tab screens
### Visual Design ### Visual Design
- **Rounded corners**: Modern iOS design language - **Rounded corners**: Modern iOS design language
@ -556,7 +557,7 @@ The following changes **automatically require** PRD updates:
- Snooze duration settings - Snooze duration settings
### Noise Tab ### Noise Tab
1. **Sound Selection**: Browse sounds by category with search functionality 1. **Sound Selection**: Browse sounds by category with system search in the navigation bar (iPhone and iPad)
2. **Sound Preview**: Long-press for 3-second preview 2. **Sound Preview**: Long-press for 3-second preview
3. **Visual Feedback**: Grid layout with clear selection states 3. **Visual Feedback**: Grid layout with clear selection states
4. **Auto-stop**: Automatically stops current sound when selecting new one 4. **Auto-stop**: Automatically stops current sound when selecting new one

View File

@ -52,8 +52,6 @@ struct ContentView: View {
ClockSettingsView(style: clockViewModel.style) { newStyle in ClockSettingsView(style: clockViewModel.style) { newStyle in
clockViewModel.updateStyle(newStyle) clockViewModel.updateStyle(newStyle)
} }
.navigationTitle("Settings")
.navigationBarTitleDisplayMode(.inline)
} }
.tabItem { .tabItem {
Label("Settings", systemImage: "gearshape") Label("Settings", systemImage: "gearshape")

View File

@ -18,6 +18,7 @@ struct AlarmView: View {
// MARK: - Body // MARK: - Body
var body: some View { var body: some View {
let isPad = UIDevice.current.userInterfaceIdiom == .pad
Group { Group {
if viewModel.alarms.isEmpty { if viewModel.alarms.isEmpty {
EmptyAlarmsView { EmptyAlarmsView {
@ -50,7 +51,8 @@ struct AlarmView: View {
.frame(maxWidth: .infinity, alignment: .center) .frame(maxWidth: .infinity, alignment: .center)
} }
} }
.navigationTitle("Alarms") .navigationTitle(isPad ? "" : "Alarms")
.navigationBarTitleDisplayMode(.inline)
.toolbar { .toolbar {
ToolbarItem(placement: .navigationBarTrailing) { ToolbarItem(placement: .navigationBarTrailing) {
Button { Button {

View File

@ -27,6 +27,7 @@ struct ClockSettingsView: View {
// MARK: - Body // MARK: - Body
var body: some View { var body: some View {
let isPad = UIDevice.current.userInterfaceIdiom == .pad
ScrollView { ScrollView {
VStack(spacing: Design.Spacing.xxLarge) { VStack(spacing: Design.Spacing.xxLarge) {
BasicAppearanceSection( BasicAppearanceSection(
@ -101,7 +102,7 @@ struct ClockSettingsView: View {
.padding(.bottom, Design.Spacing.xxxLarge) .padding(.bottom, Design.Spacing.xxxLarge)
} }
.background(AppSurface.primary) .background(AppSurface.primary)
.navigationTitle("Clock Settings") .navigationTitle(isPad ? "" : "Settings")
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.onAppear { .onAppear {
digitColor = Color(hex: style.digitColorHex) ?? .white digitColor = Color(hex: style.digitColorHex) ?? .white

View File

@ -15,8 +15,10 @@ struct ClockToolbar: View {
// MARK: - Body // MARK: - Body
var body: some View { var body: some View {
let isPad = UIDevice.current.userInterfaceIdiom == .pad
EmptyView() EmptyView()
.navigationTitle(isDisplayMode ? "" : "Clock") .navigationTitle(isDisplayMode || isPad ? "" : "Clock")
.navigationBarTitleDisplayMode(.inline)
.navigationBarBackButtonHidden(isDisplayMode) .navigationBarBackButtonHidden(isDisplayMode)
.toolbar(isDisplayMode ? .hidden : .automatic) .toolbar(isDisplayMode ? .hidden : .automatic)
} }

View File

@ -16,7 +16,7 @@ struct SoundCategoryView: View {
let sounds: [Sound] let sounds: [Sound]
@Binding var selectedSound: Sound? @Binding var selectedSound: Sound?
@State private var selectedCategory: SoundCategory = .all @State private var selectedCategory: SoundCategory = .all
@State private var searchText: String = "" @Binding var searchText: String
@State private var viewModel = SoundViewModel() @State private var viewModel = SoundViewModel()
// MARK: - Computed Properties // MARK: - Computed Properties
@ -69,9 +69,6 @@ struct SoundCategoryView: View {
// MARK: - Body // MARK: - Body
var body: some View { var body: some View {
VStack(spacing: Design.Spacing.medium) { VStack(spacing: Design.Spacing.medium) {
// Search Bar
searchBar
// Category Tabs // Category Tabs
categoryTabs categoryTabs
@ -83,24 +80,6 @@ struct SoundCategoryView: View {
.padding(.top, Design.Spacing.medium) .padding(.top, Design.Spacing.medium)
} }
// MARK: - Subviews
private var searchBar: some View {
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(.secondary)
TextField("Search sounds...", text: $searchText)
.textFieldStyle(.plain)
.foregroundColor(.primary)
.autocorrectionDisabled()
.textInputAutocapitalization(.never)
}
.padding(.horizontal, Design.Spacing.medium)
.padding(.vertical, Design.Spacing.small)
.background(Color(.systemGray6))
.cornerRadius(10)
}
private var categoryTabs: some View { private var categoryTabs: some View {
ScrollView(.horizontal, showsIndicators: false) { ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: Design.Spacing.small) { HStack(spacing: Design.Spacing.small) {
@ -257,7 +236,8 @@ struct SoundCard: View {
Sound(name: "Fan Noise", fileName: "fan-noise.mp3", category: "mechanical", description: "Fan sounds"), Sound(name: "Fan Noise", fileName: "fan-noise.mp3", category: "mechanical", description: "Fan sounds"),
Sound(name: "Digital Alarm", fileName: "digital-alarm.mp3", category: "alarm", description: "Alarm sound") Sound(name: "Digital Alarm", fileName: "digital-alarm.mp3", category: "alarm", description: "Alarm sound")
], ],
selectedSound: .constant(nil) selectedSound: .constant(nil),
searchText: .constant("")
) )
.padding() .padding()
} }

View File

@ -22,6 +22,11 @@ struct NoiseView: View {
} }
} }
} }
@State private var searchText: String = ""
private var isPad: Bool {
UIDevice.current.userInterfaceIdiom == .pad
}
// MARK: - Body // MARK: - Body
var body: some View { var body: some View {
@ -42,6 +47,11 @@ struct NoiseView: View {
.frame(maxWidth: .infinity, alignment: .center) .frame(maxWidth: .infinity, alignment: .center)
} }
.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
@ -64,8 +74,10 @@ struct NoiseView: View {
VStack(spacing: 0) { VStack(spacing: 0) {
// Fixed header // Fixed header
VStack(alignment: .leading, spacing: Design.Spacing.medium) { VStack(alignment: .leading, spacing: Design.Spacing.medium) {
if !isPad {
Text("Ambient Sounds") Text("Ambient Sounds")
.sectionTitleStyle() .sectionTitleStyle()
}
// Playback controls - always visible when sound is selected // Playback controls - always visible when sound is selected
if selectedSound != nil { if selectedSound != nil {
@ -81,7 +93,8 @@ struct NoiseView: View {
ScrollView { ScrollView {
SoundCategoryView( SoundCategoryView(
sounds: viewModel.availableSounds, sounds: viewModel.availableSounds,
selectedSound: $selectedSound selectedSound: $selectedSound,
searchText: $searchText
) )
.contentPadding(horizontal: Design.Spacing.large, vertical: Design.Spacing.large) .contentPadding(horizontal: Design.Spacing.large, vertical: Design.Spacing.large)
} }
@ -92,8 +105,10 @@ struct NoiseView: View {
HStack(spacing: Design.Spacing.large) { HStack(spacing: Design.Spacing.large) {
// Left side: Player controls // Left side: Player controls
VStack(alignment: .leading, spacing: Design.Spacing.medium) { VStack(alignment: .leading, spacing: Design.Spacing.medium) {
if !isPad {
Text("Ambient Sounds") Text("Ambient Sounds")
.sectionTitleStyle() .sectionTitleStyle()
}
if selectedSound != nil { if selectedSound != nil {
soundControlView soundControlView
@ -123,7 +138,8 @@ struct NoiseView: View {
ScrollView { ScrollView {
SoundCategoryView( SoundCategoryView(
sounds: viewModel.availableSounds, sounds: viewModel.availableSounds,
selectedSound: $selectedSound selectedSound: $selectedSound,
searchText: $searchText
) )
.contentPadding(horizontal: Design.Spacing.large, vertical: Design.Spacing.large) .contentPadding(horizontal: Design.Spacing.large, vertical: Design.Spacing.large)
} }