727 lines
27 KiB
Swift
727 lines
27 KiB
Swift
import SwiftUI
|
|
|
|
// MARK: - Play Hub
|
|
|
|
struct PlayHubView: View {
|
|
@EnvironmentObject var appState: AppState
|
|
@State private var isPremium = false
|
|
@State private var showPaywall = false
|
|
|
|
let games: [(icon: String, title: String, description: String, color: Color, isPremium: Bool, gameType: GameType)] = [
|
|
("dice.fill", "Spin the Wheel", "Let fate decide your next adventure", .closerPrimary, false, .wheel),
|
|
("hand.raised.fill", "This or That", "Discover each other's preferences", .closerSecondary, false, .thisOrThat),
|
|
("person.fill.questionmark", "How Well Do You Know Me", "Test your knowledge of each other", .categoryCommunication, false, .howWell),
|
|
("sparkles", "Desire Sync", "Align your desires and dreams", .closerSecondary, true, .desireSync),
|
|
("mountain.2.fill", "Connection Challenges", "Multi-day challenges for couples", .closerGold, true, .connectionChallenges),
|
|
("clock.fill", "Memory Lane", "Revisit your time capsules", .closerPrimary, true, .memoryLane),
|
|
]
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: CloserSpacing.xl) {
|
|
Text("Play Together")
|
|
.font(CloserFont.title1)
|
|
.foregroundColor(.closerText)
|
|
.closerPadding()
|
|
|
|
LazyVGrid(columns: [GridItem(.flexible())], spacing: CloserSpacing.md) {
|
|
ForEach(games, id: \.title) { game in
|
|
GameCard(
|
|
icon: game.icon,
|
|
title: game.title,
|
|
description: game.description,
|
|
color: game.color,
|
|
isPremium: game.isPremium,
|
|
isUnlocked: !game.isPremium || isPremium,
|
|
gameType: game.gameType
|
|
)
|
|
}
|
|
}
|
|
.closerPadding()
|
|
|
|
// Game History
|
|
NavigationLink {
|
|
GameHistoryView()
|
|
} label: {
|
|
HStack {
|
|
Image(systemName: "clock.arrow.circlepath")
|
|
Text("Past Games")
|
|
Spacer()
|
|
Image(systemName: "chevron.right")
|
|
}
|
|
.font(CloserFont.body)
|
|
.foregroundColor(.closerText)
|
|
.padding()
|
|
.closerCard()
|
|
}
|
|
.buttonStyle(.plain)
|
|
.closerPadding()
|
|
}
|
|
.padding(.vertical)
|
|
}
|
|
.background(Color.closerBackground)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.sheet(isPresented: $showPaywall) {
|
|
PaywallView()
|
|
}
|
|
.task {
|
|
isPremium = await DefaultEntitlementChecker().hasPremium()
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Game Card
|
|
|
|
struct GameCard: View {
|
|
let icon: String
|
|
let title: String
|
|
let description: String
|
|
let color: Color
|
|
let isPremium: Bool
|
|
let isUnlocked: Bool
|
|
let gameType: GameType
|
|
@State private var showGame = false
|
|
|
|
var body: some View {
|
|
Button(action: handleTap) {
|
|
HStack(spacing: CloserSpacing.lg) {
|
|
// Icon
|
|
ZStack {
|
|
RoundedRectangle(cornerRadius: CloserRadius.medium)
|
|
.fill(color.opacity(0.15))
|
|
.frame(width: 60, height: 60)
|
|
if gameType == .wheel {
|
|
Image("illustration-spin-wheel")
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(width: 52, height: 52)
|
|
} else {
|
|
Image(systemName: icon)
|
|
.font(.title2)
|
|
.foregroundColor(color)
|
|
}
|
|
}
|
|
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
HStack(spacing: 6) {
|
|
Text(title)
|
|
.font(CloserFont.headline)
|
|
.foregroundColor(.closerText)
|
|
if isPremium {
|
|
PremiumBadge()
|
|
}
|
|
}
|
|
Text(description)
|
|
.font(CloserFont.caption)
|
|
.foregroundColor(.closerTextSecondary)
|
|
.lineLimit(2)
|
|
}
|
|
|
|
Spacer()
|
|
|
|
if !isUnlocked {
|
|
Image(systemName: "lock.fill")
|
|
.foregroundColor(.closerTextSecondary)
|
|
}
|
|
|
|
Image(systemName: "chevron.right")
|
|
.font(.caption)
|
|
.foregroundColor(.closerDivider)
|
|
}
|
|
.padding(CloserSpacing.md)
|
|
.closerCard()
|
|
}
|
|
.buttonStyle(.plain)
|
|
.navigationDestination(isPresented: $showGame) {
|
|
destinationView(for: gameType)
|
|
}
|
|
}
|
|
|
|
private func handleTap() {
|
|
guard isUnlocked else { return }
|
|
showGame = true
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func destinationView(for type: GameType) -> some View {
|
|
switch type {
|
|
case .wheel:
|
|
CategoryPickerView()
|
|
case .thisOrThat:
|
|
ThisOrThatView()
|
|
case .howWell:
|
|
HowWellView()
|
|
case .desireSync:
|
|
DesireSyncView()
|
|
case .connectionChallenges:
|
|
ConnectionChallengesView()
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Game History
|
|
|
|
struct GameHistoryView: View {
|
|
@State private var sessions: [QuestionSession] = []
|
|
@State private var isLoading = true
|
|
|
|
var body: some View {
|
|
List {
|
|
if isLoading {
|
|
ProgressView()
|
|
.frame(maxWidth: .infinity)
|
|
} else if sessions.isEmpty {
|
|
EmptyStateView(
|
|
icon: "clock.arrow.circlepath",
|
|
title: "No Games Yet",
|
|
message: "Your game history will appear here once you start playing."
|
|
)
|
|
.listRowBackground(Color.clear)
|
|
} else {
|
|
ForEach(sessions) { session in
|
|
NavigationLink {
|
|
destinationForReplay(session.gameType, sessionId: session.id)
|
|
} label: {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(session.gameType.replacing("_", with: " ").capitalized)
|
|
.font(CloserFont.body)
|
|
.foregroundColor(.closerText)
|
|
Text(session.startedAt, style: .date)
|
|
.font(CloserFont.caption)
|
|
.foregroundColor(.closerTextSecondary)
|
|
}
|
|
.padding(.vertical, 4)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.listStyle(.insetGrouped)
|
|
.background(Color.closerBackground)
|
|
.navigationTitle("Game History")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.task {
|
|
try? await Task.sleep(nanoseconds: 500_000_000)
|
|
isLoading = false
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func destinationForReplay(_ gameType: String, sessionId: String) -> some View {
|
|
switch gameType {
|
|
case "this_or_that": ThisOrThatReplayView(sessionId: sessionId)
|
|
case "how_well": HowWellReplayView(sessionId: sessionId)
|
|
case "desire_sync": DesireSyncReplayView(sessionId: sessionId)
|
|
default: Text("Replay not available")
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - This or That
|
|
|
|
struct ThisOrThatView: View {
|
|
@State private var currentPair = 0
|
|
@State private var choices: [String] = []
|
|
@State private var showResults = false
|
|
|
|
let pairs = [
|
|
("Beach vacation", "Mountain retreat"),
|
|
("Dinner out", "Cooking together"),
|
|
("Movie night", "Board game night"),
|
|
("Early bird", "Night owl"),
|
|
("Cats", "Dogs"),
|
|
("Coffee", "Tea"),
|
|
("Summer", "Winter"),
|
|
("City life", "Country life"),
|
|
]
|
|
|
|
var body: some View {
|
|
VStack(spacing: CloserSpacing.xxl) {
|
|
if showResults {
|
|
// Results view
|
|
VStack(spacing: CloserSpacing.lg) {
|
|
Image(systemName: "checkmark.circle.fill")
|
|
.font(.system(size: 64))
|
|
.foregroundColor(.closerSuccess)
|
|
Text("All Done!")
|
|
.font(CloserFont.title1)
|
|
Text("Your choices are recorded. See how they match with your partner when they play too.")
|
|
.font(CloserFont.callout)
|
|
.foregroundColor(.closerTextSecondary)
|
|
.multilineTextAlignment(.center)
|
|
Button("Play Again") {
|
|
currentPair = 0
|
|
choices = []
|
|
showResults = false
|
|
}
|
|
.buttonStyle(PrimaryButtonStyle())
|
|
}
|
|
} else {
|
|
// Progress
|
|
Text("\(currentPair + 1) of \(pairs.count)")
|
|
.font(CloserFont.subheadline)
|
|
.foregroundColor(.closerTextSecondary)
|
|
|
|
ProgressView(value: Double(currentPair + 1), total: Double(pairs.count))
|
|
.tint(.closerPrimary)
|
|
|
|
// Current pair
|
|
VStack(spacing: CloserSpacing.lg) {
|
|
Text("Would you rather...")
|
|
.font(CloserFont.title3)
|
|
.foregroundColor(.closerText)
|
|
|
|
let options = [pairs[currentPair].0, pairs[currentPair].1]
|
|
ForEach(options, id: \.self) { option in
|
|
Button(action: { choose(option) }) {
|
|
Text(option)
|
|
.font(CloserFont.body)
|
|
.foregroundColor(.closerText)
|
|
.frame(maxWidth: .infinity)
|
|
.padding()
|
|
.background(Color.closerSurface)
|
|
.cornerRadius(CloserRadius.large)
|
|
.overlay(RoundedRectangle(cornerRadius: CloserRadius.large).stroke(Color.closerDivider))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.closerPadding()
|
|
.background(Color.closerBackground)
|
|
.navigationTitle("This or That")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
|
|
private func choose(_ option: String) {
|
|
choices.append(option)
|
|
if currentPair < pairs.count - 1 {
|
|
withAnimation {
|
|
currentPair += 1
|
|
}
|
|
} else {
|
|
withAnimation {
|
|
showResults = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - How Well Do You Know Me
|
|
|
|
struct HowWellView: View {
|
|
@State private var currentQuestion = 0
|
|
@State private var score = 0
|
|
@State private var showResults = false
|
|
@State private var selectedAnswer: String?
|
|
|
|
let questions: [(question: String, options: [String], correctIndex: Int)] = [
|
|
("What's my favorite color?", ["Blue", "Red", "Green", "Purple"], 0),
|
|
("What's my go-to comfort food?", ["Pizza", "Ice cream", "Pasta", "Chocolate"], 1),
|
|
("What would I do with a free day?", ["Read a book", "Go outside", "Watch movies", "Sleep in"], 2),
|
|
("What's my dream travel destination?", ["Japan", "Italy", "New Zealand", "Greece"], 3),
|
|
("Am I more introverted or extroverted?", ["Introverted", "Extroverted", "It depends", "Both equally"], 0),
|
|
]
|
|
|
|
var body: some View {
|
|
VStack(spacing: CloserSpacing.xxl) {
|
|
if showResults {
|
|
VStack(spacing: CloserSpacing.lg) {
|
|
Image(systemName: score == questions.count ? "star.fill" : "heart.fill")
|
|
.font(.system(size: 64))
|
|
.foregroundColor(score == questions.count ? .closerGold : .closerPrimary)
|
|
Text("\(score) / \(questions.count)")
|
|
.font(CloserFont.title1)
|
|
Text(scoreMessage)
|
|
.font(CloserFont.callout)
|
|
.foregroundColor(.closerTextSecondary)
|
|
.multilineTextAlignment(.center)
|
|
Button("Play Again") {
|
|
currentQuestion = 0
|
|
score = 0
|
|
showResults = false
|
|
}
|
|
.buttonStyle(PrimaryButtonStyle())
|
|
}
|
|
} else {
|
|
Text("\(currentQuestion + 1) of \(questions.count)")
|
|
.font(CloserFont.subheadline)
|
|
.foregroundColor(.closerTextSecondary)
|
|
|
|
ProgressView(value: Double(currentQuestion + 1), total: Double(questions.count))
|
|
.tint(.closerPrimary)
|
|
|
|
Text(questions[currentQuestion].question)
|
|
.font(CloserFont.title2)
|
|
.foregroundColor(.closerText)
|
|
.multilineTextAlignment(.center)
|
|
|
|
VStack(spacing: CloserSpacing.md) {
|
|
ForEach(questions[currentQuestion].options.indices, id: \.self) { index in
|
|
Button(action: { selectAnswer(index) }) {
|
|
Text(questions[currentQuestion].options[index])
|
|
.font(CloserFont.body)
|
|
.foregroundColor(.closerText)
|
|
.frame(maxWidth: .infinity)
|
|
.padding()
|
|
.background(selectedAnswer == questions[currentQuestion].options[index] ? Color.closerPrimary.opacity(0.1) : Color.closerSurface)
|
|
.cornerRadius(CloserRadius.large)
|
|
.overlay(RoundedRectangle(cornerRadius: CloserRadius.large).stroke(
|
|
selectedAnswer == questions[currentQuestion].options[index] ? Color.closerPrimary : Color.closerDivider
|
|
))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.closerPadding()
|
|
.background(Color.closerBackground)
|
|
.navigationTitle("How Well Do You Know Me")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
|
|
private func selectAnswer(_ index: Int) {
|
|
let correct = questions[currentQuestion].correctIndex
|
|
if index == correct { score += 1 }
|
|
selectedAnswer = questions[currentQuestion].options[index]
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
|
|
selectedAnswer = nil
|
|
if currentQuestion < questions.count - 1 {
|
|
withAnimation { currentQuestion += 1 }
|
|
} else {
|
|
withAnimation { showResults = true }
|
|
}
|
|
}
|
|
}
|
|
|
|
private var scoreMessage: String {
|
|
switch score {
|
|
case 0...2: return "Time to learn more about each other!"
|
|
case 3...4: return "You know your partner pretty well!"
|
|
case 5: return "Perfect score! You really know each other!"
|
|
default: return ""
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Desire Sync
|
|
|
|
struct DesireSyncView: View {
|
|
@State private var currentQuestion = 0
|
|
@State private var preferences: [String: Int] = [:]
|
|
@State private var showResults = false
|
|
|
|
let questions: [(question: String, item: String)] = [
|
|
("How important is regular date night?", "date_night"),
|
|
("How important is daily check-in?", "daily_checkin"),
|
|
("How important is physical intimacy?", "intimacy"),
|
|
("How important is shared adventure?", "adventure"),
|
|
("How important is quality time at home?", "home_time"),
|
|
]
|
|
|
|
var body: some View {
|
|
VStack(spacing: CloserSpacing.xxl) {
|
|
if showResults {
|
|
VStack(spacing: CloserSpacing.lg) {
|
|
Image(systemName: "sparkles")
|
|
.font(.system(size: 64))
|
|
.foregroundColor(.closerPrimary)
|
|
Text("Preferences Recorded!")
|
|
.font(CloserFont.title1)
|
|
Text("Your responses are saved. Compare with your partner when they complete theirs.")
|
|
.font(CloserFont.callout)
|
|
.foregroundColor(.closerTextSecondary)
|
|
.multilineTextAlignment(.center)
|
|
|
|
Button("View Comparison") {
|
|
// Navigate to comparison view
|
|
}
|
|
.buttonStyle(PrimaryButtonStyle())
|
|
|
|
Button("Play Again") {
|
|
currentQuestion = 0
|
|
preferences = [:]
|
|
showResults = false
|
|
}
|
|
.buttonStyle(SecondaryButtonStyle())
|
|
}
|
|
} else {
|
|
Text("\(currentQuestion + 1) of \(questions.count)")
|
|
.font(CloserFont.subheadline)
|
|
.foregroundColor(.closerTextSecondary)
|
|
|
|
ProgressView(value: Double(currentQuestion + 1), total: Double(questions.count))
|
|
.tint(.closerPrimary)
|
|
|
|
Text(questions[currentQuestion].question)
|
|
.font(CloserFont.title2)
|
|
.foregroundColor(.closerText)
|
|
.multilineTextAlignment(.center)
|
|
|
|
VStack(spacing: CloserSpacing.sm) {
|
|
ForEach(1...5, id: \.self) { value in
|
|
Button(action: { setPreference(value) }) {
|
|
HStack {
|
|
Text(desireLabel(value))
|
|
.font(CloserFont.callout)
|
|
.foregroundColor(.closerText)
|
|
Spacer()
|
|
if preferences[questions[currentQuestion].item] == value {
|
|
Image(systemName: "checkmark.circle.fill")
|
|
.foregroundColor(.closerPrimary)
|
|
}
|
|
}
|
|
.padding()
|
|
.background(preferences[questions[currentQuestion].item] == value ? Color.closerPrimary.opacity(0.1) : Color.closerSurface)
|
|
.cornerRadius(CloserRadius.medium)
|
|
.overlay(RoundedRectangle(cornerRadius: CloserRadius.medium).stroke(Color.closerDivider))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.closerPadding()
|
|
.background(Color.closerBackground)
|
|
.navigationTitle("Desire Sync")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
|
|
private func setPreference(_ value: Int) {
|
|
preferences[questions[currentQuestion].item] = value
|
|
if currentQuestion < questions.count - 1 {
|
|
withAnimation { currentQuestion += 1 }
|
|
} else {
|
|
withAnimation { showResults = true }
|
|
}
|
|
}
|
|
|
|
private func desireLabel(_ value: Int) -> String {
|
|
switch value {
|
|
case 1: return "Not important"
|
|
case 2: return "Slightly important"
|
|
case 3: return "Moderately important"
|
|
case 4: return "Very important"
|
|
case 5: return "Essential"
|
|
default: return ""
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Connection Challenges
|
|
|
|
struct ConnectionChallengesView: View {
|
|
@State private var challenges: [ConnectionChallenge] = []
|
|
@State private var isLoading = true
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: CloserSpacing.lg) {
|
|
Text("Connection Challenges")
|
|
.font(CloserFont.title1)
|
|
.foregroundColor(.closerText)
|
|
.closerPadding()
|
|
|
|
Text("Multi-day programs designed to strengthen your bond")
|
|
.font(CloserFont.callout)
|
|
.foregroundColor(.closerTextSecondary)
|
|
.closerPadding()
|
|
|
|
if isLoading {
|
|
LoadingView(message: "Loading challenges...")
|
|
} else if challenges.isEmpty {
|
|
EmptyStateView(
|
|
icon: "mountain.2",
|
|
title: "No Challenges Yet",
|
|
message: "Connection challenges are coming soon!"
|
|
)
|
|
} else {
|
|
ForEach(challenges) { challenge in
|
|
ChallengeCard(challenge: challenge)
|
|
.closerPadding()
|
|
}
|
|
}
|
|
}
|
|
.padding(.vertical)
|
|
}
|
|
.background(Color.closerBackground)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.task {
|
|
try? await Task.sleep(nanoseconds: 500_000_000)
|
|
isLoading = false
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ChallengeCard: View {
|
|
let challenge: ConnectionChallenge
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: CloserSpacing.sm) {
|
|
HStack {
|
|
Text(challenge.title)
|
|
.font(CloserFont.headline)
|
|
.foregroundColor(.closerText)
|
|
Spacer()
|
|
if challenge.isPremium {
|
|
PremiumBadge()
|
|
}
|
|
}
|
|
Text(challenge.description)
|
|
.font(CloserFont.callout)
|
|
.foregroundColor(.closerTextSecondary)
|
|
HStack {
|
|
Image(systemName: "calendar")
|
|
Text("\(challenge.durationDays) days")
|
|
}
|
|
.font(CloserFont.caption)
|
|
.foregroundColor(.closerTextSecondary)
|
|
}
|
|
.padding(CloserSpacing.md)
|
|
.closerCard()
|
|
}
|
|
}
|
|
|
|
// MARK: - Memory Lane
|
|
|
|
struct MemoryLaneView: View {
|
|
@State private var capsules: [MemoryCapsule] = []
|
|
@State private var isLoading = true
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: CloserSpacing.lg) {
|
|
Text("Memory Lane")
|
|
.font(CloserFont.title1)
|
|
.foregroundColor(.closerText)
|
|
.closerPadding()
|
|
|
|
if isLoading {
|
|
LoadingView(message: "Loading memories...")
|
|
} else if capsules.isEmpty {
|
|
EmptyStateView(
|
|
icon: "clock.fill",
|
|
title: "No Memories Yet",
|
|
message: "Create time capsules to unlock memories in the future."
|
|
)
|
|
} else {
|
|
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: CloserSpacing.md) {
|
|
ForEach(capsules) { capsule in
|
|
CapsuleCard(capsule: capsule)
|
|
}
|
|
}
|
|
.closerPadding()
|
|
}
|
|
}
|
|
.padding(.vertical)
|
|
}
|
|
.background(Color.closerBackground)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.task {
|
|
try? await Task.sleep(nanoseconds: 500_000_000)
|
|
isLoading = false
|
|
}
|
|
}
|
|
}
|
|
|
|
struct CapsuleCard: View {
|
|
let capsule: MemoryCapsule
|
|
|
|
var body: some View {
|
|
VStack(spacing: CloserSpacing.sm) {
|
|
Image(systemName: capsule.status == "unlocked" ? "envelope.open.fill" : "envelope.fill")
|
|
.font(.title)
|
|
.foregroundColor(capsule.status == "unlocked" ? .closerPrimary : .closerTextSecondary)
|
|
|
|
Text(capsule.title)
|
|
.font(CloserFont.headline)
|
|
.foregroundColor(.closerText)
|
|
.lineLimit(2)
|
|
|
|
if capsule.status == "sealed" {
|
|
Text("Unlocks \(capsule.unlockAt, style: .date)")
|
|
.font(CloserFont.caption)
|
|
.foregroundColor(.closerTextSecondary)
|
|
}
|
|
|
|
if capsule.status == "unlocked" {
|
|
Text("Open")
|
|
.font(CloserFont.caption)
|
|
.foregroundColor(.closerSuccess)
|
|
}
|
|
}
|
|
.padding(CloserSpacing.md)
|
|
.frame(maxWidth: .infinity)
|
|
.closerCard()
|
|
}
|
|
}
|
|
|
|
// MARK: - Waiting for Partner
|
|
|
|
struct WaitingForPartnerView: View {
|
|
let gameName: String
|
|
|
|
var body: some View {
|
|
VStack(spacing: CloserSpacing.xxl) {
|
|
Image(systemName: "hourglass")
|
|
.font(.system(size: 64))
|
|
.foregroundColor(.closerPrimary)
|
|
|
|
Text("Waiting for \(gameName)")
|
|
.font(CloserFont.title2)
|
|
.foregroundColor(.closerText)
|
|
|
|
Text("Your partner hasn't finished this activity yet. Results will appear here once they do.")
|
|
.font(CloserFont.callout)
|
|
.foregroundColor(.closerTextSecondary)
|
|
.multilineTextAlignment(.center)
|
|
|
|
ProgressView()
|
|
.tint(.closerPrimary)
|
|
.scaleEffect(1.5)
|
|
}
|
|
.closerPadding()
|
|
.background(Color.closerBackground)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
}
|
|
|
|
// MARK: - Replay Views
|
|
|
|
struct ThisOrThatReplayView: View {
|
|
let sessionId: String
|
|
|
|
var body: some View {
|
|
WaitingForPartnerView(gameName: "This or That")
|
|
.navigationTitle("Results")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
}
|
|
|
|
struct HowWellReplayView: View {
|
|
let sessionId: String
|
|
|
|
var body: some View {
|
|
WaitingForPartnerView(gameName: "How Well Do You Know Me")
|
|
.navigationTitle("Results")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
}
|
|
|
|
struct DesireSyncReplayView: View {
|
|
let sessionId: String
|
|
|
|
var body: some View {
|
|
WaitingForPartnerView(gameName: "Desire Sync")
|
|
.navigationTitle("Results")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
}
|
|
|
|
// MARK: - Helper Extensions
|
|
|
|
extension String {
|
|
func replacing(_ target: String, with replacement: String) -> String {
|
|
replacingOccurrences(of: target, with: replacement)
|
|
}
|
|
}
|