Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
6d3837022d
commit
706aba5d30
@ -10,10 +10,10 @@ import Foundation
|
|||||||
/// Configuration model for sound system loaded from JSON
|
/// Configuration model for sound system loaded from JSON
|
||||||
public struct SoundConfiguration: Codable {
|
public struct SoundConfiguration: Codable {
|
||||||
public let sounds: [Sound]
|
public let sounds: [Sound]
|
||||||
public let categories: [SoundCategory]
|
public let categories: [SoundCategory]?
|
||||||
public let settings: AudioSettings
|
public let settings: AudioSettings
|
||||||
|
|
||||||
public init(sounds: [Sound], categories: [SoundCategory], settings: AudioSettings) {
|
public init(sounds: [Sound], categories: [SoundCategory]? = nil, settings: AudioSettings) {
|
||||||
self.sounds = sounds
|
self.sounds = sounds
|
||||||
self.categories = categories
|
self.categories = categories
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
@ -109,7 +109,7 @@ public class SoundConfigurationService {
|
|||||||
|
|
||||||
/// Get available categories
|
/// Get available categories
|
||||||
public func getAvailableCategories() -> [SoundCategory] {
|
public func getAvailableCategories() -> [SoundCategory] {
|
||||||
return getConfiguration().categories
|
return getConfiguration().categories ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get audio settings
|
/// Get audio settings
|
||||||
|
|||||||
100
TheNoiseClock/Models/SoundCategory.swift
Normal file
100
TheNoiseClock/Models/SoundCategory.swift
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
//
|
||||||
|
// SoundCategory.swift
|
||||||
|
// TheNoiseClock
|
||||||
|
//
|
||||||
|
// Created by Matt Bruce on 9/8/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Enum representing sound categories with associated icons, display names, and metadata
|
||||||
|
public enum SoundCategory: String, CaseIterable, Identifiable {
|
||||||
|
case all = "all"
|
||||||
|
case colored = "colored"
|
||||||
|
case ambient = "ambient"
|
||||||
|
case nature = "nature"
|
||||||
|
case mechanical = "mechanical"
|
||||||
|
case alarm = "alarm"
|
||||||
|
|
||||||
|
public var id: String { rawValue }
|
||||||
|
|
||||||
|
/// Display name for the category
|
||||||
|
public var displayName: String {
|
||||||
|
switch self {
|
||||||
|
case .all: return "All"
|
||||||
|
case .colored: return "Colored"
|
||||||
|
case .ambient: return "Ambient"
|
||||||
|
case .nature: return "Nature"
|
||||||
|
case .mechanical: return "Mechanical"
|
||||||
|
case .alarm: return "Alarm Sounds"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SF Symbol icon name for the category
|
||||||
|
public var icon: String {
|
||||||
|
switch self {
|
||||||
|
case .all: return "speaker.wave.2"
|
||||||
|
case .colored: return "waveform.path"
|
||||||
|
case .ambient: return "waveform"
|
||||||
|
case .nature: return "cloud.rain"
|
||||||
|
case .mechanical: return "fan"
|
||||||
|
case .alarm: return "alarm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bundle name for organizing audio files
|
||||||
|
public var bundleName: String? {
|
||||||
|
switch self {
|
||||||
|
case .all: return nil
|
||||||
|
case .colored: return "Colored"
|
||||||
|
case .ambient: return "Ambient"
|
||||||
|
case .nature: return "Nature"
|
||||||
|
case .mechanical: return "Mechanical"
|
||||||
|
case .alarm: return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Description of the category
|
||||||
|
public var description: String {
|
||||||
|
switch self {
|
||||||
|
case .all: return "All available sounds"
|
||||||
|
case .colored: return "Synthetic noise signals for focus, sleep, and relaxation"
|
||||||
|
case .ambient: return "General ambient sounds"
|
||||||
|
case .nature: return "Natural environmental sounds"
|
||||||
|
case .mechanical: return "Mechanical and electronic sounds"
|
||||||
|
case .alarm: return "Wake-up and notification alarm sounds"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Preferred sort order for categories (lower number = appears first)
|
||||||
|
public var sortOrder: Int {
|
||||||
|
switch self {
|
||||||
|
case .all: return 0
|
||||||
|
case .colored: return 1
|
||||||
|
case .ambient: return 2
|
||||||
|
case .nature: return 3
|
||||||
|
case .mechanical: return 4
|
||||||
|
case .alarm: return 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize from string, returning nil if invalid
|
||||||
|
public init?(from string: String) {
|
||||||
|
self.init(rawValue: string)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all non-alarm categories for noise selection
|
||||||
|
public static var noiseCategories: [SoundCategory] {
|
||||||
|
return [.all, .colored, .ambient, .nature, .mechanical]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all categories sorted by preferred order
|
||||||
|
public static var sortedCategories: [SoundCategory] {
|
||||||
|
return allCases.sorted { $0.sortOrder < $1.sortOrder }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get noise categories sorted by preferred order
|
||||||
|
public static var sortedNoiseCategories: [SoundCategory] {
|
||||||
|
return noiseCategories.sorted { $0.sortOrder < $1.sortOrder }
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
TheNoiseClock/Resources/Ambient.bundle/ambient-waves.mp3
Normal file
BIN
TheNoiseClock/Resources/Ambient.bundle/ambient-waves.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Ambient.bundle/atmospheric-pad.mp3
Normal file
BIN
TheNoiseClock/Resources/Ambient.bundle/atmospheric-pad.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Ambient.bundle/calm-ambient-pad.mp3
Normal file
BIN
TheNoiseClock/Resources/Ambient.bundle/calm-ambient-pad.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Ambient.bundle/dark-ambient.mp3
Normal file
BIN
TheNoiseClock/Resources/Ambient.bundle/dark-ambient.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Ambient.bundle/ethereal-ambient.mp3
Normal file
BIN
TheNoiseClock/Resources/Ambient.bundle/ethereal-ambient.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Colored.bundle/brown-noise.mp3
Normal file
BIN
TheNoiseClock/Resources/Colored.bundle/brown-noise.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Colored.bundle/green-noise.mp3
Normal file
BIN
TheNoiseClock/Resources/Colored.bundle/green-noise.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Colored.bundle/grey-noise.mp3
Normal file
BIN
TheNoiseClock/Resources/Colored.bundle/grey-noise.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Colored.bundle/pink-noise.mp3
Normal file
BIN
TheNoiseClock/Resources/Colored.bundle/pink-noise.mp3
Normal file
Binary file not shown.
Binary file not shown.
BIN
TheNoiseClock/Resources/Mechanical.bundle/clock-ticking.mp3
Normal file
BIN
TheNoiseClock/Resources/Mechanical.bundle/clock-ticking.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Mechanical.bundle/electric-fan.mp3
Normal file
BIN
TheNoiseClock/Resources/Mechanical.bundle/electric-fan.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Mechanical.bundle/engine-idling.mp3
Normal file
BIN
TheNoiseClock/Resources/Mechanical.bundle/engine-idling.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Nature.bundle/crickets-night.mp3
Normal file
BIN
TheNoiseClock/Resources/Nature.bundle/crickets-night.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Nature.bundle/distant-thunderstorm.mp3
Normal file
BIN
TheNoiseClock/Resources/Nature.bundle/distant-thunderstorm.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Nature.bundle/forest-ambience.mp3
Normal file
BIN
TheNoiseClock/Resources/Nature.bundle/forest-ambience.mp3
Normal file
Binary file not shown.
BIN
TheNoiseClock/Resources/Nature.bundle/ocean-waves.mp3
Normal file
BIN
TheNoiseClock/Resources/Nature.bundle/ocean-waves.mp3
Normal file
Binary file not shown.
@ -42,14 +42,6 @@
|
|||||||
"bundleName": null
|
"bundleName": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"categories": [
|
|
||||||
{
|
|
||||||
"id": "alarm",
|
|
||||||
"name": "Alarm Sounds",
|
|
||||||
"description": "Wake-up and notification alarm sounds",
|
|
||||||
"bundleName": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"settings": {
|
"settings": {
|
||||||
"defaultVolume": 1.0,
|
"defaultVolume": 1.0,
|
||||||
"defaultLoopCount": -1,
|
"defaultLoopCount": -1,
|
||||||
|
|||||||
@ -4,45 +4,164 @@
|
|||||||
"id": "white-noise",
|
"id": "white-noise",
|
||||||
"name": "White Noise",
|
"name": "White Noise",
|
||||||
"fileName": "white-noise.mp3",
|
"fileName": "white-noise.mp3",
|
||||||
"category": "ambient",
|
"category": "colored",
|
||||||
"description": "Classic white noise for focus and relaxation",
|
"description": "Classic white noise with equal energy across frequencies for focus and relaxation",
|
||||||
"bundleName": "Ambient"
|
"bundleName": "Colored"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pink-noise",
|
||||||
|
"name": "Pink Noise",
|
||||||
|
"fileName": "pink-noise.mp3",
|
||||||
|
"category": "colored",
|
||||||
|
"description": "Soft, warm noise resembling steady rain, ideal for relaxation",
|
||||||
|
"bundleName": "Colored",
|
||||||
|
"sourceUrl": "https://freesound.org/search/?q=pink+noise+loop"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "brown-noise",
|
||||||
|
"name": "Brown Noise",
|
||||||
|
"fileName": "brown-noise.mp3",
|
||||||
|
"category": "colored",
|
||||||
|
"description": "Deep, rumbling noise like distant thunder, great for deep sleep",
|
||||||
|
"bundleName": "Colored"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "green-noise",
|
||||||
|
"name": "Green Noise",
|
||||||
|
"fileName": "green-noise.mp3",
|
||||||
|
"category": "colored",
|
||||||
|
"description": "Mid-range noise resembling rustling leaves, soothing and nature-like",
|
||||||
|
"bundleName": "Colored"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "grey-noise",
|
||||||
|
"name": "Grey Noise",
|
||||||
|
"fileName": "grey-noise.mp3",
|
||||||
|
"category": "colored",
|
||||||
|
"description": "Balanced noise adjusted for human hearing, perfect for calm focus",
|
||||||
|
"bundleName": "Colored"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "heavy-rain",
|
"id": "heavy-rain",
|
||||||
"name": "Heavy Rain White Noise",
|
"name": "Heavy Rain",
|
||||||
"fileName": "heavy-rain-white-noise.mp3",
|
"fileName": "heavy-rain.mp3",
|
||||||
"category": "nature",
|
"category": "nature",
|
||||||
"description": "Heavy rainfall sounds for peaceful sleep",
|
"description": "Heavy rainfall sounds for peaceful sleep",
|
||||||
"bundleName": "Nature"
|
"bundleName": "Nature"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "fan-noise",
|
"id": "ocean-waves",
|
||||||
"name": "Fan White Noise",
|
"name": "Ocean Waves",
|
||||||
"fileName": "fan-white-noise-heater.mp3",
|
"fileName": "ocean-waves.mp3",
|
||||||
"category": "mechanical",
|
"category": "nature",
|
||||||
"description": "Fan and heater sounds for consistent background noise",
|
"description": "Gentle waves crashing on the shore for relaxation",
|
||||||
"bundleName": "Mechanical"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"categories": [
|
|
||||||
{
|
|
||||||
"id": "ambient",
|
|
||||||
"name": "Ambient",
|
|
||||||
"description": "General ambient sounds",
|
|
||||||
"bundleName": "Ambient"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "nature",
|
|
||||||
"name": "Nature",
|
|
||||||
"description": "Natural environmental sounds",
|
|
||||||
"bundleName": "Nature"
|
"bundleName": "Nature"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "mechanical",
|
"id": "forest-ambience",
|
||||||
"name": "Mechanical",
|
"name": "Forest Ambience",
|
||||||
"description": "Mechanical and electronic sounds",
|
"fileName": "forest-ambience.mp3",
|
||||||
|
"category": "nature",
|
||||||
|
"description": "Calm forest sounds with birds and wind for nature lovers",
|
||||||
|
"bundleName": "Nature"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fan-noise",
|
||||||
|
"name": "Fan Heater",
|
||||||
|
"fileName": "fan-heater.mp3",
|
||||||
|
"category": "mechanical",
|
||||||
|
"description": "Fan and heater sounds for consistent background noise",
|
||||||
"bundleName": "Mechanical"
|
"bundleName": "Mechanical"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "air-conditioner",
|
||||||
|
"name": "Air Conditioner Hum",
|
||||||
|
"fileName": "air-conditioner-hum.mp3",
|
||||||
|
"category": "mechanical",
|
||||||
|
"description": "Steady hum of an air conditioner for focus or sleep",
|
||||||
|
"bundleName": "Mechanical"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ambient-pad",
|
||||||
|
"name": "Atmospheric Pad",
|
||||||
|
"fileName": "atmospheric-pad.mp3",
|
||||||
|
"category": "ambient",
|
||||||
|
"description": "Soothing atmospheric drone for meditation or focus",
|
||||||
|
"bundleName": "Ambient",
|
||||||
|
"sourceUrl": "https://pixabay.com/sound-effects/search/ambient%20drone/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "calm-pad",
|
||||||
|
"name": "Calm Ambient Pad",
|
||||||
|
"fileName": "calm-ambient-pad.mp3",
|
||||||
|
"category": "ambient",
|
||||||
|
"description": "Soft, warm ambient pad for deep relaxation",
|
||||||
|
"bundleName": "Ambient",
|
||||||
|
"sourceUrl": "https://pixabay.com/sound-effects/search/ambient%20pad/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dark-ambient",
|
||||||
|
"name": "Dark Ambient Atmosphere",
|
||||||
|
"fileName": "dark-ambient.mp3",
|
||||||
|
"category": "ambient",
|
||||||
|
"description": "A moody, atmospheric soundscape with deep tones, ideal for introspection or creative focus.",
|
||||||
|
"bundleName": "Ambient"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ethereal-ambient",
|
||||||
|
"name": "Ethereal Ambient Soundscape",
|
||||||
|
"fileName": "ethereal-ambient.mp3",
|
||||||
|
"category": "ambient",
|
||||||
|
"description": "A dreamy, ethereal sound with delicate tones, perfect for relaxation or spiritual practices.",
|
||||||
|
"bundleName": "Ambient"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ambient-waves",
|
||||||
|
"name": "Ambient Waves",
|
||||||
|
"fileName": "ambient-waves.mp3",
|
||||||
|
"category": "ambient",
|
||||||
|
"description": "A smooth, wave-like ambient sound, evoking a sense of calm flow. Ideal for meditation or focus.",
|
||||||
|
"bundleName": "Ambient"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "clock-ticking",
|
||||||
|
"name": "Clock Ticking Mechanism",
|
||||||
|
"fileName": "clock-ticking.mp3",
|
||||||
|
"category": "mechanical",
|
||||||
|
"description": "The rhythmic ticking of a clock, providing a consistent, hypnotic sound for focus or relaxation.",
|
||||||
|
"bundleName": "Mechanical"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "electric-fan",
|
||||||
|
"name": "Electric Fan Whirring",
|
||||||
|
"fileName": "electric-fan.mp3",
|
||||||
|
"category": "mechanical",
|
||||||
|
"description": "The steady whir of an electric fan, creating a monotonous sound for sleep or concentration.",
|
||||||
|
"bundleName": "Mechanical"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "engine-idling",
|
||||||
|
"name": "Engine Idling",
|
||||||
|
"fileName": "engine-idling.mp3",
|
||||||
|
"category": "mechanical",
|
||||||
|
"description": "A low, steady engine idle, offering a deep hum for background noise or relaxation.",
|
||||||
|
"bundleName": "Mechanical"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "crickets-night",
|
||||||
|
"name": "Crickets at Night",
|
||||||
|
"fileName": "crickets-night.mp3",
|
||||||
|
"category": "nature",
|
||||||
|
"description": "The rhythmic chirping of crickets under a night sky, perfect for calming sleep.",
|
||||||
|
"bundleName": "Nature"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "distant-thunderstorm",
|
||||||
|
"name": "Distant Thunderstorm",
|
||||||
|
"fileName": "distant-thunderstorm.mp3",
|
||||||
|
"category": "nature",
|
||||||
|
"description": "Soft thunder and rain in the distance, creating a soothing, stormy ambiance.",
|
||||||
|
"bundleName": "Nature"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|||||||
@ -56,8 +56,8 @@ class AlarmSoundService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get alarm sound categories
|
/// Get alarm sound categories
|
||||||
func getAlarmSoundCategories() -> [SoundCategory] {
|
func getAlarmSoundCategories() -> [TheNoiseClock.SoundCategory] {
|
||||||
return loadAlarmConfiguration().categories
|
return [.alarm]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get alarm sound settings
|
/// Get alarm sound settings
|
||||||
|
|||||||
@ -14,56 +14,56 @@ struct SoundCategoryView: View {
|
|||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
let sounds: [Sound]
|
let sounds: [Sound]
|
||||||
@Binding var selectedSound: Sound?
|
@Binding var selectedSound: Sound?
|
||||||
@State private var selectedCategory: String = "all"
|
@State private var selectedCategory: SoundCategory = .all
|
||||||
@State private var searchText: String = ""
|
@State private var searchText: String = ""
|
||||||
@State private var viewModel = SoundViewModel()
|
@State private var viewModel = SoundViewModel()
|
||||||
|
|
||||||
// MARK: - Computed Properties
|
// MARK: - Computed Properties
|
||||||
private var filteredSounds: [Sound] {
|
private var filteredSounds: [Sound] {
|
||||||
let nonAlarmSounds = sounds.filter { $0.category != "alarm" }
|
let nonAlarmSounds = sounds.filter { $0.category != SoundCategory.alarm.rawValue }
|
||||||
|
|
||||||
let categoryFiltered = selectedCategory == "all"
|
let categoryFiltered = selectedCategory == .all
|
||||||
? nonAlarmSounds
|
? nonAlarmSounds
|
||||||
: nonAlarmSounds.filter { $0.category == selectedCategory }
|
: nonAlarmSounds.filter { $0.category == selectedCategory.rawValue }
|
||||||
|
|
||||||
if searchText.isEmpty {
|
let searchFiltered = if searchText.isEmpty {
|
||||||
return categoryFiltered
|
categoryFiltered
|
||||||
} else {
|
} else {
|
||||||
return categoryFiltered.filter { sound in
|
categoryFiltered.filter { sound in
|
||||||
sound.name.localizedCaseInsensitiveContains(searchText) ||
|
sound.name.localizedCaseInsensitiveContains(searchText) ||
|
||||||
sound.description.localizedCaseInsensitiveContains(searchText)
|
sound.description.localizedCaseInsensitiveContains(searchText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort sounds alphabetically by name
|
||||||
|
return searchFiltered.sorted { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending }
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Helper Methods
|
// MARK: - Helper Methods
|
||||||
private func getCategoryCount(for category: String) -> Int {
|
private func getCategoryCount(for category: SoundCategory) -> Int {
|
||||||
let nonAlarmSounds = sounds.filter { $0.category != "alarm" }
|
let nonAlarmSounds = sounds.filter { $0.category != SoundCategory.alarm.rawValue }
|
||||||
|
|
||||||
if category == "all" {
|
if category == .all {
|
||||||
return nonAlarmSounds.count
|
return nonAlarmSounds.count
|
||||||
} else {
|
} else {
|
||||||
return nonAlarmSounds.filter { $0.category == category }.count
|
return nonAlarmSounds.filter { $0.category == category.rawValue }.count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var categories: [String] {
|
private var categories: [SoundCategory] {
|
||||||
let nonAlarmSounds = sounds.filter { $0.category != "alarm" }
|
let nonAlarmSounds = sounds.filter { $0.category != SoundCategory.alarm.rawValue }
|
||||||
let uniqueCategories = Set(nonAlarmSounds.map { $0.category })
|
let uniqueCategoryStrings = Set(nonAlarmSounds.map { $0.category })
|
||||||
return ["all"] + Array(uniqueCategories).sorted()
|
|
||||||
|
// Convert string categories to enum cases and filter out invalid ones
|
||||||
|
let validCategories = uniqueCategoryStrings.compactMap { SoundCategory(from: $0) }
|
||||||
|
|
||||||
|
// Always include "All" and filter other categories based on available sounds
|
||||||
|
let allCategories = [SoundCategory.all] + validCategories
|
||||||
|
|
||||||
|
// Return sorted categories using the enum's sort order
|
||||||
|
return SoundCategory.sortedNoiseCategories.filter { allCategories.contains($0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private var categoryDisplayName: (String) -> String {
|
|
||||||
return { category in
|
|
||||||
switch category {
|
|
||||||
case "all": return "All"
|
|
||||||
case "ambient": return "Ambient"
|
|
||||||
case "nature": return "Nature"
|
|
||||||
case "mechanical": return "Mechanical"
|
|
||||||
default: return category.capitalized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Body
|
// MARK: - Body
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@ -103,9 +103,9 @@ struct SoundCategoryView: View {
|
|||||||
private var categoryTabs: some View {
|
private var categoryTabs: some View {
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
HStack(spacing: UIConstants.Spacing.small) {
|
HStack(spacing: UIConstants.Spacing.small) {
|
||||||
ForEach(categories, id: \.self) { category in
|
ForEach(categories) { category in
|
||||||
CategoryTab(
|
CategoryTab(
|
||||||
title: categoryDisplayName(category),
|
title: category.displayName,
|
||||||
isSelected: selectedCategory == category,
|
isSelected: selectedCategory == category,
|
||||||
count: getCategoryCount(for: category)
|
count: getCategoryCount(for: category)
|
||||||
) {
|
) {
|
||||||
@ -252,16 +252,7 @@ struct SoundCard: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var soundIcon: String {
|
private var soundIcon: String {
|
||||||
switch sound.category {
|
return SoundCategory(from: sound.category)?.icon ?? "speaker.wave.2"
|
||||||
case "ambient":
|
|
||||||
return "waveform"
|
|
||||||
case "nature":
|
|
||||||
return "cloud.rain"
|
|
||||||
case "mechanical":
|
|
||||||
return "fan"
|
|
||||||
default:
|
|
||||||
return "speaker.wave.2"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user