From 14439f50ff67b8edc0ab3937962f24c288a57c0a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 18 Jan 2026 16:28:52 +0000 Subject: [PATCH] feat: Add Credits page accessible from Settings - Create CreditsView with placeholder contributors - Contributors can have Torn profile links (auto-generated from ID) - Support for multiple sections (Special Thanks, Beta Testers) - Navigate to Credits via heart icon button in Settings footer --- MacTorn/MacTorn/Views/CreditsView.swift | 173 +++++++++++++++++++++++ MacTorn/MacTorn/Views/SettingsView.swift | 45 ++++-- 2 files changed, 208 insertions(+), 10 deletions(-) create mode 100644 MacTorn/MacTorn/Views/CreditsView.swift diff --git a/MacTorn/MacTorn/Views/CreditsView.swift b/MacTorn/MacTorn/Views/CreditsView.swift new file mode 100644 index 0000000..976d85d --- /dev/null +++ b/MacTorn/MacTorn/Views/CreditsView.swift @@ -0,0 +1,173 @@ +import SwiftUI + +struct CreditsView: View { + @Binding var showCredits: Bool + + // MARK: - Contributors Data + // Format: (name: "Username", tornID: 123456) + // The tornID will automatically link to the Torn profile + + private let specialThanks: [TornContributor] = [ + // TODO: Add contributors here + // Example: TornContributor(name: "bombel", tornID: 2362436), + TornContributor(name: "Placeholder1", tornID: nil), + TornContributor(name: "Placeholder2", tornID: nil), + TornContributor(name: "Placeholder3", tornID: nil), + ] + + private let testers: [TornContributor] = [ + // TODO: Add testers here + TornContributor(name: "TesterPlaceholder1", tornID: nil), + TornContributor(name: "TesterPlaceholder2", tornID: nil), + ] + + var body: some View { + VStack(spacing: 16) { + // Header + VStack(spacing: 8) { + Image(systemName: "heart.fill") + .font(.system(size: 36)) + .foregroundStyle( + LinearGradient( + colors: [.pink, .red], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + ) + + Text("Credits") + .font(.title2.bold()) + + Text("Thank you for your support!") + .font(.caption) + .foregroundColor(.secondary) + } + + Divider() + + ScrollView { + VStack(spacing: 16) { + // Special Thanks Section + if !specialThanks.isEmpty { + contributorSection( + title: "Special Thanks", + icon: "star.fill", + iconColor: .yellow, + contributors: specialThanks + ) + } + + // Testers Section + if !testers.isEmpty { + contributorSection( + title: "Beta Testers", + icon: "checkmark.seal.fill", + iconColor: .green, + contributors: testers + ) + } + } + .padding(.horizontal) + } + .frame(maxHeight: 200) + + Spacer() + + // Back Button + Button { + showCredits = false + } label: { + HStack(spacing: 4) { + Image(systemName: "chevron.left") + Text("Back to Settings") + } + .font(.caption) + } + .buttonStyle(.plain) + .foregroundColor(.accentColor) + } + .padding() + .frame(width: 320) + } + + // MARK: - Section View + @ViewBuilder + private func contributorSection( + title: String, + icon: String, + iconColor: Color, + contributors: [TornContributor] + ) -> some View { + VStack(alignment: .leading, spacing: 8) { + HStack(spacing: 6) { + Image(systemName: icon) + .foregroundColor(iconColor) + Text(title) + .font(.subheadline.bold()) + } + + VStack(spacing: 4) { + ForEach(contributors) { contributor in + contributorRow(contributor) + } + } + .padding(10) + .frame(maxWidth: .infinity) + .background(Color.secondary.opacity(0.1)) + .cornerRadius(8) + } + } + + // MARK: - Contributor Row + @ViewBuilder + private func contributorRow(_ contributor: TornContributor) -> some View { + if let tornID = contributor.tornID { + Button { + openTornProfile(tornID) + } label: { + HStack { + Text(contributor.name) + .font(.caption) + Spacer() + Image(systemName: "arrow.up.right.square") + .font(.caption2) + .foregroundColor(.secondary) + } + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .foregroundColor(.accentColor) + } else { + HStack { + Text(contributor.name) + .font(.caption) + .foregroundColor(.primary) + Spacer() + } + } + } + + // MARK: - Helper + private func openTornProfile(_ tornID: Int) { + let urlString = "https://www.torn.com/profiles.php?XID=\(tornID)" + if let url = URL(string: urlString) { + NSWorkspace.shared.open(url) + } + } +} + +// MARK: - Contributor Model +struct TornContributor: Identifiable { + let id = UUID() + let name: String + let tornID: Int? + + init(name: String, tornID: Int?) { + self.name = name + self.tornID = tornID + } +} + +#Preview { + CreditsView(showCredits: .constant(true)) +} diff --git a/MacTorn/MacTorn/Views/SettingsView.swift b/MacTorn/MacTorn/Views/SettingsView.swift index 935c34e..b28f42d 100644 --- a/MacTorn/MacTorn/Views/SettingsView.swift +++ b/MacTorn/MacTorn/Views/SettingsView.swift @@ -3,11 +3,20 @@ import SwiftUI struct SettingsView: View { @EnvironmentObject var appState: AppState @State private var inputKey: String = "" - + @State private var showCredits: Bool = false + // Developer ID for tip feature (bombel) private let developerID = 2362436 - + var body: some View { + if showCredits { + CreditsView(showCredits: $showCredits) + } else { + settingsContent + } + } + + private var settingsContent: some View { VStack(spacing: 20) { // Header Image(systemName: "bolt.circle.fill") @@ -143,15 +152,31 @@ struct SettingsView: View { // GitHub & Version VStack(spacing: 4) { - HStack(spacing: 4) { - Image(systemName: "chevron.left.forwardslash.chevron.right") - .font(.caption2) - .foregroundColor(.gray) - Link("View on GitHub", - destination: URL(string: "https://github.com/pawelorzech/MacTorn")!) - .font(.caption) + HStack(spacing: 12) { + HStack(spacing: 4) { + Image(systemName: "chevron.left.forwardslash.chevron.right") + .font(.caption2) + .foregroundColor(.gray) + Link("View on GitHub", + destination: URL(string: "https://github.com/pawelorzech/MacTorn")!) + .font(.caption) + } + + Button { + showCredits = true + } label: { + HStack(spacing: 4) { + Image(systemName: "heart.fill") + .font(.caption2) + .foregroundColor(.pink) + Text("Credits") + .font(.caption) + } + } + .buttonStyle(.plain) + .foregroundColor(.accentColor) } - + if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String { Text("v\(version)") .font(.caption2)