Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
@ -33,6 +33,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"+1 555 123 4567" : {
|
||||
|
||||
},
|
||||
"Add a QR widget so your card is always one tap away." : {
|
||||
"localizations" : {
|
||||
@ -153,6 +156,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Example" : {
|
||||
|
||||
},
|
||||
"Hold your phone near another device to share instantly. NFC setup is on the way." : {
|
||||
"localizations" : {
|
||||
@ -350,6 +356,9 @@
|
||||
},
|
||||
"Shared With" : {
|
||||
|
||||
},
|
||||
"Tap \"New Card\" to create your first card" : {
|
||||
|
||||
},
|
||||
"Tap to share" : {
|
||||
"localizations" : {
|
||||
@ -466,6 +475,21 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Your card will appear here" : {
|
||||
|
||||
},
|
||||
"Your Company" : {
|
||||
|
||||
},
|
||||
"Your Name" : {
|
||||
|
||||
},
|
||||
"Your Role" : {
|
||||
|
||||
},
|
||||
"your@email.com" : {
|
||||
|
||||
}
|
||||
},
|
||||
"version" : "1.1"
|
||||
|
||||
@ -12,13 +12,6 @@ final class CardStore: BusinessCardProviding {
|
||||
init(modelContext: ModelContext) {
|
||||
self.modelContext = modelContext
|
||||
fetchCards()
|
||||
|
||||
if cards.isEmpty {
|
||||
BusinessCard.createSamples(in: modelContext)
|
||||
saveContext()
|
||||
fetchCards()
|
||||
}
|
||||
|
||||
self.selectedCardID = cards.first(where: { $0.isDefault })?.id ?? cards.first?.id
|
||||
syncToWatch()
|
||||
}
|
||||
|
||||
@ -12,12 +12,6 @@ final class ContactsStore: ContactTracking {
|
||||
init(modelContext: ModelContext) {
|
||||
self.modelContext = modelContext
|
||||
fetchContacts()
|
||||
|
||||
if contacts.isEmpty {
|
||||
Contact.createSamples(in: modelContext)
|
||||
saveContext()
|
||||
fetchContacts()
|
||||
}
|
||||
}
|
||||
|
||||
func fetchContacts() {
|
||||
|
||||
@ -7,15 +7,18 @@ struct CardCarouselView: View {
|
||||
|
||||
var body: some View {
|
||||
@Bindable var cardStore = appState.cardStore
|
||||
let hasCards = !cardStore.cards.isEmpty
|
||||
|
||||
VStack(spacing: Design.Spacing.medium) {
|
||||
HStack {
|
||||
Text("Create multiple business cards")
|
||||
Text(hasCards ? "Create multiple business cards" : "Your card will appear here")
|
||||
.font(.headline)
|
||||
.bold()
|
||||
.foregroundStyle(Color.Text.primary)
|
||||
Spacer()
|
||||
}
|
||||
|
||||
if hasCards {
|
||||
TabView(selection: $cardStore.selectedCardID) {
|
||||
ForEach(cardStore.cards) { card in
|
||||
BusinessCardView(card: card)
|
||||
@ -31,6 +34,10 @@ struct CardCarouselView: View {
|
||||
cardStore.setDefaultCard(selected)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DemoCardView()
|
||||
.padding(.vertical, Design.Spacing.medium)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -51,6 +58,93 @@ private struct CardDefaultToggleView: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// A demo card shown when the user has no cards yet.
|
||||
/// Displays placeholder content with an "Example" badge to prompt card creation.
|
||||
private struct DemoCardView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: Design.Spacing.medium) {
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
||||
HStack(spacing: Design.Spacing.medium) {
|
||||
Circle()
|
||||
.fill(Color.AppText.inverted)
|
||||
.frame(width: Design.CardSize.avatarSize, height: Design.CardSize.avatarSize)
|
||||
.overlay(
|
||||
Image(systemName: "person.crop.circle")
|
||||
.foregroundStyle(Color.CardPalette.coral)
|
||||
)
|
||||
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||
Text("Your Name")
|
||||
.font(.headline)
|
||||
.bold()
|
||||
.foregroundStyle(Color.AppText.inverted)
|
||||
Text("Your Role")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(Color.AppText.inverted.opacity(Design.Opacity.almostFull))
|
||||
Text("Your Company")
|
||||
.font(.caption)
|
||||
.foregroundStyle(Color.AppText.inverted.opacity(Design.Opacity.medium))
|
||||
}
|
||||
|
||||
Spacer(minLength: Design.Spacing.small)
|
||||
|
||||
Text("Example")
|
||||
.font(.caption)
|
||||
.bold()
|
||||
.foregroundStyle(Color.AppText.inverted)
|
||||
.padding(.horizontal, Design.Spacing.small)
|
||||
.padding(.vertical, Design.Spacing.xxSmall)
|
||||
.background(Color.AppText.inverted.opacity(Design.Opacity.hint))
|
||||
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
|
||||
}
|
||||
|
||||
Divider()
|
||||
.overlay(Color.AppText.inverted.opacity(Design.Opacity.medium))
|
||||
|
||||
HStack(spacing: Design.Spacing.xSmall) {
|
||||
Image(systemName: "envelope")
|
||||
.font(.caption)
|
||||
Text("your@email.com")
|
||||
.font(.caption)
|
||||
}
|
||||
.foregroundStyle(Color.AppText.inverted.opacity(Design.Opacity.heavy))
|
||||
|
||||
HStack(spacing: Design.Spacing.xSmall) {
|
||||
Image(systemName: "phone")
|
||||
.font(.caption)
|
||||
Text("+1 555 123 4567")
|
||||
.font(.caption)
|
||||
}
|
||||
.foregroundStyle(Color.AppText.inverted.opacity(Design.Opacity.heavy))
|
||||
}
|
||||
.padding(Design.Spacing.large)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(
|
||||
LinearGradient(
|
||||
colors: [Color.CardPalette.coral, Color.CardPalette.sand],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
)
|
||||
.clipShape(.rect(cornerRadius: Design.CornerRadius.xLarge))
|
||||
.shadow(
|
||||
color: Color.AppText.secondary.opacity(Design.Opacity.hint),
|
||||
radius: Design.Shadow.radiusLarge,
|
||||
x: Design.Shadow.offsetNone,
|
||||
y: Design.Shadow.offsetMedium
|
||||
)
|
||||
.opacity(Design.Opacity.strong)
|
||||
|
||||
Text("Tap \"New Card\" to create your first card")
|
||||
.font(.caption)
|
||||
.foregroundStyle(Color.AppText.secondary)
|
||||
}
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel(String.localized("Example business card"))
|
||||
.accessibilityHint(String.localized("Create a new card to replace this example"))
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
CardCarouselView()
|
||||
.environment(AppState(modelContext: try! ModelContainer(for: BusinessCard.self, Contact.self).mainContext))
|
||||
|
||||
@ -18,6 +18,14 @@ struct CardsHomeView: View {
|
||||
CardCarouselView()
|
||||
|
||||
HStack(spacing: Design.Spacing.medium) {
|
||||
if appState.cardStore.cards.isEmpty {
|
||||
PrimaryActionButton(
|
||||
title: String.localized("Create Card"),
|
||||
systemImage: "plus"
|
||||
) {
|
||||
showingCreateCard = true
|
||||
}
|
||||
} else {
|
||||
PrimaryActionButton(
|
||||
title: String.localized("Send my card"),
|
||||
systemImage: "paperplane.fill"
|
||||
@ -33,6 +41,7 @@ struct CardsHomeView: View {
|
||||
.controlSize(.large)
|
||||
.accessibilityHint(String.localized("Create a new business card"))
|
||||
}
|
||||
}
|
||||
|
||||
WidgetsCalloutView()
|
||||
}
|
||||
|
||||
@ -137,7 +137,6 @@ BusinessCardTests/ # Unit tests
|
||||
- Share URLs are sample placeholders
|
||||
- Wallet/NFC flows are stubs with alerts only
|
||||
- Widget UI is a visual preview (not a WidgetKit extension)
|
||||
- First launch creates sample cards for demonstration
|
||||
|
||||
## Running
|
||||
|
||||
|
||||
BIN
_design/screenshots/app1/Screenshot 2026-01-08 at 6.50.37 PM.png
Normal file
|
After Width: | Height: | Size: 348 KiB |
BIN
_design/screenshots/app1/Screenshot 2026-01-08 at 6.50.59 PM.png
Normal file
|
After Width: | Height: | Size: 268 KiB |
BIN
_design/screenshots/app1/Screenshot 2026-01-08 at 6.51.14 PM.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0849.jpeg
Normal file
|
After Width: | Height: | Size: 165 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0850.jpeg
Normal file
|
After Width: | Height: | Size: 211 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0851.jpeg
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0852.jpeg
Normal file
|
After Width: | Height: | Size: 180 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0853.jpeg
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0854.jpeg
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0855.jpeg
Normal file
|
After Width: | Height: | Size: 161 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0856.jpeg
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0857.jpeg
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0858.jpeg
Normal file
|
After Width: | Height: | Size: 171 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0859.jpeg
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0860.jpeg
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0861.jpeg
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0862.jpeg
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0863.jpeg
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0864.jpeg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0865.jpeg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0866.jpeg
Normal file
|
After Width: | Height: | Size: 157 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0867.jpeg
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0868.jpeg
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0869.jpeg
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0870.jpeg
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0871.jpeg
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0872.jpeg
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0873.jpeg
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0874.jpeg
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0875.jpeg
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0876.jpeg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0878.jpeg
Normal file
|
After Width: | Height: | Size: 157 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0879.jpeg
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0880.jpeg
Normal file
|
After Width: | Height: | Size: 168 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0881.jpeg
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0882.jpeg
Normal file
|
After Width: | Height: | Size: 121 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0883.jpeg
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0884.jpeg
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0885.jpeg
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0901.jpeg
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0902.jpeg
Normal file
|
After Width: | Height: | Size: 176 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0903.jpeg
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0904.jpeg
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0905.jpeg
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0906.jpeg
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0907.jpeg
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0908.jpeg
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0909.jpeg
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0910.jpeg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
_design/screenshots/app2/add-edit-business-card/IMG_0911.jpeg
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
_design/screenshots/app2/add-edit-contact/IMG_0889.jpeg
Normal file
|
After Width: | Height: | Size: 183 KiB |
BIN
_design/screenshots/app2/add-edit-contact/IMG_0890.jpeg
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
_design/screenshots/app2/add-edit-contact/IMG_0891.jpeg
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
_design/screenshots/app2/add-edit-contact/IMG_0892.jpeg
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
_design/screenshots/app2/add-edit-contact/IMG_0893.jpeg
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
_design/screenshots/app2/add-edit-contact/IMG_0894.jpeg
Normal file
|
After Width: | Height: | Size: 145 KiB |
BIN
_design/screenshots/app2/inapp/IMG_0846.jpeg
Normal file
|
After Width: | Height: | Size: 184 KiB |
BIN
_design/screenshots/app2/inapp/IMG_0847.jpeg
Normal file
|
After Width: | Height: | Size: 200 KiB |
BIN
_design/screenshots/app2/inapp/IMG_0848.jpeg
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
_design/screenshots/app2/inapp/IMG_0887.jpeg
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
_design/screenshots/app2/inapp/IMG_0896.jpeg
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
_design/screenshots/app2/nfc/IMG_0900.jpeg
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
_design/screenshots/app2/onboarding/IMG_0830.jpeg
Normal file
|
After Width: | Height: | Size: 178 KiB |
BIN
_design/screenshots/app2/onboarding/IMG_0831.jpeg
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
_design/screenshots/app2/onboarding/IMG_0832.jpeg
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
_design/screenshots/app2/onboarding/IMG_0833.jpeg
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
_design/screenshots/app2/onboarding/IMG_0834.jpeg
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
_design/screenshots/app2/onboarding/IMG_0835.jpeg
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
_design/screenshots/app2/onboarding/IMG_0836.jpeg
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
_design/screenshots/app2/onboarding/IMG_0837.jpeg
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
_design/screenshots/app2/onboarding/IMG_0838.jpeg
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
_design/screenshots/app2/onboarding/IMG_0839.jpeg
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
_design/screenshots/app2/onboarding/IMG_0842.jpeg
Normal file
|
After Width: | Height: | Size: 241 KiB |
BIN
_design/screenshots/app2/onboarding/IMG_0844.jpeg
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
_design/screenshots/app2/onboarding/IMG_0845.jpeg
Normal file
|
After Width: | Height: | Size: 225 KiB |
BIN
_design/screenshots/app2/widget/IMG_0843.jpeg
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
_design/screenshots/app2/widget/IMG_0877.jpeg
Normal file
|
After Width: | Height: | Size: 315 KiB |
BIN
_design/screenshots/app2/widget/IMG_0899.jpeg
Normal file
|
After Width: | Height: | Size: 206 KiB |
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 90 KiB |