From 5fef2f15b8eca9c3a08e6b0431d3130df3412ecc Mon Sep 17 00:00:00 2001 From: Francesco <100066266+cranci1@users.noreply.github.com> Date: Tue, 8 Apr 2025 20:33:16 +0200 Subject: [PATCH] =?UTF-8?q?better=20=F0=9F=92=B4=20=F0=9F=92=B4?= =?UTF-8?q?=F0=9F=92=B4=F0=9F=92=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AniList/Auth/Anilist-Token.swift | 68 ++++++++++++------- .../Mutations/AniListPushUpdates.swift | 5 ++ .../CustomPlayer/CustomPlayer.swift | 1 - .../SettingsViewTrackers.swift | 35 +++++++++- Sora/Views/SettingsView/SettingsView.swift | 6 +- 5 files changed, 85 insertions(+), 30 deletions(-) diff --git a/Sora/Tracking Services/AniList/Auth/Anilist-Token.swift b/Sora/Tracking Services/AniList/Auth/Anilist-Token.swift index 68bc232..883e0e9 100644 --- a/Sora/Tracking Services/AniList/Auth/Anilist-Token.swift +++ b/Sora/Tracking Services/AniList/Auth/Anilist-Token.swift @@ -17,6 +17,9 @@ class AniListToken { static let serviceName = "me.cranci.sora.AniListToken" static let accountName = "AniListAccessToken" + static let authSuccessNotification = Notification.Name("AniListAuthenticationSuccess") + static let authFailureNotification = Notification.Name("AniListAuthenticationFailure") + static func saveTokenToKeychain(token: String) -> Bool { let tokenData = token.data(using: .utf8)! @@ -43,7 +46,10 @@ class AniListToken { guard let url = URL(string: tokenEndpoint) else { Logger.shared.log("Invalid token endpoint URL", type: "Error") - completion(false) + DispatchQueue.main.async { + NotificationCenter.default.post(name: authFailureNotification, object: nil, userInfo: ["error": "Invalid token endpoint URL"]) + completion(false) + } return } @@ -55,31 +61,43 @@ class AniListToken { request.httpBody = bodyString.data(using: .utf8) let task = URLSession.shared.dataTask(with: request) { data, response, error in - if let error = error { - Logger.shared.log("Error: \(error.localizedDescription)", type: "Error") - completion(false) - return - } - - guard let data = data else { - Logger.shared.log("No data received", type: "Error") - completion(false) - return - } - - do { - if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] { - if let accessToken = json["access_token"] as? String { - let success = saveTokenToKeychain(token: accessToken) - completion(success) - } else { - Logger.shared.log("Unexpected response: \(json)", type: "Error") - completion(false) - } + DispatchQueue.main.async { + if let error = error { + Logger.shared.log("Error: \(error.localizedDescription)", type: "Error") + NotificationCenter.default.post(name: authFailureNotification, object: nil, userInfo: ["error": error.localizedDescription]) + completion(false) + return + } + + guard let data = data else { + Logger.shared.log("No data received", type: "Error") + NotificationCenter.default.post(name: authFailureNotification, object: nil, userInfo: ["error": "No data received"]) + completion(false) + return + } + + do { + if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] { + if let accessToken = json["access_token"] as? String { + let success = saveTokenToKeychain(token: accessToken) + if success { + NotificationCenter.default.post(name: authSuccessNotification, object: nil) + } else { + NotificationCenter.default.post(name: authFailureNotification, object: nil, userInfo: ["error": "Failed to save token to keychain"]) + } + completion(success) + } else { + let errorMessage = (json["error"] as? String) ?? "Unexpected response" + Logger.shared.log("Authentication error: \(errorMessage)", type: "Error") + NotificationCenter.default.post(name: authFailureNotification, object: nil, userInfo: ["error": errorMessage]) + completion(false) + } + } + } catch { + Logger.shared.log("Failed to parse JSON: \(error.localizedDescription)", type: "Error") + NotificationCenter.default.post(name: authFailureNotification, object: nil, userInfo: ["error": "Failed to parse response: \(error.localizedDescription)"]) + completion(false) } - } catch { - Logger.shared.log("Failed to parse JSON: \(error.localizedDescription)", type: "Error") - completion(false) } } diff --git a/Sora/Tracking Services/AniList/Mutations/AniListPushUpdates.swift b/Sora/Tracking Services/AniList/Mutations/AniListPushUpdates.swift index 69db061..ade8898 100644 --- a/Sora/Tracking Services/AniList/Mutations/AniListPushUpdates.swift +++ b/Sora/Tracking Services/AniList/Mutations/AniListPushUpdates.swift @@ -34,6 +34,11 @@ class AniListMutation { } func updateAnimeProgress(animeId: Int, episodeNumber: Int, completion: @escaping (Result) -> Void) { + if let sendPushUpdates = UserDefaults.standard.object(forKey: "sendPushUpdates") as? Bool, + sendPushUpdates == false { + return + } + guard let userToken = getTokenFromKeychain() else { completion(.failure(NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "Access token not found"]))) return diff --git a/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift b/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift index 44a7eb8..4580c3e 100644 --- a/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift +++ b/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift @@ -1640,7 +1640,6 @@ class CustomMediaPlayerViewController: UIViewController { guard self != nil else { return } if player.timeControlStatus == .paused, let reason = player.reasonForWaitingToPlay { - // If we are paused for a “stall/minimize stalls” reason, forcibly resume: Logger.shared.log("Paused reason: \(reason)", type: "Error") if reason == .toMinimizeStalls || reason == .evaluatingBufferingRate { player.play() diff --git a/Sora/Views/SettingsView/SettingsSubViews/SettingsViewTrackers.swift b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewTrackers.swift index 6727051..18ca0c2 100644 --- a/Sora/Views/SettingsView/SettingsSubViews/SettingsViewTrackers.swift +++ b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewTrackers.swift @@ -10,6 +10,8 @@ import Security import Kingfisher struct SettingsViewTrackers: View { + @AppStorage("sendPushUpdates") private var isSendPushUpdates = true + @State private var status: String = "You are not logged in" @State private var isLoggedIn: Bool = false @State private var username: String = "" @@ -18,7 +20,7 @@ struct SettingsViewTrackers: View { var body: some View { Form { - Section(header: Text("AniList"), footer: Text("Sora and cranci1 are not affiliated with AniList in any way.")) { + Section(header: Text("AniList"), footer: Text("Sora and cranci1 are not affiliated with AniList in any way.\n\nNote that push updates may not be 100% acurate.")) { HStack() { KFImage(URL(string: "https://raw.githubusercontent.com/cranci1/Ryu/2f10226aa087154974a70c1ec78aa83a47daced9/Ryu/Assets.xcassets/Listing/Anilist.imageset/anilist.png")) .placeholder { @@ -50,6 +52,10 @@ struct SettingsViewTrackers: View { .multilineTextAlignment(.center) } } + if isLoggedIn { + Toggle("Send push updates", isOn: $isSendPushUpdates) + .tint(.accentColor) + } Button(isLoggedIn ? "Log Out from AniList.co" : "Log In with AniList.co") { if isLoggedIn { logout() @@ -63,11 +69,38 @@ struct SettingsViewTrackers: View { .navigationTitle("Trackers") .onAppear { updateStatus() + setupNotificationObservers() } + .onDisappear { + removeNotificationObservers() + } + } + + func setupNotificationObservers() { + NotificationCenter.default.addObserver(forName: AniListToken.authSuccessNotification, object: nil, queue: .main) { _ in + self.status = "Authentication successful!" + self.updateStatus() + } + + NotificationCenter.default.addObserver(forName: AniListToken.authFailureNotification, object: nil, queue: .main) { notification in + if let error = notification.userInfo?["error"] as? String { + self.status = "Login failed: \(error)" + } else { + self.status = "Login failed with unknown error" + } + self.isLoggedIn = false + self.isLoading = false + } + } + + func removeNotificationObservers() { + NotificationCenter.default.removeObserver(self, name: AniListToken.authSuccessNotification, object: nil) + NotificationCenter.default.removeObserver(self, name: AniListToken.authFailureNotification, object: nil) } func login() { status = "Starting authentication..." + isLoading = true AniListLogin.authenticate() } diff --git a/Sora/Views/SettingsView/SettingsView.swift b/Sora/Views/SettingsView/SettingsView.swift index eacb628..27d255c 100644 --- a/Sora/Views/SettingsView/SettingsView.swift +++ b/Sora/Views/SettingsView/SettingsView.swift @@ -21,9 +21,9 @@ struct SettingsView: View { NavigationLink(destination: SettingsViewModule()) { Text("Modules") } - //NavigationLink(destination: SettingsViewTrackers()) { - // Text("Trackers") - //} + NavigationLink(destination: SettingsViewTrackers()) { + Text("Trackers") + } } Section(header: Text("Info")) {