feat: authorize iOS Hermes accounts and secure session isolation
This commit is contained in:
1 parent
9bdaea2408
commit
bebaf59466
23 files changed
+911
-185
No files matched your search
@@ -2,6 +2,7 @@ import SwiftUI
|
||||
|
||||
struct ConnectionTestView: View {
|
||||
@ObservedObject var settings: SettingsStore
|
||||
var bearerToken: String = ""
|
||||
@State private var isBusy = false
|
||||
@State private var result: Result<TokenResponse, Error>?
|
||||
@State private var urlError: String?
|
||||
@@ -67,7 +68,7 @@ struct ConnectionTestView: View {
|
||||
case .success(let config):
|
||||
isBusy = true
|
||||
Task {
|
||||
let client = TokenClient(config: config)
|
||||
let client = TokenClient(config: config, bearerToken: bearerToken)
|
||||
do {
|
||||
let response = try await client.fetchToken()
|
||||
result = .success(response)
|
||||
|
||||
@@ -13,7 +13,6 @@ struct SettingsView: View {
|
||||
@State private var urlError: String?
|
||||
@State private var showVoicePicker = false
|
||||
@State private var savedURLConfirmation: String?
|
||||
@State private var showModelPicker = false
|
||||
@FocusState private var urlFocused: Bool
|
||||
init(settings: SettingsStore, session: VoiceSession) {
|
||||
self.settings = settings
|
||||
@@ -46,19 +45,6 @@ struct SettingsView: View {
|
||||
} footer: {
|
||||
Text("Voice and typing preferences are saved on this iPhone. You can switch modes from the Talk tab.")
|
||||
}
|
||||
Section {
|
||||
if session.isReady, !session.models.isEmpty {
|
||||
Button {
|
||||
showModelPicker = true
|
||||
} label: {
|
||||
LabeledContent("Model", value: session.selectedModel ?? settings.modelID ?? "Server default")
|
||||
}
|
||||
} else {
|
||||
LabeledContent("Model", value: settings.modelID ?? "Server default")
|
||||
Text("Connect from Talk to see the models available on your server.")
|
||||
.font(.footnote).foregroundStyle(Nearlight.quiet)
|
||||
}
|
||||
} header: { Text("Intelligence") }
|
||||
Section {
|
||||
Text("Server URL")
|
||||
.font(.footnote.weight(.semibold))
|
||||
@@ -106,7 +92,7 @@ struct SettingsView: View {
|
||||
.foregroundStyle(Nearlight.quiet)
|
||||
}
|
||||
NavigationLink("Connection test") {
|
||||
ConnectionTestView(settings: settings)
|
||||
ConnectionTestView(settings: settings, bearerToken: session.bootstrapBearerToken)
|
||||
}
|
||||
} header: {
|
||||
Text("Connection")
|
||||
@@ -120,9 +106,7 @@ struct SettingsView: View {
|
||||
.sheet(isPresented: $showVoicePicker) {
|
||||
VoicePickerView(settings: settings, session: session)
|
||||
}
|
||||
.sheet(isPresented: $showModelPicker) {
|
||||
ModelPickerView(settings: settings, session: session)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Validates the draft and persists it only on success. On failure the
|
||||
@@ -190,51 +174,6 @@ struct VoicePickerView: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct ModelPickerView: View {
|
||||
@ObservedObject var settings: SettingsStore
|
||||
@ObservedObject var session: VoiceSession
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
private var activeModelID: String? {
|
||||
if !session.models.isEmpty,
|
||||
let selected = session.selectedModel ?? settings.modelID,
|
||||
session.models.contains(where: { $0.id == selected }) {
|
||||
return selected
|
||||
}
|
||||
return session.models.first?.id
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List {
|
||||
ForEach(session.models, id: \.id) { model in
|
||||
Button {
|
||||
settings.modelID = model.id
|
||||
Task { await session.selectModel(model.id) }
|
||||
dismiss()
|
||||
} label: {
|
||||
HStack {
|
||||
Text(model.name ?? model.id)
|
||||
if activeModelID == model.id {
|
||||
Spacer()
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundStyle(Nearlight.rosewood)
|
||||
}
|
||||
}
|
||||
}
|
||||
.accessibilityAddTraits(activeModelID == model.id ? .isSelected : [])
|
||||
}
|
||||
}
|
||||
.navigationTitle("Model")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") { dismiss() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
let previewSettings = SettingsStore()
|
||||
guard case .success(let previewConfig) = ServerConfig.parse(SettingsStore.defaultServerURL) else {
|
||||
|
||||
Reference in new issue
Block a user