From c6f91f16f85a69267776348fed4122fbdc17c0fd Mon Sep 17 00:00:00 2001 From: cranci1 <100066266+cranci1@users.noreply.github.com> Date: Sat, 1 Feb 2025 17:41:21 +0100 Subject: [PATCH] added yes --- Sora.xcodeproj/project.pbxproj | 4 + .../Modules/ModuleAdditionSettingsView.swift | 187 ++++++++++++++++++ Sora/Utils/Modules/Modules.swift | 7 +- .../SettingsSubViews/SettingsViewModule.swift | 43 ++-- Sora/Views/SettingsView/SettingsView.swift | 1 - 5 files changed, 215 insertions(+), 27 deletions(-) create mode 100644 Sora/Utils/Modules/ModuleAdditionSettingsView.swift diff --git a/Sora.xcodeproj/project.pbxproj b/Sora.xcodeproj/project.pbxproj index d2c5d28..732ea53 100644 --- a/Sora.xcodeproj/project.pbxproj +++ b/Sora.xcodeproj/project.pbxproj @@ -31,6 +31,7 @@ 1399FAD62D3AB3DB00E97C31 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1399FAD52D3AB3DB00E97C31 /* Logger.swift */; }; 13D842522D4523B800EBBFA6 /* Drops in Frameworks */ = {isa = PBXBuildFile; productRef = 13D842512D4523B800EBBFA6 /* Drops */; }; 13D842552D45267500EBBFA6 /* DropManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13D842542D45267500EBBFA6 /* DropManager.swift */; }; + 13D99CF72D4E73C300250A86 /* ModuleAdditionSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13D99CF62D4E73C300250A86 /* ModuleAdditionSettingsView.swift */; }; 13DC0C462D302C7500D0F966 /* VideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DC0C452D302C7500D0F966 /* VideoPlayer.swift */; }; 13EA2BD52D32D97400C1EBD7 /* CustomPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD12D32D97400C1EBD7 /* CustomPlayer.swift */; }; 13EA2BD62D32D97400C1EBD7 /* Double+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD32D32D97400C1EBD7 /* Double+Extension.swift */; }; @@ -65,6 +66,7 @@ 1399FAD32D3AB38C00E97C31 /* SettingsViewLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewLogger.swift; sourceTree = ""; }; 1399FAD52D3AB3DB00E97C31 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; 13D842542D45267500EBBFA6 /* DropManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropManager.swift; sourceTree = ""; }; + 13D99CF62D4E73C300250A86 /* ModuleAdditionSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleAdditionSettingsView.swift; sourceTree = ""; }; 13DC0C412D2EC9BA00D0F966 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 13DC0C452D302C7500D0F966 /* VideoPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayer.swift; sourceTree = ""; }; 13EA2BD12D32D97400C1EBD7 /* CustomPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomPlayer.swift; sourceTree = ""; }; @@ -186,6 +188,7 @@ 133D7C882D2BE2640075467E /* Modules */ = { isa = PBXGroup; children = ( + 13D99CF62D4E73C300250A86 /* ModuleAdditionSettingsView.swift */, 139935652D468C450065CEFF /* ModuleManager.swift */, 133D7C892D2BE2640075467E /* Modules.swift */, ); @@ -373,6 +376,7 @@ 13EA2BD92D32D98400C1EBD7 /* NormalPlayer.swift in Sources */, 133D7C932D2BE2640075467E /* Modules.swift in Sources */, 133D7C702D2BE2500075467E /* ContentView.swift in Sources */, + 13D99CF72D4E73C300250A86 /* ModuleAdditionSettingsView.swift in Sources */, 13EA2BD62D32D97400C1EBD7 /* Double+Extension.swift in Sources */, 133D7C8F2D2BE2640075467E /* MediaInfoView.swift in Sources */, 131845F92D47C62D00CA7A54 /* SettingsViewGeneral.swift in Sources */, diff --git a/Sora/Utils/Modules/ModuleAdditionSettingsView.swift b/Sora/Utils/Modules/ModuleAdditionSettingsView.swift new file mode 100644 index 0000000..e5197e1 --- /dev/null +++ b/Sora/Utils/Modules/ModuleAdditionSettingsView.swift @@ -0,0 +1,187 @@ +// +// ModuleAdditionSettingsView.swift +// Sora +// +// Created by Francesco on 01/02/25. +// + +import SwiftUI +import Kingfisher + +struct ModuleAdditionSettingsView: View { + @Environment(\.presentationMode) var presentationMode + @EnvironmentObject var moduleManager: ModuleManager + + @State private var moduleMetadata: ModuleMetadata? + @State private var isLoading = false + @State private var errorMessage: String? + var moduleUrl: String + + var body: some View { + ScrollView { + VStack { + if let metadata = moduleMetadata { + VStack(spacing: 25) { + VStack(spacing: 15) { + KFImage(URL(string: metadata.iconUrl)) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 120, height: 120) + .clipShape(Circle()) + .shadow(radius: 5) + .transition(.scale) + + Text(metadata.sourceName) + .font(.system(size: 28, weight: .bold)) + .multilineTextAlignment(.center) + } + .padding(.top) + + Divider() + + HStack(spacing: 15) { + KFImage(URL(string: metadata.author.icon)) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 60, height: 60) + .clipShape(Circle()) + .shadow(radius: 3) + + VStack(alignment: .leading, spacing: 4) { + Text(metadata.author.name) + .font(.headline) + Text("Author") + .font(.subheadline) + .foregroundColor(.secondary) + } + Spacer() + } + .padding(.horizontal) + + Divider() + + VStack(alignment: .leading, spacing: 12) { + InfoRow(title: "Version", value: metadata.version) + InfoRow(title: "Language", value: metadata.language) + InfoRow(title: "Base URL", value: metadata.baseUrl) + InfoRow(title: "Script URL", value: metadata.scriptUrl) + } + .padding(.horizontal) + } + + Divider() + + Button(action: addModule) { + HStack { + Image(systemName: "plus.circle.fill") + Text("Add Module") + } + .font(.headline) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .padding() + .background( + RoundedRectangle(cornerRadius: 15) + .fill(Color.accentColor) + ) + .padding(.horizontal) + } + .disabled(isLoading) + .opacity(isLoading ? 0.6 : 1) + + } else if isLoading { + VStack(spacing: 20) { + ProgressView() + .scaleEffect(1.5) + Text("Loading module information...") + .foregroundColor(.secondary) + } + .frame(maxHeight: .infinity) + .padding(.top, 100) + } else if let errorMessage = errorMessage { + VStack(spacing: 20) { + Image(systemName: "exclamationmark.triangle.fill") + .font(.system(size: 50)) + .foregroundColor(.red) + Text(errorMessage) + .foregroundColor(.red) + .multilineTextAlignment(.center) + } + .frame(maxHeight: .infinity) + .padding(.top, 100) + } + } + } + .navigationTitle("Add Module") + .onAppear(perform: fetchModuleMetadata) + } + + private func fetchModuleMetadata() { + isLoading = true + errorMessage = nil + + Task { + do { + guard let url = URL(string: moduleUrl) else { + DispatchQueue.main.async { + print(moduleUrl) + self.errorMessage = "Invalid URL" + self.isLoading = false + } + return + } + let (data, _) = try await URLSession.custom.data(from: url) + let metadata = try JSONDecoder().decode(ModuleMetadata.self, from: data) + DispatchQueue.main.async { + self.moduleMetadata = metadata + self.isLoading = false + } + } catch { + DispatchQueue.main.async { + self.errorMessage = "Failed to fetch module: \(error.localizedDescription)" + self.isLoading = false + } + } + } + } + + private func addModule() { + isLoading = true + Task { + do { + let _ = try await moduleManager.addModule(metadataUrl: moduleUrl) + DispatchQueue.main.async { + isLoading = false + DropManager.shared.showDrop(title: "Module Added", subtitle: "clicking it to select it", duration: 2.0, icon: UIImage(systemName:"app.badge.checkmark")) + self.presentationMode.wrappedValue.dismiss() + } + } catch { + DispatchQueue.main.async { + isLoading = false + if (error as NSError).domain == "Module already exists" { + errorMessage = "Module already exists" + } else { + errorMessage = "Failed to add module: \(error.localizedDescription)" + } + Logger.shared.log("Failed to add module: \(error.localizedDescription)") + } + } + } + } +} + +struct InfoRow: View { + let title: String + let value: String + + var body: some View { + VStack(alignment: .leading, spacing: 4) { + Text(title) + .font(.subheadline) + .foregroundColor(.secondary) + Text(value) + .font(.body) + .lineLimit(1) + } + } +} diff --git a/Sora/Utils/Modules/Modules.swift b/Sora/Utils/Modules/Modules.swift index b3e894e..411e0dd 100644 --- a/Sora/Utils/Modules/Modules.swift +++ b/Sora/Utils/Modules/Modules.swift @@ -9,7 +9,7 @@ import Foundation struct ModuleMetadata: Codable, Hashable { let sourceName: String - let author: String + let author: Author let iconUrl: String let version: String let language: String @@ -18,6 +18,11 @@ struct ModuleMetadata: Codable, Hashable { let scriptUrl: String let asyncJS: Bool? let streamAsyncJS: Bool? + + struct Author: Codable, Hashable { + let name: String + let icon: String + } } struct ScrapingModule: Codable, Identifiable, Hashable { diff --git a/Sora/Views/SettingsView/SettingsSubViews/SettingsViewModule.swift b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewModule.swift index f7131b6..1535f67 100644 --- a/Sora/Views/SettingsView/SettingsSubViews/SettingsViewModule.swift +++ b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewModule.swift @@ -15,6 +15,7 @@ struct SettingsViewModule: View { @State private var errorMessage: String? @State private var isLoading = false @State private var isRefreshing = false + @State private var moduleUrl: String = "" var body: some View { VStack { @@ -52,7 +53,7 @@ struct SettingsViewModule: View { .font(.subheadline) .foregroundColor(.secondary) } - Text("Author: \(module.metadata.author)") + Text("Author: \(module.metadata.author.name)") .font(.subheadline) .foregroundColor(.secondary) Text("Language: \(module.metadata.language)") @@ -116,6 +117,11 @@ struct SettingsViewModule: View { isRefreshing = false } } + .onAppear { + Task { + await moduleManager.refreshModules() + } + } .alert(isPresented: .constant(errorMessage != nil)) { Alert( title: Text("Error"), @@ -135,7 +141,7 @@ struct SettingsViewModule: View { alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) alert.addAction(UIAlertAction(title: "Add", style: .default, handler: { _ in if let url = alert.textFields?.first?.text { - addModule(from: url) + displayModuleView(url: url) } })) @@ -145,28 +151,15 @@ struct SettingsViewModule: View { } } - private func addModule(from url: String) { - isLoading = true - errorMessage = nil - - Task { - do { - _ = try await moduleManager.addModule(metadataUrl: url) - DispatchQueue.main.async { - isLoading = false - DropManager.shared.showDrop(title: "Module Added", subtitle: "clicking it to select it", duration: 2.0, icon: UIImage(systemName: "app.badge.checkmark")) - } - } catch { - DispatchQueue.main.async { - isLoading = false - if (error as NSError).domain == "Module already exists" { - errorMessage = "Module already exists" - } else { - errorMessage = "Failed to add module: \(error.localizedDescription)" - } - Logger.shared.log("Failed to add module: \(error.localizedDescription)") - } - } + func displayModuleView(url: String) { + DispatchQueue.main.async { + let addModuleView = ModuleAdditionSettingsView(moduleUrl: url).environmentObject(moduleManager) + let hostingController = UIHostingController(rootView: addModuleView) + + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let window = windowScene.windows.first { + window.rootViewController?.present(hostingController, animated: true, completion: nil) + } } } -} \ No newline at end of file +} diff --git a/Sora/Views/SettingsView/SettingsView.swift b/Sora/Views/SettingsView/SettingsView.swift index fd300b8..17b5f75 100644 --- a/Sora/Views/SettingsView/SettingsView.swift +++ b/Sora/Views/SettingsView/SettingsView.swift @@ -8,7 +8,6 @@ import SwiftUI struct SettingsView: View { - var body: some View { NavigationView { Form {