265 lines
8.6 KiB
Swift
265 lines
8.6 KiB
Swift
import SwiftUI
|
|
|
|
struct ProfileView: View {
|
|
@EnvironmentObject var authManager: AuthManager
|
|
@EnvironmentObject var cardManager: CardManager
|
|
@State private var showingLogoutAlert = false
|
|
|
|
var body: some View {
|
|
NavigationView {
|
|
ZStack {
|
|
// Background
|
|
LinearGradient(
|
|
gradient: Gradient(colors: [
|
|
Color(hex: "0a0a0a"),
|
|
Color(hex: "1a1a1a")
|
|
]),
|
|
startPoint: .top,
|
|
endPoint: .bottom
|
|
)
|
|
.ignoresSafeArea()
|
|
|
|
ScrollView {
|
|
VStack(spacing: 24) {
|
|
// Profile header
|
|
if let user = authManager.currentUser {
|
|
ProfileHeaderView(user: user)
|
|
}
|
|
|
|
// Collection summary
|
|
CollectionSummaryView(cards: cardManager.userCards)
|
|
|
|
// Menu items
|
|
VStack(spacing: 1) {
|
|
MenuRow(
|
|
icon: "arrow.triangle.2.circlepath",
|
|
title: "データ同期",
|
|
subtitle: "atproto PDSと同期"
|
|
) {
|
|
// TODO: Implement sync
|
|
}
|
|
|
|
MenuRow(
|
|
icon: "crown",
|
|
title: "ユニークカード",
|
|
subtitle: "所有しているユニークカード"
|
|
) {
|
|
// TODO: Show unique cards
|
|
}
|
|
|
|
MenuRow(
|
|
icon: "info.circle",
|
|
title: "アプリについて",
|
|
subtitle: "バージョン情報"
|
|
) {
|
|
// TODO: Show about
|
|
}
|
|
}
|
|
.background(Color.white.opacity(0.05))
|
|
.cornerRadius(12)
|
|
|
|
Spacer(minLength: 40)
|
|
|
|
// Logout button
|
|
Button(action: {
|
|
showingLogoutAlert = true
|
|
}) {
|
|
HStack {
|
|
Image(systemName: "rectangle.portrait.and.arrow.right")
|
|
Text("ログアウト")
|
|
}
|
|
.font(.headline)
|
|
.foregroundColor(.red)
|
|
.frame(maxWidth: .infinity)
|
|
.frame(height: 50)
|
|
.background(Color.red.opacity(0.1))
|
|
.cornerRadius(12)
|
|
.overlay(
|
|
RoundedRectangle(cornerRadius: 12)
|
|
.stroke(Color.red.opacity(0.3), lineWidth: 1)
|
|
)
|
|
}
|
|
|
|
Spacer(minLength: 100)
|
|
}
|
|
.padding(.horizontal)
|
|
}
|
|
}
|
|
.navigationTitle("プロフィール")
|
|
.navigationBarTitleDisplayMode(.large)
|
|
.alert("ログアウト", isPresented: $showingLogoutAlert) {
|
|
Button("キャンセル", role: .cancel) { }
|
|
Button("ログアウト", role: .destructive) {
|
|
authManager.logout()
|
|
}
|
|
} message: {
|
|
Text("ログアウトしますか?")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ProfileHeaderView: View {
|
|
let user: User
|
|
|
|
var body: some View {
|
|
VStack(spacing: 16) {
|
|
// Avatar
|
|
Circle()
|
|
.fill(
|
|
LinearGradient(
|
|
gradient: Gradient(colors: [
|
|
Color(hex: "fff700"),
|
|
Color(hex: "ff00ff")
|
|
]),
|
|
startPoint: .topLeading,
|
|
endPoint: .bottomTrailing
|
|
)
|
|
)
|
|
.frame(width: 80, height: 80)
|
|
.overlay(
|
|
Text(user.handle.prefix(1).uppercased())
|
|
.font(.title)
|
|
.fontWeight(.bold)
|
|
.foregroundColor(.black)
|
|
)
|
|
|
|
// User info
|
|
VStack(spacing: 4) {
|
|
Text("@\(user.handle)")
|
|
.font(.title2)
|
|
.fontWeight(.bold)
|
|
.foregroundColor(.white)
|
|
|
|
Text(user.did)
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
.lineLimit(1)
|
|
.truncationMode(.middle)
|
|
}
|
|
}
|
|
.padding(.vertical, 20)
|
|
}
|
|
}
|
|
|
|
struct CollectionSummaryView: View {
|
|
let cards: [Card]
|
|
|
|
private var summary: (total: Int, unique: Int, rarest: CardRarity?) {
|
|
let total = cards.count
|
|
let uniqueCount = cards.filter { $0.isUnique }.count
|
|
let rarest = cards.map { $0.status }.max { lhs, rhs in
|
|
rarityOrder(lhs) < rarityOrder(rhs)
|
|
}
|
|
|
|
return (total, uniqueCount, rarest)
|
|
}
|
|
|
|
private func rarityOrder(_ rarity: CardRarity) -> Int {
|
|
switch rarity {
|
|
case .normal: return 0
|
|
case .rare: return 1
|
|
case .superRare: return 2
|
|
case .kira: return 3
|
|
case .unique: return 4
|
|
}
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(spacing: 16) {
|
|
Text("コレクション統計")
|
|
.font(.headline)
|
|
.foregroundColor(.white)
|
|
|
|
HStack(spacing: 20) {
|
|
SummaryItem(
|
|
title: "総カード数",
|
|
value: "\(summary.total)",
|
|
color: Color(hex: "fff700")
|
|
)
|
|
|
|
SummaryItem(
|
|
title: "ユニーク",
|
|
value: "\(summary.unique)",
|
|
color: Color(hex: "ff00ff")
|
|
)
|
|
|
|
if let rarest = summary.rarest {
|
|
SummaryItem(
|
|
title: "最高レア",
|
|
value: rarest.displayName,
|
|
color: Color(hex: rarest.gradientColors.first ?? "ffffff")
|
|
)
|
|
}
|
|
}
|
|
}
|
|
.padding()
|
|
.background(Color.white.opacity(0.05))
|
|
.cornerRadius(12)
|
|
}
|
|
}
|
|
|
|
struct SummaryItem: View {
|
|
let title: String
|
|
let value: String
|
|
let color: Color
|
|
|
|
var body: some View {
|
|
VStack(spacing: 8) {
|
|
Text(value)
|
|
.font(.title2)
|
|
.fontWeight(.bold)
|
|
.foregroundColor(color)
|
|
|
|
Text(title)
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
}
|
|
|
|
struct MenuRow: View {
|
|
let icon: String
|
|
let title: String
|
|
let subtitle: String
|
|
let action: () -> Void
|
|
|
|
var body: some View {
|
|
Button(action: action) {
|
|
HStack(spacing: 12) {
|
|
Image(systemName: icon)
|
|
.font(.title2)
|
|
.foregroundColor(Color(hex: "fff700"))
|
|
.frame(width: 24)
|
|
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(title)
|
|
.font(.headline)
|
|
.foregroundColor(.white)
|
|
|
|
Text(subtitle)
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
Spacer()
|
|
|
|
Image(systemName: "chevron.right")
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
.padding()
|
|
}
|
|
.buttonStyle(PlainButtonStyle())
|
|
}
|
|
}
|
|
|
|
struct ProfileView_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
ProfileView()
|
|
.environmentObject(AuthManager())
|
|
.environmentObject(CardManager())
|
|
}
|
|
} |