diff --git a/Sora/Views/SettingsView/SettingsSubViews/SettingsViewAbout.swift b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewAbout.swift new file mode 100644 index 0000000..977b0be --- /dev/null +++ b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewAbout.swift @@ -0,0 +1,175 @@ +// +// SettingsViewAbout.swift +// Sulfur +// +// Created by Francesco on 26/05/25. +// + +import SwiftUI +import Kingfisher + +struct SettingsViewAbout: View { + let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "ALPHA" + + var body: some View { + Form { + Section(footer: Text("Sora/Sulfur will always remain free with no ADs!")) { + HStack { + KFImage(URL(string: "https://raw.githubusercontent.com/cranci1/Sora/refs/heads/dev/Sora/Assets.xcassets/AppIcons/AppIcon_Default.appiconset/darkmode.png")) + .placeholder { + ProgressView() + } + .resizable() + .frame(width: 100, height: 100) + .cornerRadius(20) + .shadow(radius: 5) + + VStack(spacing: 8) { + Text("Sora") + .font(.title) + .bold() + Text("Version \(version)") + .font(.subheadline) + .foregroundColor(.secondary) + } + } + .listRowInsets(EdgeInsets()) + .padding() + } + + Section("Main Developer") { + Button(action: { + if let url = URL(string: "https://github.com/cranci1") { + UIApplication.shared.open(url) + } + }) { + HStack { + KFImage(URL(string: "https://avatars.githubusercontent.com/u/100066266?v=4")) + .placeholder { + ProgressView() + } + .resizable() + .frame(width: 40, height: 40) + .clipShape(Circle()) + + VStack(alignment: .leading) { + Text("cranci1") + .font(.headline) + .foregroundColor(.indigo) + Text("me frfr") + .font(.subheadline) + .foregroundColor(.secondary) + } + Spacer() + Image(systemName: "safari") + .foregroundColor(.indigo) + } + } + } + + Section("Contributors") { + ContributorsView() + } + } + .navigationTitle("About") + } +} + +struct ContributorsView: View { + @State private var contributors: [Contributor] = [] + @State private var isLoading = true + @State private var error: Error? + + var body: some View { + Group { + if isLoading { + HStack { + Spacer() + ProgressView() + Spacer() + } + } else if error != nil { + Text("Failed to load contributors") + .foregroundColor(.secondary) + } else { + ForEach(filteredContributors) { contributor in + ContributorView(contributor: contributor) + } + } + } + .onAppear { + loadContributors() + } + } + + private var filteredContributors: [Contributor] { + contributors.filter { contributor in + !["cranci1", "code-factor"].contains(contributor.login.lowercased()) + } + } + + private func loadContributors() { + let url = URL(string: "https://api.github.com/repos/cranci1/Sora/contributors")! + + URLSession.shared.dataTask(with: url) { data, response, error in + DispatchQueue.main.async { + isLoading = false + + if let error = error { + self.error = error + return + } + + if let data = data { + do { + self.contributors = try JSONDecoder().decode([Contributor].self, from: data) + } catch { + self.error = error + } + } + } + }.resume() + } +} + +struct ContributorView: View { + let contributor: Contributor + + var body: some View { + Button(action: { + if let url = URL(string: "https://github.com/\(contributor.login)") { + UIApplication.shared.open(url) + } + }) { + HStack { + KFImage(URL(string: contributor.avatarUrl)) + .placeholder { + ProgressView() + } + .resizable() + .frame(width: 40, height: 40) + .clipShape(Circle()) + + Text(contributor.login) + .font(.headline) + .foregroundColor(.accentColor) + + Spacer() + Image(systemName: "safari") + .foregroundColor(.accentColor) + } + } + } +} + +struct Contributor: Identifiable, Decodable { + let id: Int + let login: String + let avatarUrl: String + + enum CodingKeys: String, CodingKey { + case id + case login + case avatarUrl = "avatar_url" + } +} \ No newline at end of file diff --git a/Sora/Views/SettingsView/SettingsView.swift b/Sora/Views/SettingsView/SettingsView.swift index 32b77cd..51350a8 100644 --- a/Sora/Views/SettingsView/SettingsView.swift +++ b/Sora/Views/SettingsView/SettingsView.swift @@ -41,6 +41,9 @@ struct SettingsView: View { } Section(header: Text("Info")) { + NavigationLink(destination: SettingsViewAbout()) { + Text("About Sora") + } Button(action: { if let url = URL(string: "https://github.com/cranci1/Sora") { UIApplication.shared.open(url) diff --git a/Sulfur.xcodeproj/project.pbxproj b/Sulfur.xcodeproj/project.pbxproj index 110a1b0..fd74cbf 100644 --- a/Sulfur.xcodeproj/project.pbxproj +++ b/Sulfur.xcodeproj/project.pbxproj @@ -40,6 +40,7 @@ 136BBE802DB1038000906B5E /* Notification+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 136BBE7F2DB1038000906B5E /* Notification+Name.swift */; }; 138AA1B82D2D66FD0021F9DF /* EpisodeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138AA1B62D2D66FD0021F9DF /* EpisodeCell.swift */; }; 138AA1B92D2D66FD0021F9DF /* CircularProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138AA1B72D2D66FD0021F9DF /* CircularProgressBar.swift */; }; + 1398FB3F2DE4E161004D3F5F /* SettingsViewAbout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1398FB3E2DE4E161004D3F5F /* SettingsViewAbout.swift */; }; 139935662D468C450065CEFF /* ModuleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 139935652D468C450065CEFF /* ModuleManager.swift */; }; 1399FAD42D3AB38C00E97C31 /* SettingsViewLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1399FAD32D3AB38C00E97C31 /* SettingsViewLogger.swift */; }; 1399FAD62D3AB3DB00E97C31 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1399FAD52D3AB3DB00E97C31 /* Logger.swift */; }; @@ -118,6 +119,7 @@ 136BBE7F2DB1038000906B5E /* Notification+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Name.swift"; sourceTree = ""; }; 138AA1B62D2D66FD0021F9DF /* EpisodeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeCell.swift; sourceTree = ""; }; 138AA1B72D2D66FD0021F9DF /* CircularProgressBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircularProgressBar.swift; sourceTree = ""; }; + 1398FB3E2DE4E161004D3F5F /* SettingsViewAbout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewAbout.swift; sourceTree = ""; }; 139935652D468C450065CEFF /* ModuleManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleManager.swift; sourceTree = ""; }; 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 = ""; }; @@ -287,6 +289,7 @@ 130C6BF92D53AB1F00DC1432 /* SettingsViewData.swift */, 13DB46912D900BCE008CBC03 /* SettingsViewTrackers.swift */, 72443C7E2DC8038300A61321 /* SettingsViewDownloads.swift */, + 1398FB3E2DE4E161004D3F5F /* SettingsViewAbout.swift */, ); path = SettingsSubViews; sourceTree = ""; @@ -652,6 +655,7 @@ 13E62FC22DABC5830007E259 /* Trakt-Login.swift in Sources */, 133F55BB2D33B55100E08EEA /* LibraryManager.swift in Sources */, 13E62FC42DABC58C0007E259 /* Trakt-Token.swift in Sources */, + 1398FB3F2DE4E161004D3F5F /* SettingsViewAbout.swift in Sources */, 133D7C8E2D2BE2640075467E /* LibraryView.swift in Sources */, 13E62FC72DABFE900007E259 /* TraktPushUpdates.swift in Sources */, 133D7C6E2D2BE2500075467E /* SoraApp.swift in Sources */,