142 lines
4.9 KiB
Swift
142 lines
4.9 KiB
Swift
import SwiftUI
|
|
|
|
struct WatchContentView: View {
|
|
@Environment(WatchCardStore.self) private var cardStore
|
|
@State private var selectedCardID: UUID?
|
|
|
|
private var displayCards: [WatchCard] {
|
|
cardStore.cards
|
|
}
|
|
|
|
private var defaultSelection: UUID? {
|
|
cardStore.defaultCardID ?? displayCards.first?.id
|
|
}
|
|
|
|
var body: some View {
|
|
if displayCards.isEmpty {
|
|
WatchEmptyStateView()
|
|
} else if displayCards.count == 1 {
|
|
WatchQRCodeCardView(card: displayCards[0])
|
|
} else {
|
|
TabView(selection: Binding(
|
|
get: { selectedCardID ?? defaultSelection ?? displayCards[0].id },
|
|
set: { selectedCardID = $0 }
|
|
)) {
|
|
ForEach(displayCards) { card in
|
|
WatchQRCodeCardView(card: card)
|
|
.tag(card.id)
|
|
}
|
|
}
|
|
.tabViewStyle(.page(indexDisplayMode: .never))
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct WatchEmptyStateView: View {
|
|
var body: some View {
|
|
VStack(spacing: WatchDesign.Spacing.medium) {
|
|
Image(systemName: "rectangle.stack")
|
|
.font(.title)
|
|
.foregroundStyle(Color.WatchPalette.muted)
|
|
|
|
Text("No Cards")
|
|
.font(.headline)
|
|
.foregroundStyle(Color.WatchPalette.text)
|
|
|
|
Text("Open the iPhone app to create cards")
|
|
.font(.caption)
|
|
.foregroundStyle(Color.WatchPalette.muted)
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
.padding(WatchDesign.Spacing.large)
|
|
}
|
|
}
|
|
|
|
private enum WatchQRMode: String, CaseIterable {
|
|
case vCard = "vCard"
|
|
case appClip = "App Clip"
|
|
}
|
|
|
|
private struct WatchQRCodeCardView: View {
|
|
let card: WatchCard
|
|
@State private var selectedQRIndex = 0
|
|
|
|
private var hasAppClipQR: Bool { card.appClipQRCodeImage != nil }
|
|
private var qrPages: [(WatchQRMode, Image?)] {
|
|
var pages: [(WatchQRMode, Image?)] = [(.vCard, card.qrCodeImage)]
|
|
if hasAppClipQR {
|
|
pages.append((.appClip, card.appClipQRCodeImage))
|
|
}
|
|
return pages
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(spacing: WatchDesign.Spacing.small) {
|
|
if qrPages.count > 1 {
|
|
// Swipeable QR codes (vCard ↔ App Clip) - no buttons, just swipe
|
|
TabView(selection: $selectedQRIndex) {
|
|
ForEach(Array(qrPages.enumerated()), id: \.offset) { index, item in
|
|
qrCodeContent(image: item.1)
|
|
.tag(index)
|
|
}
|
|
}
|
|
.tabViewStyle(.page(indexDisplayMode: .never))
|
|
.frame(height: WatchDesign.Size.qrSize + WatchDesign.Spacing.medium * 2)
|
|
} else {
|
|
// Single QR code
|
|
qrCodeContent(image: card.qrCodeImage ?? card.appClipQRCodeImage)
|
|
}
|
|
|
|
VStack(spacing: WatchDesign.Spacing.extraSmall) {
|
|
Text(card.fullName)
|
|
.font(.headline)
|
|
.foregroundStyle(Color.WatchPalette.text)
|
|
.lineLimit(1)
|
|
|
|
Text(card.role)
|
|
.font(.caption)
|
|
.foregroundStyle(Color.WatchPalette.muted)
|
|
.lineLimit(1)
|
|
}
|
|
}
|
|
.padding(WatchDesign.Spacing.medium)
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
.background(Color.WatchPalette.background)
|
|
.accessibilityElement(children: .ignore)
|
|
.accessibilityLabel(String(localized: "QR code"))
|
|
.accessibilityValue("\(card.fullName), \(card.role)")
|
|
.accessibilityHint(hasAppClipQR ? String(localized: "Swipe left or right to switch QR code type") : "")
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func qrCodeContent(image: Image?) -> some View {
|
|
if let image {
|
|
image
|
|
.resizable()
|
|
.interpolation(.none)
|
|
.scaledToFit()
|
|
.frame(width: WatchDesign.Size.qrSize, height: WatchDesign.Size.qrSize)
|
|
.padding(WatchDesign.Spacing.small)
|
|
.background(Color.WatchPalette.card)
|
|
.clipShape(.rect(cornerRadius: WatchDesign.CornerRadius.large))
|
|
} else {
|
|
VStack(spacing: WatchDesign.Spacing.small) {
|
|
Image(systemName: "qrcode")
|
|
.font(.largeTitle)
|
|
.foregroundStyle(Color.WatchPalette.muted)
|
|
Text("Sync from iPhone")
|
|
.font(.caption2)
|
|
.foregroundStyle(Color.WatchPalette.muted)
|
|
}
|
|
.frame(width: WatchDesign.Size.qrSize, height: WatchDesign.Size.qrSize)
|
|
.background(Color.WatchPalette.card)
|
|
.clipShape(.rect(cornerRadius: WatchDesign.CornerRadius.large))
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
WatchContentView()
|
|
.environment(WatchCardStore())
|
|
}
|