Archived
chore: scaffold Voice iOS app (Phase 0)
XcodeGen project, SwiftUI iOS 17 target dev.shane.voice, SPM livekit-client-swift 2.16.0, mic usage + ATS LAN exception in Info.plist, placeholder conversation screen and Settings with server URL field.
This commit is contained in:
commit
297fbaa19c
17 files changed
+1056
No files matched your search
@@ -0,0 +1,41 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
final class SettingsStore: ObservableObject {
|
||||
@Published var serverURL: String {
|
||||
didSet { UserDefaults.standard.set(serverURL, forKey: Keys.serverURL) }
|
||||
}
|
||||
@Published var voiceID: String {
|
||||
didSet { UserDefaults.standard.set(voiceID, forKey: Keys.voiceID) }
|
||||
}
|
||||
@Published var modelID: String? {
|
||||
didSet { UserDefaults.standard.set(modelID, forKey: Keys.modelID) }
|
||||
}
|
||||
@Published var textMode: Bool {
|
||||
didSet { UserDefaults.standard.set(textMode, forKey: Keys.textMode) }
|
||||
}
|
||||
@Published var volume: Double {
|
||||
didSet { UserDefaults.standard.set(volume, forKey: Keys.volume) }
|
||||
}
|
||||
|
||||
private enum Keys {
|
||||
static let serverURL = "serverURL"
|
||||
static let voiceID = "voiceID"
|
||||
static let modelID = "modelID"
|
||||
static let textMode = "textMode"
|
||||
static let volume = "volume"
|
||||
}
|
||||
|
||||
init() {
|
||||
let d = UserDefaults.standard
|
||||
serverURL = d.string(forKey: Keys.serverURL) ?? "https://192.168.86.2:9178"
|
||||
voiceID = d.string(forKey: Keys.voiceID) ?? ""
|
||||
modelID = d.string(forKey: Keys.modelID)
|
||||
textMode = d.bool(forKey: Keys.textMode)
|
||||
volume = d.object(forKey: Keys.volume) as? Double ?? 0.8
|
||||
}
|
||||
|
||||
var trimmedServerURL: String {
|
||||
serverURL.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct VoiceApp: App {
|
||||
@StateObject private var settings = SettingsStore()
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ConversationView(settings: settings)
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ConversationView: View {
|
||||
@ObservedObject var settings: SettingsStore
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
VStack(spacing: 24) {
|
||||
Spacer()
|
||||
Circle()
|
||||
.fill(.blue.gradient)
|
||||
.frame(width: 96, height: 96)
|
||||
Text("Hope Voice")
|
||||
.font(.title.bold())
|
||||
Text(settings.trimmedServerURL)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
Spacer()
|
||||
}
|
||||
.navigationTitle("Voice")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
NavigationLink {
|
||||
SettingsView(settings: settings)
|
||||
} label: {
|
||||
Image(systemName: "gearshape")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ConversationView(settings: SettingsStore())
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsView: View {
|
||||
@ObservedObject var settings: SettingsStore
|
||||
@State private var urlText: String = ""
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section("Server") {
|
||||
TextField("https://192.168.86.2:9178", text: $urlText)
|
||||
.keyboardType(.URL)
|
||||
.autocorrectionDisabled()
|
||||
.onAppear { urlText = settings.serverURL }
|
||||
.onChange(of: urlText) { _, newValue in
|
||||
settings.serverURL = newValue
|
||||
}
|
||||
}
|
||||
Section("Interaction") {
|
||||
Toggle("Text mode", isOn: $settings.textMode)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Settings")
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
SettingsView(settings: SettingsStore())
|
||||
}
|
||||
Reference in new issue
Block a user