Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
51d6859209
commit
c93d80e591
3
PRD.md
3
PRD.md
@ -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
|
||||||
|
|||||||
@ -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")
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {
|
||||||
Text("Ambient Sounds")
|
if !isPad {
|
||||||
.sectionTitleStyle()
|
Text("Ambient Sounds")
|
||||||
|
.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) {
|
||||||
Text("Ambient Sounds")
|
if !isPad {
|
||||||
.sectionTitleStyle()
|
Text("Ambient Sounds")
|
||||||
|
.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)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user