diff --git a/Sora/Views/MediaInfoView/CustomMatching/AnilistMatchPopupView.swift b/Sora/Views/MediaInfoView/CustomMatching/AnilistMatchPopupView.swift index 975736c..8e05e38 100644 --- a/Sora/Views/MediaInfoView/CustomMatching/AnilistMatchPopupView.swift +++ b/Sora/Views/MediaInfoView/CustomMatching/AnilistMatchPopupView.swift @@ -1,33 +1,32 @@ // -// AnilistMatchPopupView.swift -// Sulfur -// -// Created by seiike on 01/06/2025. +// AnilistMatchPopupView.swift +// Sulfur // +// Created by seiike on 01/06/2025. import NukeUI import SwiftUI struct AnilistMatchPopupView: View { let seriesTitle: String - let onSelect: (Int) -> Void - + let onSelect: (Int, String) -> Void + @State private var results: [[String: Any]] = [] @State private var isLoading = true - + @AppStorage("selectedAppearance") private var selectedAppearance: Appearance = .system @Environment(\.colorScheme) private var colorScheme - + private var isLightMode: Bool { selectedAppearance == .light - || (selectedAppearance == .system && colorScheme == .light) + || (selectedAppearance == .system && colorScheme == .light) } - + @State private var manualIDText: String = "" @State private var showingManualIDAlert = false - + @Environment(\.dismiss) private var dismiss - + var body: some View { NavigationView { ScrollView { @@ -36,7 +35,7 @@ struct AnilistMatchPopupView: View { .font(.footnote) .foregroundStyle(.gray) .padding(.horizontal, 10) - + VStack(spacing: 0) { if isLoading { ProgressView() @@ -52,10 +51,11 @@ struct AnilistMatchPopupView: View { LazyVStack(spacing: 15) { ForEach(results.indices, id: \.self) { index in let result = results[index] - Button(action: { if let id = result["id"] as? Int { - onSelect(id) + let title = result["title"] as? String ?? seriesTitle + onSelect(id, title) + dismiss() } }) { HStack(spacing: 12) { @@ -76,19 +76,18 @@ struct AnilistMatchPopupView: View { } } } - + VStack(alignment: .leading, spacing: 2) { Text(result["title"] as? String ?? "Unknown") .font(.body) .foregroundStyle(.primary) - if let english = result["title_english"] as? String { Text(english) .font(.caption) .foregroundStyle(.secondary) } } - + Spacer() } .padding(11) @@ -120,7 +119,7 @@ struct AnilistMatchPopupView: View { .padding(.top, 16) } } - + if !results.isEmpty { Text("Tap a title to override the current match.") .font(.footnote) @@ -135,38 +134,36 @@ struct AnilistMatchPopupView: View { .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { - Button("Cancel") { - dismiss() - } - .foregroundColor(isLightMode ? .black : .white) + Button("Cancel") { dismiss() } + .foregroundColor(isLightMode ? .black : .white) } ToolbarItem(placement: .navigationBarTrailing) { - Button(action: { + Button { manualIDText = "" showingManualIDAlert = true - }) { + } label: { Image(systemName: "number") .foregroundColor(isLightMode ? .black : .white) } } } - .alert("Set Custom AniList ID", isPresented: $showingManualIDAlert, actions: { + .alert("Set Custom AniList ID", isPresented: $showingManualIDAlert) { TextField("AniList ID", text: $manualIDText) .keyboardType(.numberPad) Button("Cancel", role: .cancel) { } - Button("Save", action: { + Button("Save") { if let idInt = Int(manualIDText.trimmingCharacters(in: .whitespaces)) { - onSelect(idInt) + onSelect(idInt, seriesTitle) dismiss() } - }) - }, message: { + } + } message: { Text("Enter the AniList ID for this series") - }) + } } .onAppear(perform: fetchMatches) } - + private func fetchMatches() { let query = """ query { @@ -184,35 +181,32 @@ struct AnilistMatchPopupView: View { } } """ - + guard let url = URL(string: "https://graphql.anilist.co") else { return } - var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.httpBody = try? JSONSerialization.data(withJSONObject: ["query": query]) - + URLSession.shared.dataTask(with: request) { data, _, _ in DispatchQueue.main.async { - self.isLoading = false - - guard let data = data, - let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], - let dataDict = json["data"] as? [String: Any], - let page = dataDict["Page"] as? [String: Any], - let mediaList = page["media"] as? [[String: Any]] else { - return - } - - self.results = mediaList.map { media in + isLoading = false + guard + let data = data, + let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + let dataDict = json["data"] as? [String: Any], + let page = dataDict["Page"] as? [String: Any], + let mediaList = page["media"] as? [[String: Any]] + else { return } + + results = mediaList.map { media in let titleInfo = media["title"] as? [String: Any] let cover = (media["coverImage"] as? [String: Any])?["large"] as? String - return [ "id": media["id"] ?? 0, "title": titleInfo?["romaji"] ?? "Unknown", - "title_english": titleInfo?["english"], - "cover": cover + "title_english": titleInfo?["english"] as Any, + "cover": cover as Any ] } } diff --git a/Sora/Views/MediaInfoView/CustomMatching/TMDBMatchPopupView.swift b/Sora/Views/MediaInfoView/CustomMatching/TMDBMatchPopupView.swift index 3293a2a..d34bc69 100644 --- a/Sora/Views/MediaInfoView/CustomMatching/TMDBMatchPopupView.swift +++ b/Sora/Views/MediaInfoView/CustomMatching/TMDBMatchPopupView.swift @@ -1,16 +1,15 @@ // -// TMDBMatchPopupView.swift -// Sulfur -// -// Created by seiike on 12/06/2025. +// TMDBMatchPopupView.swift +// Sulfur // +// Created by seiike on 12/06/2025. import SwiftUI import NukeUI struct TMDBMatchPopupView: View { let seriesTitle: String - let onSelect: (Int, TMDBFetcher.MediaType) -> Void + let onSelect: (Int, TMDBFetcher.MediaType, String) -> Void @State private var results: [ResultItem] = [] @State private var isLoading = true @@ -54,10 +53,10 @@ struct TMDBMatchPopupView: View { } else { LazyVStack(spacing: 15) { ForEach(results) { item in - Button(action: { - onSelect(item.id, item.mediaType) + Button { + onSelect(item.id, item.mediaType, item.title) dismiss() - }) { + } label: { HStack(spacing: 12) { if let poster = item.posterURL, let url = URL(string: poster) { LazyImage(url: url) { state in @@ -112,9 +111,7 @@ struct TMDBMatchPopupView: View { .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { - Button("Cancel") { - dismiss() - } + Button("Cancel") { dismiss() } } } .alert("Error Fetching Results", isPresented: $showingError) { @@ -129,7 +126,6 @@ struct TMDBMatchPopupView: View { private func fetchMatches() { isLoading = true results = [] - let fetcher = TMDBFetcher() let apiKey = fetcher.apiKey let dispatchGroup = DispatchGroup() @@ -148,9 +144,10 @@ struct TMDBMatchPopupView: View { URLSession.shared.dataTask(with: url) { data, _, error in defer { dispatchGroup.leave() } - - guard error == nil, let data = data, - let response = try? JSONDecoder().decode(TMDBSearchResponse.self, from: data) else { + guard error == nil, + let data = data, + let response = try? JSONDecoder().decode(TMDBSearchResponse.self, from: data) + else { encounteredError = true return } @@ -165,10 +162,7 @@ struct TMDBMatchPopupView: View { } dispatchGroup.notify(queue: .main) { - if encounteredError { - showingError = true - } - // Keep API order (by popularity), limit to top 6 overall + if encounteredError { showingError = true } results = Array(temp.prefix(6)) isLoading = false } diff --git a/Sora/Views/MediaInfoView/MediaInfoView.swift b/Sora/Views/MediaInfoView/MediaInfoView.swift index a4f5cb6..31d847d 100644 --- a/Sora/Views/MediaInfoView/MediaInfoView.swift +++ b/Sora/Views/MediaInfoView/MediaInfoView.swift @@ -655,14 +655,17 @@ struct MediaInfoView: View { .circularGradientOutline() } .sheet(isPresented: $isMatchingPresented) { - AnilistMatchPopupView(seriesTitle: title) { selectedID in - handleAniListMatch(selectedID: selectedID) + AnilistMatchPopupView(seriesTitle: title) { id, matched in + handleAniListMatch(selectedID: id) + matchedTitle = matched // ← now in scope fetchMetadataIDIfNeeded() } } .sheet(isPresented: $isTMDBMatchingPresented) { - TMDBMatchPopupView(seriesTitle: title) { id, type in - tmdbID = id; tmdbType = type + TMDBMatchPopupView(seriesTitle: title) { id, type, matched in + tmdbID = id + tmdbType = type + matchedTitle = matched // ← now in scope fetchMetadataIDIfNeeded() } } @@ -671,18 +674,12 @@ struct MediaInfoView: View { @ViewBuilder private var menuContent: some View { Group { - if let active = activeProvider { - Text("Provider: \(active)") - .font(.caption) - .foregroundColor(.gray) - .padding(.vertical, 4) - Divider() + if let provider = activeProvider { + Text("Matched \(provider): \(matchedTitle ?? title)") + .font(.caption2) + .foregroundColor(.secondary) } - Text("Matched ID: \(itemID ?? 0)") - .font(.caption2) - .foregroundColor(.secondary) - if activeProvider == "AniList" { Button("Match with AniList") { isMatchingPresented = true