From 7bd01554de4b863d2db145c50ab57aef2a39e3ec Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Sat, 31 Jan 2026 11:54:14 -0600 Subject: [PATCH] Signed-off-by: Matt Bruce --- TheNoiseClock/App/ContentView.swift | 29 +++- .../Features/Alarms/Views/AlarmView.swift | 5 + .../Features/Clock/Models/ClockStyle.swift | 2 +- .../Clock/Services/AmbientLightService.swift | 12 +- .../Features/Clock/State/ClockViewModel.swift | 10 +- .../Clock/Views/ClockSettingsView.swift | 143 +++++++++--------- .../Features/Clock/Views/ClockView.swift | 15 +- .../Clock/Views/Components/ClockToolbar.swift | 11 -- .../Features/Noise/Views/NoiseView.swift | 17 ++- 9 files changed, 125 insertions(+), 119 deletions(-) diff --git a/TheNoiseClock/App/ContentView.swift b/TheNoiseClock/App/ContentView.swift index 274a8ea..7d5aeb2 100644 --- a/TheNoiseClock/App/ContentView.swift +++ b/TheNoiseClock/App/ContentView.swift @@ -12,14 +12,25 @@ import Bedrock struct ContentView: View { // MARK: - Body + private enum Tab: Hashable { + case clock + case alarms + case noise + case settings + } + + @State private var selectedTab: Tab = .clock + @State private var clockViewModel = ClockViewModel() + var body: some View { - TabView { + TabView(selection: $selectedTab) { NavigationStack { - ClockView() + ClockView(viewModel: clockViewModel) } .tabItem { Label("Clock", systemImage: "clock") } + .tag(Tab.clock) NavigationStack { AlarmView() @@ -27,6 +38,7 @@ struct ContentView: View { .tabItem { Label("Alarms", systemImage: "alarm") } + .tag(Tab.alarms) NavigationStack { NoiseView() @@ -34,6 +46,19 @@ struct ContentView: View { .tabItem { Label("Noise", systemImage: "waveform") } + .tag(Tab.noise) + + NavigationStack { + ClockSettingsView(style: clockViewModel.style) { newStyle in + clockViewModel.updateStyle(newStyle) + } + .navigationTitle("Settings") + .navigationBarTitleDisplayMode(.inline) + } + .tabItem { + Label("Settings", systemImage: "gearshape") + } + .tag(Tab.settings) } .accentColor(AppAccent.primary) .background(Color.Branding.primary.ignoresSafeArea()) diff --git a/TheNoiseClock/Features/Alarms/Views/AlarmView.swift b/TheNoiseClock/Features/Alarms/Views/AlarmView.swift index 7ed61c8..c540e82 100644 --- a/TheNoiseClock/Features/Alarms/Views/AlarmView.swift +++ b/TheNoiseClock/Features/Alarms/Views/AlarmView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Bedrock /// Main alarm management view struct AlarmView: View { @@ -26,6 +27,8 @@ struct AlarmView: View { .onTapGesture { showAddAlarm = true } + .frame(maxWidth: Design.Size.maxContentWidthPortrait) + .frame(maxWidth: .infinity, alignment: .center) } else { List { ForEach(viewModel.alarms) { alarm in @@ -43,6 +46,8 @@ struct AlarmView: View { } .onDelete(perform: deleteAlarm) } + .frame(maxWidth: Design.Size.maxContentWidthPortrait) + .frame(maxWidth: .infinity, alignment: .center) } } .navigationTitle("Alarms") diff --git a/TheNoiseClock/Features/Clock/Models/ClockStyle.swift b/TheNoiseClock/Features/Clock/Models/ClockStyle.swift index 4a490c5..e376f18 100644 --- a/TheNoiseClock/Features/Clock/Models/ClockStyle.swift +++ b/TheNoiseClock/Features/Clock/Models/ClockStyle.swift @@ -354,7 +354,7 @@ class ClockStyle: Codable, Equatable { // Color-aware brightness adaptation let colorAwareBrightness = getColorAwareBrightness() - Design.debugLog("[brightness] effectiveBrightness: Color-aware brightness = \(String(format: \"%.2f\", colorAwareBrightness))") + Design.debugLog("[brightness] effectiveBrightness: Color-aware brightness = \(String(format: "%.2f", colorAwareBrightness))") return colorAwareBrightness } diff --git a/TheNoiseClock/Features/Clock/Services/AmbientLightService.swift b/TheNoiseClock/Features/Clock/Services/AmbientLightService.swift index 5b733d6..4cca505 100644 --- a/TheNoiseClock/Features/Clock/Services/AmbientLightService.swift +++ b/TheNoiseClock/Features/Clock/Services/AmbientLightService.swift @@ -61,15 +61,15 @@ class AmbientLightService { let previousBrightness = UIScreen.main.brightness Design.debugLog("[ambient] AmbientLightService.setBrightness:") - Design.debugLog("[ambient] - Requested brightness: \(String(format: \"%.2f\", brightness))") - Design.debugLog("[ambient] - Clamped brightness: \(String(format: \"%.2f\", clampedBrightness))") - Design.debugLog("[ambient] - Previous screen brightness: \(String(format: \"%.2f\", previousBrightness))") + Design.debugLog("[ambient] - Requested brightness: \(String(format: "%.2f", brightness))") + Design.debugLog("[ambient] - Clamped brightness: \(String(format: "%.2f", clampedBrightness))") + Design.debugLog("[ambient] - Previous screen brightness: \(String(format: "%.2f", previousBrightness))") UIScreen.main.brightness = clampedBrightness currentBrightness = clampedBrightness - Design.debugLog("[ambient] - New screen brightness: \(String(format: \"%.2f\", UIScreen.main.brightness))") - Design.debugLog("[ambient] - Service currentBrightness: \(String(format: \"%.2f\", currentBrightness))") + Design.debugLog("[ambient] - New screen brightness: \(String(format: "%.2f", UIScreen.main.brightness))") + Design.debugLog("[ambient] - Service currentBrightness: \(String(format: "%.2f", currentBrightness))") } /// Get current screen brightness @@ -90,7 +90,7 @@ class AmbientLightService { let previousBrightness = currentBrightness currentBrightness = newBrightness - Design.debugLog("[ambient] AmbientLightService: Brightness changed from \(String(format: \"%.2f\", previousBrightness)) to \(String(format: \"%.2f\", newBrightness))") + Design.debugLog("[ambient] AmbientLightService: Brightness changed from \(String(format: "%.2f", previousBrightness)) to \(String(format: "%.2f", newBrightness))") // Notify that brightness changed onBrightnessChange?() diff --git a/TheNoiseClock/Features/Clock/State/ClockViewModel.swift b/TheNoiseClock/Features/Clock/State/ClockViewModel.swift index 4f9a2c7..6afd8a8 100644 --- a/TheNoiseClock/Features/Clock/State/ClockViewModel.swift +++ b/TheNoiseClock/Features/Clock/State/ClockViewModel.swift @@ -221,16 +221,16 @@ class ClockViewModel { Design.debugLog("[brightness] Auto Brightness Debug:") Design.debugLog("[brightness] - Auto brightness enabled: \(style.autoBrightness)") - Design.debugLog("[brightness] - Current screen brightness: \(String(format: \"%.2f\", currentScreenBrightness))") - Design.debugLog("[brightness] - Target brightness: \(String(format: \"%.2f\", targetBrightness))") + Design.debugLog("[brightness] - Current screen brightness: \(String(format: "%.2f", currentScreenBrightness))") + Design.debugLog("[brightness] - Target brightness: \(String(format: "%.2f", targetBrightness))") Design.debugLog("[brightness] - Night mode active: \(isNightMode)") Design.debugLog("[brightness] - Color theme: \(style.selectedColorTheme)") - Design.debugLog("[brightness] - Ambient light threshold: \(String(format: \"%.2f\", style.ambientLightThreshold))") + Design.debugLog("[brightness] - Ambient light threshold: \(String(format: "%.2f", style.ambientLightThreshold))") ambientLightService.setBrightness(targetBrightness) - Design.debugLog("[brightness] - Brightness set to: \(String(format: \"%.2f\", targetBrightness))") - Design.debugLog("[brightness] - Actual screen brightness now: \(String(format: \"%.2f\", UIScreen.main.brightness))") + Design.debugLog("[brightness] - Brightness set to: \(String(format: "%.2f", targetBrightness))") + Design.debugLog("[brightness] - Actual screen brightness now: \(String(format: "%.2f", UIScreen.main.brightness))") Design.debugLog("[brightness] ---") } else { Design.debugLog("[brightness] Auto Brightness: DISABLED") diff --git a/TheNoiseClock/Features/Clock/Views/ClockSettingsView.swift b/TheNoiseClock/Features/Clock/Views/ClockSettingsView.swift index d8ce42e..6619c1f 100644 --- a/TheNoiseClock/Features/Clock/Views/ClockSettingsView.swift +++ b/TheNoiseClock/Features/Clock/Views/ClockSettingsView.swift @@ -18,7 +18,6 @@ struct ClockSettingsView: View { @State private var digitColor: Color = .white @State private var backgroundColor: Color = .black @State private var showAdvancedSettings = false - @Environment(\.dismiss) private var dismiss // MARK: - Init init(style: ClockStyle, onCommit: @escaping (ClockStyle) -> Void) { @@ -28,94 +27,88 @@ struct ClockSettingsView: View { // MARK: - Body var body: some View { - NavigationStack { - ScrollView { - VStack(spacing: Design.Spacing.xxLarge) { - BasicAppearanceSection( - style: $style, - digitColor: $digitColor, - backgroundColor: $backgroundColor - ) + ScrollView { + VStack(spacing: Design.Spacing.xxLarge) { + BasicAppearanceSection( + style: $style, + digitColor: $digitColor, + backgroundColor: $backgroundColor + ) - BasicDisplaySection(style: $style) + BasicDisplaySection(style: $style) - if showAdvancedSettings { - AdvancedAppearanceSection(style: $style) + if showAdvancedSettings { + AdvancedAppearanceSection(style: $style) - FontSection(style: $style) + FontSection(style: $style) - NightModeSection(style: $style) + NightModeSection(style: $style) - OverlaySection(style: $style) + OverlaySection(style: $style) - AdvancedDisplaySection(style: $style) - } + AdvancedDisplaySection(style: $style) + } - SettingsSectionHeader( - title: "Advanced", - systemImage: "gearshape", + SettingsSectionHeader( + title: "Advanced", + systemImage: "gearshape", + accentColor: AppAccent.primary + ) + + SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) { + SettingsToggle( + title: "Show Advanced Settings", + subtitle: "Reveal additional customization options", + isOn: $showAdvancedSettings, accentColor: AppAccent.primary ) + } - SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) { - SettingsToggle( - title: "Show Advanced Settings", - subtitle: "Reveal additional customization options", - isOn: $showAdvancedSettings, - accentColor: AppAccent.primary + #if DEBUG + SettingsSectionHeader( + title: "Debug", + systemImage: "ant.fill", + accentColor: AppStatus.error + ) + + SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) { + SettingsNavigationRow( + title: "Icon Generator", + subtitle: "Generate and save app icon", + backgroundColor: AppSurface.primary + ) { + IconGeneratorView(config: .noiseClock, appName: "TheNoiseClock") + } + + SettingsNavigationRow( + title: "Branding Preview", + subtitle: "Preview icon and launch screen", + backgroundColor: AppSurface.primary + ) { + BrandingPreviewView( + iconConfig: .noiseClock, + launchConfig: .noiseClock, + appName: "TheNoiseClock" ) } - - #if DEBUG - SettingsSectionHeader( - title: "Debug", - systemImage: "ant.fill", - accentColor: AppStatus.error - ) - - SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) { - SettingsNavigationRow( - title: "Icon Generator", - subtitle: "Generate and save app icon", - backgroundColor: AppSurface.primary - ) { - IconGeneratorView(config: .noiseClock, appName: "TheNoiseClock") - } - - SettingsNavigationRow( - title: "Branding Preview", - subtitle: "Preview icon and launch screen", - backgroundColor: AppSurface.primary - ) { - BrandingPreviewView( - iconConfig: .noiseClock, - launchConfig: .noiseClock, - appName: "TheNoiseClock" - ) - } - } - #endif } - .padding(.horizontal, Design.Spacing.large) - .padding(.top, Design.Spacing.large) - .padding(.bottom, Design.Spacing.xxxLarge) - } - .background(AppSurface.primary) - .navigationTitle("Clock Settings") - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - Button("Done") { dismiss() } - .foregroundStyle(AppAccent.primary) - } - } - .onAppear { - digitColor = Color(hex: style.digitColorHex) ?? .white - backgroundColor = Color(hex: style.backgroundHex) ?? .black - } - .onDisappear { - onCommit(style) + #endif } + .frame(maxWidth: Design.Size.maxContentWidthPortrait) + .frame(maxWidth: .infinity, alignment: .center) + .padding(.horizontal, Design.Spacing.large) + .padding(.top, Design.Spacing.large) + .padding(.bottom, Design.Spacing.xxxLarge) + } + .background(AppSurface.primary) + .navigationTitle("Clock Settings") + .navigationBarTitleDisplayMode(.inline) + .onAppear { + digitColor = Color(hex: style.digitColorHex) ?? .white + backgroundColor = Color(hex: style.backgroundHex) ?? .black + } + .onDisappear { + onCommit(style) } } } diff --git a/TheNoiseClock/Features/Clock/Views/ClockView.swift b/TheNoiseClock/Features/Clock/Views/ClockView.swift index fb5fb17..5848ccf 100644 --- a/TheNoiseClock/Features/Clock/Views/ClockView.swift +++ b/TheNoiseClock/Features/Clock/Views/ClockView.swift @@ -12,8 +12,7 @@ import Bedrock struct ClockView: View { // MARK: - Properties - @State private var viewModel = ClockViewModel() - @State private var showSettings = false + @Bindable var viewModel: ClockViewModel @State private var showFullScreenHint = false // MARK: - Body @@ -43,20 +42,10 @@ struct ClockView: View { } .ignoresSafeArea(.all, edges: viewModel.isDisplayMode ? .bottom : []) .statusBarHidden(viewModel.isDisplayMode) - .sheet(isPresented: $showSettings) { - ClockSettingsView(style: viewModel.style) { newStyle in - viewModel.updateStyle(newStyle) - } - .presentationDetents([.large]) - .presentationDragIndicator(.hidden) - .presentationBackgroundInteraction(.enabled) - .presentationBackground(AppSurface.overlay) - } .overlay { // Toolbar overlay ClockToolbar( isDisplayMode: viewModel.isDisplayMode, - onSettingsTap: { showSettings = true }, onFullScreenTap: { if !viewModel.isDisplayMode { viewModel.toggleDisplayMode() @@ -87,7 +76,7 @@ struct ClockView: View { // MARK: - Preview #Preview { NavigationStack { - ClockView() + ClockView(viewModel: ClockViewModel()) } .frame(width: 400, height: 600) .background(Color.black) diff --git a/TheNoiseClock/Features/Clock/Views/Components/ClockToolbar.swift b/TheNoiseClock/Features/Clock/Views/Components/ClockToolbar.swift index cc8f024..e3ad805 100644 --- a/TheNoiseClock/Features/Clock/Views/Components/ClockToolbar.swift +++ b/TheNoiseClock/Features/Clock/Views/Components/ClockToolbar.swift @@ -12,7 +12,6 @@ struct ClockToolbar: View { // MARK: - Properties let isDisplayMode: Bool - let onSettingsTap: () -> Void let onFullScreenTap: () -> Void // MARK: - Body @@ -30,15 +29,6 @@ struct ClockToolbar: View { .transition(.opacity) } .accessibilityLabel("Enter Full Screen Mode") - - Button { - onSettingsTap() - } label: { - Image(systemName: "gear") - .font(.title2) - .transition(.opacity) - } - .accessibilityLabel("Clock Settings") } } } @@ -52,7 +42,6 @@ struct ClockToolbar: View { NavigationStack { ClockToolbar( isDisplayMode: false, - onSettingsTap: {}, onFullScreenTap: {} ) } diff --git a/TheNoiseClock/Features/Noise/Views/NoiseView.swift b/TheNoiseClock/Features/Noise/Views/NoiseView.swift index cb43e85..0d2ece1 100644 --- a/TheNoiseClock/Features/Noise/Views/NoiseView.swift +++ b/TheNoiseClock/Features/Noise/Views/NoiseView.swift @@ -27,14 +27,19 @@ struct NoiseView: View { var body: some View { GeometryReader { geometry in let isLandscape = geometry.size.width > geometry.size.height + let maxWidth = isLandscape ? Design.Size.maxContentWidthLandscape : Design.Size.maxContentWidthPortrait - if isLandscape { - // Landscape layout: Player on left, sounds on right - landscapeLayout - } else { - // Portrait layout: Stacked vertically - portraitLayout + Group { + if isLandscape { + // Landscape layout: Player on left, sounds on right + landscapeLayout + } else { + // Portrait layout: Stacked vertically + portraitLayout + } } + .frame(maxWidth: maxWidth) + .frame(maxWidth: .infinity, alignment: .center) } .animation(.easeInOut(duration: 0.3), value: selectedSound) }