mirror of
https://github.com/pawelorzech/MacTorn.git
synced 2026-01-30 04:04:27 +00:00
Merge pull request #1 from pawelorzech/claude/add-credits-page-NhUe5
Add credits page with username linking
This commit is contained in:
commit
2e485c05d1
2 changed files with 310 additions and 10 deletions
275
MacTorn/MacTorn/Views/CreditsView.swift
Normal file
275
MacTorn/MacTorn/Views/CreditsView.swift
Normal file
|
|
@ -0,0 +1,275 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct CreditsView: View {
|
||||||
|
@Binding var showCredits: Bool
|
||||||
|
|
||||||
|
// MARK: - Developer
|
||||||
|
private let developer = TornContributor(name: "bombel", tornID: 2362436)
|
||||||
|
|
||||||
|
// MARK: - Special Thanks
|
||||||
|
private let specialThanks: [TornContributor] = [
|
||||||
|
TornContributor(name: "kaszmir", tornID: 3913934),
|
||||||
|
TornContributor(name: "dylanwishop", tornID: 3918903),
|
||||||
|
TornContributor(name: "constanziagatta", tornID: 3961012),
|
||||||
|
]
|
||||||
|
|
||||||
|
// MARK: - Faction
|
||||||
|
private let factionName = "The Masters"
|
||||||
|
private let factionID = 11559
|
||||||
|
|
||||||
|
// MARK: - Company
|
||||||
|
private let companyName = "Glory Holes Productions"
|
||||||
|
private let companyOwnerID = 2362436
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
VStack(spacing: 14) {
|
||||||
|
// Developer Section
|
||||||
|
developerSection
|
||||||
|
|
||||||
|
// Special Thanks Section
|
||||||
|
contributorSection(
|
||||||
|
title: "Special Thanks",
|
||||||
|
icon: "star.fill",
|
||||||
|
iconColor: .yellow,
|
||||||
|
contributors: specialThanks
|
||||||
|
)
|
||||||
|
|
||||||
|
// Faction Section
|
||||||
|
factionSection
|
||||||
|
|
||||||
|
// Company Section
|
||||||
|
companySection
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
|
||||||
|
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: - Developer Section
|
||||||
|
private var developerSection: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
HStack(spacing: 6) {
|
||||||
|
Image(systemName: "hammer.fill")
|
||||||
|
.foregroundColor(.orange)
|
||||||
|
Text("Created by")
|
||||||
|
.font(.subheadline.bold())
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
openTornProfile(developer.tornID!)
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text(developer.name)
|
||||||
|
.font(.caption.bold())
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "arrow.up.right.square")
|
||||||
|
.font(.caption2)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
.padding(10)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.background(Color.orange.opacity(0.1))
|
||||||
|
.cornerRadius(8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Faction Section
|
||||||
|
private var factionSection: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
HStack(spacing: 6) {
|
||||||
|
Image(systemName: "shield.fill")
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
Text("Faction")
|
||||||
|
.font(.subheadline.bold())
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
openFaction(factionID)
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text(factionName)
|
||||||
|
.font(.caption)
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "arrow.up.right.square")
|
||||||
|
.font(.caption2)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
.padding(10)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.background(Color.secondary.opacity(0.1))
|
||||||
|
.cornerRadius(8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Company Section
|
||||||
|
private var companySection: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
HStack(spacing: 6) {
|
||||||
|
Image(systemName: "building.2.fill")
|
||||||
|
.foregroundColor(.purple)
|
||||||
|
Text("Company")
|
||||||
|
.font(.subheadline.bold())
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
openCompany(companyOwnerID)
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text(companyName)
|
||||||
|
.font(.caption)
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "arrow.up.right.square")
|
||||||
|
.font(.caption2)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
.padding(10)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.background(Color.secondary.opacity(0.1))
|
||||||
|
.cornerRadius(8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Contributors Section
|
||||||
|
@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: - URL Helpers
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func openFaction(_ factionID: Int) {
|
||||||
|
let urlString = "https://www.torn.com/factions.php?step=profile&ID=\(factionID)"
|
||||||
|
if let url = URL(string: urlString) {
|
||||||
|
NSWorkspace.shared.open(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func openCompany(_ ownerID: Int) {
|
||||||
|
let urlString = "https://www.torn.com/joblist.php#/p=corpinfo&userID=\(ownerID)"
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
@ -3,11 +3,20 @@ import SwiftUI
|
||||||
struct SettingsView: View {
|
struct SettingsView: View {
|
||||||
@EnvironmentObject var appState: AppState
|
@EnvironmentObject var appState: AppState
|
||||||
@State private var inputKey: String = ""
|
@State private var inputKey: String = ""
|
||||||
|
@State private var showCredits: Bool = false
|
||||||
|
|
||||||
// Developer ID for tip feature (bombel)
|
// Developer ID for tip feature (bombel)
|
||||||
private let developerID = 2362436
|
private let developerID = 2362436
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
if showCredits {
|
||||||
|
CreditsView(showCredits: $showCredits)
|
||||||
|
} else {
|
||||||
|
settingsContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var settingsContent: some View {
|
||||||
VStack(spacing: 20) {
|
VStack(spacing: 20) {
|
||||||
// Header
|
// Header
|
||||||
Image(systemName: "bolt.circle.fill")
|
Image(systemName: "bolt.circle.fill")
|
||||||
|
|
@ -143,6 +152,7 @@ struct SettingsView: View {
|
||||||
|
|
||||||
// GitHub & Version
|
// GitHub & Version
|
||||||
VStack(spacing: 4) {
|
VStack(spacing: 4) {
|
||||||
|
HStack(spacing: 12) {
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
Image(systemName: "chevron.left.forwardslash.chevron.right")
|
Image(systemName: "chevron.left.forwardslash.chevron.right")
|
||||||
.font(.caption2)
|
.font(.caption2)
|
||||||
|
|
@ -152,6 +162,21 @@ struct SettingsView: View {
|
||||||
.font(.caption)
|
.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 {
|
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
|
||||||
Text("v\(version)")
|
Text("v\(version)")
|
||||||
.font(.caption2)
|
.font(.caption2)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue