Loading https://claude.ai as the start URL caused didFinish to fire on the landing page while stale/tracking cookies were already in WKWebsiteDataStore. The 'any claude.ai cookie' check then fired immediately, closing the login sheet before the user could sign in. Loading /login ensures the URL-guard catches the initial page load and only checks cookies after the real post-login redirect.
96 lines
3.2 KiB
Swift
96 lines
3.2 KiB
Swift
import SwiftUI
|
|
import WebKit
|
|
|
|
// MARK: - Login Web View
|
|
|
|
struct LoginWebView: NSViewRepresentable {
|
|
let onAuthenticated: () -> Void
|
|
|
|
func makeNSView(context: Context) -> WKWebView {
|
|
let config = WKWebViewConfiguration()
|
|
config.websiteDataStore = WKWebsiteDataStore.default()
|
|
|
|
let webView = WKWebView(frame: .zero, configuration: config)
|
|
webView.navigationDelegate = context.coordinator
|
|
webView.load(URLRequest(url: URL(string: "https://claude.ai/login")!))
|
|
return webView
|
|
}
|
|
|
|
func updateNSView(_ nsView: WKWebView, context: Context) {}
|
|
|
|
func makeCoordinator() -> Coordinator {
|
|
Coordinator(onAuthenticated: onAuthenticated)
|
|
}
|
|
|
|
class Coordinator: NSObject, WKNavigationDelegate {
|
|
let onAuthenticated: () -> Void
|
|
var didAuthenticate = false
|
|
|
|
init(onAuthenticated: @escaping () -> Void) {
|
|
self.onAuthenticated = onAuthenticated
|
|
}
|
|
|
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
guard !didAuthenticate else { return }
|
|
// Don't fire on the login/auth pages themselves
|
|
if let url = webView.url?.absoluteString,
|
|
url.contains("/login") || url.contains("/auth") { return }
|
|
|
|
// URL is not a login/auth page, so if any claude.ai cookie exists we're signed in
|
|
WKWebsiteDataStore.default().httpCookieStore.getAllCookies { cookies in
|
|
let hasAnyCookie = cookies.contains { $0.domain.contains("claude.ai") }
|
|
guard hasAnyCookie, !self.didAuthenticate else { return }
|
|
self.didAuthenticate = true
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
self.onAuthenticated()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Login Sheet View
|
|
|
|
struct LoginSheetView: View {
|
|
@Binding var isPresented: Bool
|
|
let onDone: () -> Void
|
|
@State private var authenticated = false
|
|
|
|
var body: some View {
|
|
VStack(spacing: 0) {
|
|
HStack {
|
|
Text(authenticated ? "✓ Signed in — loading data…" : "Sign in to Claude")
|
|
.font(.system(size: 13, weight: .semibold))
|
|
Spacer()
|
|
if authenticated {
|
|
Button("Done") {
|
|
isPresented = false
|
|
onDone()
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
.controlSize(.small)
|
|
} else {
|
|
Button("Cancel") { isPresented = false }
|
|
.buttonStyle(.bordered)
|
|
.controlSize(.small)
|
|
}
|
|
}
|
|
.padding(.horizontal, 14)
|
|
.padding(.vertical, 10)
|
|
.background(Color(nsColor: .windowBackgroundColor))
|
|
|
|
Divider()
|
|
|
|
LoginWebView {
|
|
authenticated = true
|
|
// Auto-dismiss and refresh after brief delay
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
|
|
isPresented = false
|
|
onDone()
|
|
}
|
|
}
|
|
}
|
|
.frame(width: 460, height: 560)
|
|
}
|
|
}
|