Initial Commit
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
//
|
||||
// LoginView.swift
|
||||
// Mobile Music Assistant
|
||||
//
|
||||
// Created by Sven Hanold on 26.03.26.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct LoginView: View {
|
||||
@Environment(MAService.self) private var service
|
||||
|
||||
@State private var serverURL = "https://"
|
||||
@State private var token = ""
|
||||
@State private var showToken = false
|
||||
|
||||
@State private var isLoading = false
|
||||
@State private var errorMessage: String?
|
||||
@State private var showError = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
// Server URL Section
|
||||
Section {
|
||||
TextField("Server URL", text: $serverURL)
|
||||
.textContentType(.URL)
|
||||
.keyboardType(.URL)
|
||||
.autocapitalization(.none)
|
||||
.autocorrectionDisabled()
|
||||
} header: {
|
||||
Text("Server")
|
||||
} footer: {
|
||||
Text("Enter your Music Assistant server URL (e.g., https://musicassistant-app.hanold.online)")
|
||||
}
|
||||
|
||||
// Token Section
|
||||
Section {
|
||||
HStack {
|
||||
Group {
|
||||
if showToken {
|
||||
TextField("Long-Lived Access Token", text: $token)
|
||||
.textContentType(.password)
|
||||
.autocapitalization(.none)
|
||||
.autocorrectionDisabled()
|
||||
} else {
|
||||
SecureField("Long-Lived Access Token", text: $token)
|
||||
.textContentType(.password)
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
showToken.toggle()
|
||||
} label: {
|
||||
Image(systemName: showToken ? "eye.slash.fill" : "eye.fill")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
} header: {
|
||||
Text("Authentication")
|
||||
} footer: {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("How to get a token:")
|
||||
Text("1. Open Music Assistant in a browser")
|
||||
Text("2. Go to Settings → Users")
|
||||
Text("3. Create a new long-lived access token")
|
||||
Text("4. Copy and paste the token here")
|
||||
}
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
// Connect Button
|
||||
Section {
|
||||
Button {
|
||||
Task {
|
||||
await login()
|
||||
}
|
||||
} label: {
|
||||
if isLoading {
|
||||
HStack {
|
||||
Spacer()
|
||||
ProgressView()
|
||||
Text("Connecting...")
|
||||
.padding(.leading, 8)
|
||||
Spacer()
|
||||
}
|
||||
} else {
|
||||
HStack {
|
||||
Spacer()
|
||||
Text("Connect")
|
||||
.fontWeight(.semibold)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
.disabled(isLoading || !isFormValid)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Music Assistant")
|
||||
.alert("Connection Error", isPresented: $showError) {
|
||||
Button("OK", role: .cancel) { }
|
||||
} message: {
|
||||
if let errorMessage {
|
||||
Text(errorMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Computed Properties
|
||||
|
||||
private var isFormValid: Bool {
|
||||
!serverURL.isEmpty &&
|
||||
serverURL.starts(with: "http") &&
|
||||
!token.isEmpty
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
private func login() async {
|
||||
guard let url = URL(string: serverURL) else {
|
||||
showError(message: "Invalid server URL")
|
||||
return
|
||||
}
|
||||
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
|
||||
print("🔵 LoginView: Starting login with long-lived token")
|
||||
print("🔵 LoginView: Server URL = \(url.absoluteString)")
|
||||
print("🔵 LoginView: Token length = \(token.count)")
|
||||
|
||||
do {
|
||||
// Save token to keychain
|
||||
try service.authManager.saveToken(serverURL: url, token: token)
|
||||
print("✅ LoginView: Token saved to keychain")
|
||||
|
||||
// Connect WebSocket with token
|
||||
print("🔵 LoginView: Connecting WebSocket")
|
||||
try await service.connect(serverURL: url, token: token)
|
||||
print("✅ LoginView: Connected successfully")
|
||||
|
||||
isLoading = false
|
||||
} catch {
|
||||
print("❌ LoginView: Login failed - \(error)")
|
||||
isLoading = false
|
||||
showError(message: error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
private func showError(message: String) {
|
||||
errorMessage = message
|
||||
showError = true
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
LoginView()
|
||||
.environment(MAService())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user