vOOice/VoiceInk/Views/Settings/CustomSoundSettingsView.swift

146 lines
5.0 KiB
Swift

import SwiftUI
import UniformTypeIdentifiers
struct CustomSoundSettingsView: View {
@StateObject private var customSoundManager = CustomSoundManager.shared
@State private var showingAlert = false
@State private var alertTitle = ""
@State private var alertMessage = ""
var body: some View {
VStack(alignment: .leading, spacing: 8) {
soundRow(for: .start)
soundRow(for: .stop)
}
.alert(alertTitle, isPresented: $showingAlert) {
Button("OK", role: .cancel) {}
} message: {
Text(alertMessage)
}
}
@ViewBuilder
private func soundRow(for type: CustomSoundManager.SoundType) -> some View {
horizontalSoundRow(
title: type.rawValue.capitalized,
fileName: customSoundManager.getSoundDisplayName(for: type),
isCustom: type == .start ? customSoundManager.isUsingCustomStartSound : customSoundManager.isUsingCustomStopSound,
onSelect: { selectSound(for: type) },
onTest: {
if type == .start {
SoundManager.shared.playStartSound()
} else {
SoundManager.shared.playStopSound()
}
},
onReset: { customSoundManager.resetSoundToDefault(for: type) }
)
}
@ViewBuilder
private func horizontalSoundRow(
title: String,
fileName: String?,
isCustom: Bool,
onSelect: @escaping () -> Void,
onTest: @escaping () -> Void,
onReset: @escaping () -> Void
) -> some View {
HStack(spacing: 12) {
Text(title)
.font(.system(size: 13, weight: .medium))
.foregroundColor(.secondary)
.frame(maxWidth: 40, alignment: .leading)
if let fileName = fileName, isCustom {
Text(fileName)
.font(.system(size: 12))
.foregroundColor(.secondary)
.lineLimit(1)
.truncationMode(.middle)
.frame(maxWidth: 160, alignment: .leading)
HStack(spacing: 8) {
Button(action: onTest) {
Image(systemName: "play.circle.fill")
.font(.system(size: 16))
.foregroundColor(.secondary)
}
.buttonStyle(.plain)
.help("Test sound")
Button(action: onSelect) {
Image(systemName: "folder")
.font(.system(size: 15))
.foregroundColor(.secondary)
}
.buttonStyle(.plain)
.help("Change sound")
Button(action: onReset) {
Image(systemName: "arrow.uturn.backward.circle")
.font(.system(size: 15))
.foregroundColor(.secondary)
}
.buttonStyle(.plain)
.help("Reset to default")
}
} else {
Text("Default")
.font(.system(size: 12))
.foregroundColor(.secondary)
.frame(maxWidth: 160, alignment: .leading)
HStack(spacing: 8) {
Button(action: onTest) {
Image(systemName: "play.circle.fill")
.font(.system(size: 16))
.foregroundColor(.secondary)
}
.buttonStyle(.plain)
.help("Test sound")
Button(action: onSelect) {
Image(systemName: "folder.badge.plus")
.font(.system(size: 15))
.foregroundColor(.secondary)
}
.buttonStyle(.plain)
.help("Choose custom sound")
}
}
}
}
private func selectSound(for type: CustomSoundManager.SoundType) {
let panel = NSOpenPanel()
panel.title = "Choose \(type.rawValue.capitalized) Sound"
panel.message = "Select an audio file"
panel.allowedContentTypes = [
UTType.audio,
UTType.mp3,
UTType.wav,
UTType.aiff
]
panel.allowsMultipleSelection = false
panel.canChooseDirectories = false
panel.begin { response in
guard response == .OK, let url = panel.url else { return }
let result = customSoundManager.setCustomSound(url: url, for: type)
if case .failure(let error) = result {
alertTitle = "Invalid Audio File"
alertMessage = error.localizedDescription
showingAlert = true
}
}
}
}
#Preview {
CustomSoundSettingsView()
.frame(width: 600)
.padding()
}