134 lines
4.3 KiB
Swift
134 lines
4.3 KiB
Swift
//
|
|
// NoiseView.swift
|
|
// TheNoiseClock
|
|
//
|
|
// Created by Matt Bruce on 9/7/25.
|
|
//
|
|
|
|
import SwiftUI
|
|
import AudioPlaybackKit
|
|
|
|
/// Main noise/audio player view
|
|
struct NoiseView: View {
|
|
|
|
// MARK: - Properties
|
|
@State private var viewModel = SoundViewModel()
|
|
@State private var selectedSound: Sound? {
|
|
didSet {
|
|
// Stop current playback when selecting a new sound
|
|
if let newSound = selectedSound, newSound != oldValue {
|
|
viewModel.selectSound(newSound)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Body
|
|
var body: some View {
|
|
GeometryReader { geometry in
|
|
let isLandscape = geometry.size.width > geometry.size.height
|
|
|
|
if isLandscape {
|
|
// Landscape layout: Player on left, sounds on right
|
|
landscapeLayout
|
|
} else {
|
|
// Portrait layout: Stacked vertically
|
|
portraitLayout
|
|
}
|
|
}
|
|
.animation(.easeInOut(duration: 0.3), value: selectedSound)
|
|
}
|
|
|
|
// MARK: - Computed Properties
|
|
private var soundControlView: some View {
|
|
SoundControlView(
|
|
isPlaying: viewModel.isPlaying,
|
|
selectedSound: selectedSound,
|
|
onPlay: { sound in
|
|
viewModel.playSound(sound)
|
|
},
|
|
onStop: {
|
|
viewModel.stopSound()
|
|
}
|
|
)
|
|
.transition(.opacity.combined(with: .scale))
|
|
}
|
|
|
|
// MARK: - Layouts
|
|
private var portraitLayout: some View {
|
|
VStack(spacing: 0) {
|
|
// Fixed header
|
|
VStack(alignment: .leading, spacing: UIConstants.Spacing.medium) {
|
|
Text("Ambient Sounds")
|
|
.sectionTitleStyle()
|
|
|
|
// Playback controls - always visible when sound is selected
|
|
if selectedSound != nil {
|
|
soundControlView
|
|
.centered()
|
|
}
|
|
}
|
|
.contentPadding(horizontal: UIConstants.Spacing.large)
|
|
.padding(.top, UIConstants.Spacing.large)
|
|
.background(Color(.systemBackground))
|
|
|
|
// Scrollable sound selection
|
|
ScrollView {
|
|
SoundCategoryView(
|
|
sounds: viewModel.availableSounds,
|
|
selectedSound: $selectedSound
|
|
)
|
|
.contentPadding(horizontal: UIConstants.Spacing.large, vertical: UIConstants.Spacing.large)
|
|
}
|
|
}
|
|
}
|
|
|
|
private var landscapeLayout: some View {
|
|
HStack(spacing: UIConstants.Spacing.large) {
|
|
// Left side: Player controls
|
|
VStack(alignment: .leading, spacing: UIConstants.Spacing.medium) {
|
|
Text("Ambient Sounds")
|
|
.sectionTitleStyle()
|
|
|
|
if selectedSound != nil {
|
|
soundControlView
|
|
} else {
|
|
// Placeholder when no sound selected
|
|
VStack(spacing: UIConstants.Spacing.small) {
|
|
Image(systemName: "music.note")
|
|
.font(.largeTitle)
|
|
.foregroundColor(.secondary)
|
|
|
|
Text("Select a sound to begin")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
.padding(.vertical, UIConstants.Spacing.large)
|
|
}
|
|
|
|
Spacer()
|
|
}
|
|
.frame(maxWidth: 400) // Reasonable width for player section
|
|
.contentPadding(horizontal: UIConstants.Spacing.large)
|
|
.padding(.top, UIConstants.Spacing.large)
|
|
|
|
// Right side: Sound selection
|
|
VStack(spacing: 0) {
|
|
ScrollView {
|
|
SoundCategoryView(
|
|
sounds: viewModel.availableSounds,
|
|
selectedSound: $selectedSound
|
|
)
|
|
.contentPadding(horizontal: UIConstants.Spacing.large, vertical: UIConstants.Spacing.large)
|
|
}
|
|
}
|
|
}
|
|
.contentPadding(horizontal: UIConstants.Spacing.medium)
|
|
}
|
|
}
|
|
|
|
// MARK: - Preview
|
|
#Preview {
|
|
NoiseView()
|
|
}
|