Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
79d69a5495
commit
0b71f5ea65
@ -3,7 +3,6 @@ import Foundation
|
|||||||
enum AppTab: String, CaseIterable, Hashable, Identifiable {
|
enum AppTab: String, CaseIterable, Hashable, Identifiable {
|
||||||
case cards
|
case cards
|
||||||
case contacts
|
case contacts
|
||||||
case widgets
|
|
||||||
case settings
|
case settings
|
||||||
|
|
||||||
var id: String { rawValue }
|
var id: String { rawValue }
|
||||||
|
|||||||
@ -590,6 +590,10 @@
|
|||||||
},
|
},
|
||||||
"Scheduling" : {
|
"Scheduling" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Select a business card from the Cards tab to preview widget layouts." : {
|
||||||
|
"comment" : "A message displayed when no business card is selected to preview widget layouts.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Select a color" : {
|
"Select a color" : {
|
||||||
|
|
||||||
@ -621,6 +625,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Share using widgets on your phone or watch" : {
|
"Share using widgets on your phone or watch" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -665,6 +670,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Share your card quickly from your home screen and your watch face." : {
|
||||||
|
"comment" : "A description of how users can share their business cards on their iPhone and watch faces.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Shared With" : {
|
"Shared With" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
@ -916,6 +925,10 @@
|
|||||||
},
|
},
|
||||||
"Website URL" : {
|
"Website URL" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Widgets on iPhone and Watch" : {
|
||||||
|
"comment" : "A title for a view that showcases widgets for iPhone and watch faces.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Work" : {
|
"Work" : {
|
||||||
|
|
||||||
|
|||||||
@ -28,10 +28,6 @@ struct RootTabView: View {
|
|||||||
ContactsView()
|
ContactsView()
|
||||||
}
|
}
|
||||||
|
|
||||||
Tab(String.localized("Widgets"), systemImage: "square.grid.2x2", value: AppTab.widgets) {
|
|
||||||
WidgetsView()
|
|
||||||
}
|
|
||||||
|
|
||||||
Tab(String.localized("Settings"), systemImage: "gearshape", value: AppTab.settings) {
|
Tab(String.localized("Settings"), systemImage: "gearshape", value: AppTab.settings) {
|
||||||
SettingsView()
|
SettingsView()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -128,6 +128,16 @@ struct SettingsView: View {
|
|||||||
.foregroundStyle(Color.AppText.secondary)
|
.foregroundStyle(Color.AppText.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsDivider(color: AppBorder.subtle)
|
||||||
|
|
||||||
|
SettingsNavigationRow(
|
||||||
|
title: String.localized("Widgets"),
|
||||||
|
subtitle: "Phone and watch preview",
|
||||||
|
backgroundColor: .clear
|
||||||
|
) {
|
||||||
|
WidgetsView()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,24 +6,31 @@ struct WidgetsView: View {
|
|||||||
@Environment(AppState.self) private var appState
|
@Environment(AppState.self) private var appState
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
ZStack {
|
||||||
|
LinearGradient(
|
||||||
|
colors: [Color.AppBackground.base, Color.AppBackground.accent],
|
||||||
|
startPoint: .topLeading,
|
||||||
|
endPoint: .bottomTrailing
|
||||||
|
)
|
||||||
|
.ignoresSafeArea()
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(spacing: Design.Spacing.large) {
|
VStack(spacing: Design.Spacing.large) {
|
||||||
Text("Share using widgets on your phone or watch")
|
WidgetsHeroCard()
|
||||||
.typography(.title2)
|
|
||||||
.bold()
|
|
||||||
.foregroundStyle(Color.Text.primary)
|
|
||||||
|
|
||||||
if let card = appState.cardStore.selectedCard {
|
if let card = appState.cardStore.selectedCard {
|
||||||
WidgetPreviewCardView(card: card)
|
WidgetPreviewCardView(card: card)
|
||||||
|
} else {
|
||||||
|
WidgetsEmptyStateCard()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, Design.Spacing.large)
|
.padding(.horizontal, Design.Spacing.large)
|
||||||
.padding(.vertical, Design.Spacing.xLarge)
|
.padding(.vertical, Design.Spacing.xLarge)
|
||||||
}
|
}
|
||||||
.background(Color.AppBackground.base)
|
.scrollIndicators(.hidden)
|
||||||
.navigationTitle(String.localized("Widgets"))
|
|
||||||
}
|
}
|
||||||
|
.navigationTitle(String.localized("Widgets"))
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,66 +45,109 @@ private struct WidgetPreviewCardView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct WidgetsHeroCard: View {
|
||||||
|
var body: some View {
|
||||||
|
WidgetSurfaceCard {
|
||||||
|
Label("Widgets on iPhone and Watch", systemImage: "square.grid.2x2.fill")
|
||||||
|
.styled(.headingEmphasis)
|
||||||
|
|
||||||
|
Text("Share your card quickly from your home screen and your watch face.")
|
||||||
|
.styled(.subheading, emphasis: .secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct WidgetsEmptyStateCard: View {
|
||||||
|
var body: some View {
|
||||||
|
WidgetSurfaceCard {
|
||||||
|
Label("No card selected", systemImage: "person.crop.rectangle")
|
||||||
|
.styled(.headingEmphasis)
|
||||||
|
|
||||||
|
Text("Select a business card from the Cards tab to preview widget layouts.")
|
||||||
|
.styled(.subheading, emphasis: .secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private struct PhoneWidgetPreview: View {
|
private struct PhoneWidgetPreview: View {
|
||||||
let card: BusinessCard
|
let card: BusinessCard
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
WidgetSurfaceCard {
|
||||||
Text("Phone Widget")
|
Label("Phone Widget", systemImage: "iphone")
|
||||||
.typography(.heading)
|
.styled(.headingEmphasis)
|
||||||
.bold()
|
|
||||||
.foregroundStyle(Color.Text.primary)
|
|
||||||
|
|
||||||
HStack(spacing: Design.Spacing.medium) {
|
HStack(spacing: Design.Spacing.medium) {
|
||||||
QRCodeView(payload: card.vCardPayload)
|
QRCodeView(payload: card.vCardPayload)
|
||||||
.frame(width: Design.CardSize.widgetPhoneHeight, height: Design.CardSize.widgetPhoneHeight)
|
.frame(width: Design.CardSize.widgetPhoneHeight, height: Design.CardSize.widgetPhoneHeight)
|
||||||
|
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
||||||
Text(card.fullName)
|
Text(card.fullName)
|
||||||
.typography(.heading)
|
.styled(.subheadingEmphasis)
|
||||||
.foregroundStyle(Color.Text.primary)
|
.lineLimit(2)
|
||||||
Text(card.role)
|
Text(card.role)
|
||||||
.typography(.subheading)
|
.styled(.caption, emphasis: .secondary)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
.lineLimit(1)
|
||||||
Text("Tap to share")
|
Text("Tap to share")
|
||||||
.typography(.caption)
|
.styled(.caption2, emphasis: .tertiary)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(Design.Spacing.large)
|
|
||||||
.background(Color.AppBackground.elevated)
|
|
||||||
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct WatchWidgetPreview: View {
|
private struct WatchWidgetPreview: View {
|
||||||
let card: BusinessCard
|
let card: BusinessCard
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
WidgetSurfaceCard {
|
||||||
Text("Watch Widget")
|
Label("Watch Widget", systemImage: "applewatch")
|
||||||
.typography(.heading)
|
.styled(.headingEmphasis)
|
||||||
.bold()
|
|
||||||
.foregroundStyle(Color.Text.primary)
|
|
||||||
|
|
||||||
HStack(spacing: Design.Spacing.medium) {
|
HStack(spacing: Design.Spacing.medium) {
|
||||||
QRCodeView(payload: card.vCardPayload)
|
QRCodeView(payload: card.vCardPayload)
|
||||||
.frame(width: Design.CardSize.widgetWatchSize, height: Design.CardSize.widgetWatchSize)
|
.frame(width: Design.CardSize.widgetWatchSize, height: Design.CardSize.widgetWatchSize)
|
||||||
|
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
||||||
Text("Ready to scan")
|
Text("Ready to scan")
|
||||||
.typography(.subheading)
|
.styled(.subheadingEmphasis, emphasis: .secondary)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
|
||||||
Text("Open on Apple Watch")
|
Text("Open on Apple Watch")
|
||||||
.typography(.caption)
|
.styled(.caption, emphasis: .tertiary)
|
||||||
.foregroundStyle(Color.Text.secondary)
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct WidgetSurfaceCard<Content: View>: View {
|
||||||
|
let content: Content
|
||||||
|
|
||||||
|
init(@ViewBuilder content: () -> Content) {
|
||||||
|
self.content = content()
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
||||||
|
content
|
||||||
|
}
|
||||||
.padding(Design.Spacing.large)
|
.padding(Design.Spacing.large)
|
||||||
.background(Color.AppBackground.elevated)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.background(Color.AppBackground.secondary.opacity(Design.Opacity.almostFull))
|
||||||
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
|
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
|
||||||
|
.overlay {
|
||||||
|
RoundedRectangle(cornerRadius: Design.CornerRadius.large)
|
||||||
|
.strokeBorder(AppBorder.subtle, lineWidth: Design.LineWidth.thin)
|
||||||
|
}
|
||||||
|
.shadow(
|
||||||
|
color: Color.AppText.tertiary.opacity(Design.Opacity.subtle),
|
||||||
|
radius: Design.Shadow.radiusSmall,
|
||||||
|
x: Design.Shadow.offsetNone,
|
||||||
|
y: Design.Shadow.offsetSmall
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user