| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- import AppKit
- import WebKit
- @MainActor
- final class RedditOAuthPanelController: NSObject {
- static let shared = RedditOAuthPanelController()
- private var panel: NSPanel?
- private var webView: WKWebView?
- private var onComplete: (() -> Void)?
- private override init() {
- super.init()
- }
- func completeSignIn(
- callbackURL: URL,
- onFinished: @escaping () -> Void
- ) {
- dismiss()
- onComplete = onFinished
- let panel = NSPanel(
- contentRect: NSRect(x: 0, y: 0, width: 520, height: 640),
- styleMask: [.titled, .closable, .fullSizeContentView],
- backing: .buffered,
- defer: false
- )
- panel.title = "Sign in to Reddit"
- panel.isReleasedWhenClosed = false
- panel.center()
- panel.level = .floating
- let webView = WKWebView(frame: panel.contentView?.bounds ?? .zero, configuration: RedditWebViewManager.makeConfiguration())
- webView.navigationDelegate = self
- webView.autoresizingMask = [.width, .height]
- panel.contentView?.addSubview(webView)
- self.panel = panel
- self.webView = webView
- panel.makeKeyAndOrderFront(nil)
- NSApp.activate(ignoringOtherApps: true)
- webView.load(URLRequest(url: callbackURL))
- }
- func dismiss() {
- panel?.close()
- panel = nil
- webView = nil
- onComplete = nil
- }
- private func finish() {
- let completion = onComplete
- dismiss()
- completion?()
- }
- }
- extension RedditOAuthPanelController: WKNavigationDelegate {
- func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
- guard let url = webView.url, RedditWebAuthHelper.isRedditURL(url) else { return }
- if shouldFinish(for: url) {
- finish()
- return
- }
- if url.host?.lowercased() == "accounts.reddit.com" {
- DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in
- guard let self, self.panel != nil else { return }
- self.finish()
- }
- }
- }
- func webView(
- _ webView: WKWebView,
- decidePolicyFor navigationAction: WKNavigationAction,
- decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
- ) {
- guard let url = navigationAction.request.url else {
- decisionHandler(.allow)
- return
- }
- if RedditWebAuthHelper.isOAuthProviderURL(url) {
- decisionHandler(.cancel)
- OAuthAuthenticationManager.shared.authenticate(
- url: url,
- presentationWindow: panel
- ) { [weak self] result in
- guard let self else { return }
- switch result {
- case .success(let callbackURL):
- webView.load(URLRequest(url: callbackURL))
- case .failure:
- self.finish()
- }
- }
- return
- }
- if shouldFinish(for: url) {
- decisionHandler(.allow)
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
- self?.finish()
- }
- return
- }
- decisionHandler(.allow)
- }
- private func shouldFinish(for url: URL) -> Bool {
- guard let host = url.host?.lowercased() else { return false }
- let path = url.path.lowercased()
- if host == "www.reddit.com" || host == "reddit.com" {
- return !path.contains("login") && !path.contains("register")
- }
- return RedditWebAuthHelper.looksLikeLoggedInURL(url)
- }
- }
|