Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
44724914b4
commit
6b10f8b398
@ -629,6 +629,7 @@
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
"INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@ -665,6 +666,7 @@
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
"INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@ -851,6 +853,7 @@
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
"INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -888,6 +891,7 @@
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
"INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
||||
@ -7,12 +7,12 @@
|
||||
<key>BusinessCard.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>BusinessCardClip.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<key>BusinessCardWatch Watch App.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
||||
BIN
BusinessCard/Assets.xcassets/AppIcon.appiconset/AppIcon.png
Normal file
BIN
BusinessCard/Assets.xcassets/AppIcon.appiconset/AppIcon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
@ -1,6 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.278",
|
||||
"green" : "0.329",
|
||||
"red" : "0.949"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.278",
|
||||
"green" : "0.329",
|
||||
"red" : "0.949"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -72,9 +72,17 @@ struct BusinessCardApp: App {
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
RootTabView()
|
||||
.environment(appState)
|
||||
.preferredColorScheme(.light)
|
||||
ZStack {
|
||||
// Base background matching launch screen - prevents white flash
|
||||
Color.Branding.primary
|
||||
.ignoresSafeArea()
|
||||
|
||||
AppLaunchView(config: .businessCard) {
|
||||
RootTabView()
|
||||
.environment(appState)
|
||||
.preferredColorScheme(.light)
|
||||
}
|
||||
}
|
||||
}
|
||||
.modelContainer(modelContainer)
|
||||
}
|
||||
|
||||
63
BusinessCard/Design/BrandingConfig.swift
Normal file
63
BusinessCard/Design/BrandingConfig.swift
Normal file
@ -0,0 +1,63 @@
|
||||
//
|
||||
// BrandingConfig.swift
|
||||
// BusinessCard
|
||||
//
|
||||
// App-specific branding configurations for icons and launch screens.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Bedrock
|
||||
|
||||
// MARK: - App Branding Colors
|
||||
|
||||
extension Color {
|
||||
/// BusinessCard branding colors for icon and launch screen.
|
||||
enum Branding {
|
||||
/// Primary gradient color (warm coral/red).
|
||||
/// Must match LaunchBackground.colorset exactly to prevent flash.
|
||||
static let primary = Color(red: 0.949, green: 0.329, blue: 0.278)
|
||||
|
||||
/// Secondary gradient color (darker red).
|
||||
static let secondary = Color(red: 0.65, green: 0.18, blue: 0.15)
|
||||
|
||||
/// Accent color for icons and highlights.
|
||||
static let accent = Color.white
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - App Icon Configuration
|
||||
|
||||
extension AppIconConfig {
|
||||
/// BusinessCard app icon configuration.
|
||||
static let businessCard = AppIconConfig(
|
||||
title: "CARDS",
|
||||
subtitle: nil,
|
||||
iconSymbol: "person.text.rectangle",
|
||||
primaryColor: Color.Branding.primary,
|
||||
secondaryColor: Color.Branding.secondary,
|
||||
accentColor: Color.Branding.accent
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Launch Screen Configuration
|
||||
|
||||
extension LaunchScreenConfig {
|
||||
/// BusinessCard launch screen configuration.
|
||||
static let businessCard = LaunchScreenConfig(
|
||||
title: "BUSINESS CARD",
|
||||
tagline: "Share Your Professional Identity",
|
||||
iconSymbols: ["person.text.rectangle"],
|
||||
cornerSymbol: nil,
|
||||
decorativeSymbol: "circle.fill",
|
||||
patternStyle: .dots,
|
||||
layoutStyle: .iconAboveTitle,
|
||||
primaryColor: Color.Branding.primary,
|
||||
secondaryColor: Color.Branding.secondary,
|
||||
accentColor: Color.Branding.accent,
|
||||
titleColor: .white,
|
||||
iconSize: 52,
|
||||
titleSize: 32,
|
||||
iconSpacing: 12,
|
||||
animationDuration: 0.6
|
||||
)
|
||||
}
|
||||
136
BusinessCard/Design/BusinessCardTheme.swift
Normal file
136
BusinessCard/Design/BusinessCardTheme.swift
Normal file
@ -0,0 +1,136 @@
|
||||
//
|
||||
// BusinessCardTheme.swift
|
||||
// BusinessCard
|
||||
//
|
||||
// App-specific theme conforming to Bedrock's color protocols.
|
||||
// This light theme uses warm, professional tones.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Bedrock
|
||||
|
||||
// MARK: - Surface Colors
|
||||
|
||||
/// Surface colors with warm off-white tones for a professional light theme.
|
||||
public enum BusinessCardSurfaceColors: SurfaceColorProvider {
|
||||
/// Primary background - warm off-white base
|
||||
public static let primary = Color(red: 0.97, green: 0.96, blue: 0.94)
|
||||
|
||||
/// Secondary/elevated surface
|
||||
public static let secondary = Color(red: 0.95, green: 0.95, blue: 0.95)
|
||||
|
||||
/// Tertiary/card surface - most elevated
|
||||
public static let tertiary = Color(red: 1.0, green: 1.0, blue: 1.0)
|
||||
|
||||
/// Overlay background (for sheets/modals)
|
||||
public static let overlay = Color(red: 0.97, green: 0.96, blue: 0.94)
|
||||
|
||||
/// Card/grouped element background
|
||||
public static let card = Color(red: 1.0, green: 1.0, blue: 1.0)
|
||||
|
||||
/// Subtle fill for grouped content sections
|
||||
public static let groupedFill = Color(red: 0.95, green: 0.94, blue: 0.92)
|
||||
|
||||
/// Section fill for list sections
|
||||
public static let sectionFill = Color(red: 0.93, green: 0.92, blue: 0.90)
|
||||
}
|
||||
|
||||
// MARK: - Text Colors
|
||||
|
||||
public enum BusinessCardTextColors: TextColorProvider {
|
||||
public static let primary = Color(red: 0.14, green: 0.14, blue: 0.17)
|
||||
public static let secondary = Color(red: 0.32, green: 0.34, blue: 0.40)
|
||||
public static let tertiary = Color(red: 0.56, green: 0.58, blue: 0.62)
|
||||
public static let disabled = Color(red: 0.70, green: 0.72, blue: 0.75)
|
||||
public static let placeholder = Color(red: 0.60, green: 0.62, blue: 0.66)
|
||||
public static let inverse = Color(red: 0.98, green: 0.98, blue: 0.98)
|
||||
}
|
||||
|
||||
// MARK: - Accent Colors
|
||||
|
||||
public enum BusinessCardAccentColors: AccentColorProvider {
|
||||
/// Primary accent - warm red
|
||||
public static let primary = Color(red: 0.95, green: 0.33, blue: 0.28)
|
||||
|
||||
/// Light variant
|
||||
public static let light = Color(red: 0.98, green: 0.50, blue: 0.45)
|
||||
|
||||
/// Dark variant
|
||||
public static let dark = Color(red: 0.75, green: 0.25, blue: 0.22)
|
||||
|
||||
/// Secondary accent - ink/dark
|
||||
public static let secondary = Color(red: 0.12, green: 0.12, blue: 0.14)
|
||||
}
|
||||
|
||||
// MARK: - Button Colors
|
||||
|
||||
public enum BusinessCardButtonColors: ButtonColorProvider {
|
||||
public static let primaryLight = Color(red: 0.98, green: 0.45, blue: 0.40)
|
||||
public static let primaryDark = Color(red: 0.85, green: 0.28, blue: 0.24)
|
||||
public static let secondary = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.subtle)
|
||||
public static let destructive = Color.red.opacity(Design.Opacity.heavy)
|
||||
public static let cancelText = Color(red: 0.32, green: 0.34, blue: 0.40)
|
||||
}
|
||||
|
||||
// MARK: - Status Colors
|
||||
|
||||
public enum BusinessCardStatusColors: StatusColorProvider {
|
||||
public static let success = Color(red: 0.2, green: 0.75, blue: 0.4)
|
||||
public static let warning = Color(red: 0.95, green: 0.75, blue: 0.25)
|
||||
public static let error = Color(red: 0.9, green: 0.3, blue: 0.3)
|
||||
public static let info = Color(red: 0.3, green: 0.6, blue: 0.9)
|
||||
}
|
||||
|
||||
// MARK: - Border Colors
|
||||
|
||||
public enum BusinessCardBorderColors: BorderColorProvider {
|
||||
public static let subtle = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.subtle)
|
||||
public static let standard = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.hint)
|
||||
public static let emphasized = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.light)
|
||||
public static let selected = BusinessCardAccentColors.primary.opacity(Design.Opacity.medium)
|
||||
}
|
||||
|
||||
// MARK: - Interactive Colors
|
||||
|
||||
public enum BusinessCardInteractiveColors: InteractiveColorProvider {
|
||||
public static let selected = BusinessCardAccentColors.primary.opacity(Design.Opacity.selection)
|
||||
public static let hover = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.subtle)
|
||||
public static let pressed = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.hint)
|
||||
public static let focus = BusinessCardAccentColors.light
|
||||
}
|
||||
|
||||
// MARK: - Combined Theme
|
||||
|
||||
/// BusinessCard's complete color theme.
|
||||
/// Note: We use the individual color provider typealiases (AppSurface, AppThemeAccent, etc.)
|
||||
/// directly in views rather than going through the theme type.
|
||||
///
|
||||
/// This enum is not used directly but documents the theme structure.
|
||||
/// The AppColorTheme conformance is omitted to avoid MainActor isolation conflicts.
|
||||
public enum BusinessCardTheme {
|
||||
public typealias Surface = BusinessCardSurfaceColors
|
||||
public typealias Text = BusinessCardTextColors
|
||||
public typealias Accent = BusinessCardAccentColors
|
||||
public typealias Button = BusinessCardButtonColors
|
||||
public typealias Status = BusinessCardStatusColors
|
||||
public typealias Border = BusinessCardBorderColors
|
||||
public typealias Interactive = BusinessCardInteractiveColors
|
||||
}
|
||||
|
||||
// MARK: - Convenience Typealiases
|
||||
|
||||
/// Short typealiases for cleaner usage throughout the app.
|
||||
/// These avoid conflicts with Bedrock's default typealiases by using unique names.
|
||||
///
|
||||
/// Usage:
|
||||
/// ```swift
|
||||
/// .background(AppSurface.primary)
|
||||
/// .foregroundStyle(AppThemeAccent.primary)
|
||||
/// ```
|
||||
typealias AppSurface = BusinessCardSurfaceColors
|
||||
typealias AppThemeText = BusinessCardTextColors
|
||||
typealias AppThemeAccent = BusinessCardAccentColors
|
||||
typealias AppButtonColors = BusinessCardButtonColors
|
||||
typealias AppStatus = BusinessCardStatusColors
|
||||
typealias AppBorder = BusinessCardBorderColors
|
||||
typealias AppInteractive = BusinessCardInteractiveColors
|
||||
@ -12,5 +12,10 @@
|
||||
<string>$(CLOUDKIT_CONTAINER_IDENTIFIER)</string>
|
||||
<key>AppClipDomain</key>
|
||||
<string>$(APPCLIP_DOMAIN)</string>
|
||||
<key>UILaunchScreen</key>
|
||||
<dict>
|
||||
<key>UIColorName</key>
|
||||
<string>LaunchBackground</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -4,6 +4,7 @@ enum AppTab: String, CaseIterable, Hashable, Identifiable {
|
||||
case cards
|
||||
case contacts
|
||||
case widgets
|
||||
case settings
|
||||
|
||||
var id: String { rawValue }
|
||||
}
|
||||
|
||||
@ -93,6 +93,10 @@
|
||||
},
|
||||
"Address" : {
|
||||
|
||||
},
|
||||
"App Clip (includes photo)" : {
|
||||
"comment" : "A title for a section that allows users to generate an App Clip link for their card, including a photo.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Apt, Suite, Unit (optional)" : {
|
||||
|
||||
@ -115,6 +119,10 @@
|
||||
},
|
||||
"Card Label" : {
|
||||
|
||||
},
|
||||
"Card not found" : {
|
||||
"comment" : "Error message when a requested shared card is not found.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Card style" : {
|
||||
"extractionState" : "stale",
|
||||
@ -197,6 +205,10 @@
|
||||
},
|
||||
"Contact" : {
|
||||
|
||||
},
|
||||
"Could not load card: %@" : {
|
||||
"comment" : "A description of an error that might occur when fetching a shared card. The argument is text describing the underlying error.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Country" : {
|
||||
|
||||
@ -232,6 +244,10 @@
|
||||
},
|
||||
"Create your first card" : {
|
||||
|
||||
},
|
||||
"Creates a link that opens a mini-app for recipients to preview and save your card with photo." : {
|
||||
"comment" : "A description of the feature that generates an App Clip link.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Custom color" : {
|
||||
|
||||
@ -297,9 +313,24 @@
|
||||
},
|
||||
"Email or Username" : {
|
||||
|
||||
},
|
||||
"Expires in 7 days" : {
|
||||
"comment" : "A notice displayed below an App Clip URL that indicates when it will expire.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Failed to create share URL" : {
|
||||
"comment" : "Error message when the share URL for a shared card cannot be created.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"First Name" : {
|
||||
|
||||
},
|
||||
"Generate App Clip Link" : {
|
||||
|
||||
},
|
||||
"Generate New Link" : {
|
||||
"comment" : "A button label that resets the generation of an App Clip URL.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Header Layout" : {
|
||||
|
||||
@ -377,6 +408,10 @@
|
||||
},
|
||||
"Maiden Name" : {
|
||||
|
||||
},
|
||||
"Matt Bruce" : {
|
||||
"comment" : "The name of the developer of the app.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Messaging" : {
|
||||
|
||||
@ -782,6 +817,10 @@
|
||||
},
|
||||
"The default card is used for sharing and widgets." : {
|
||||
|
||||
},
|
||||
"This card has expired" : {
|
||||
"comment" : "Error message indicating that the shared card has expired.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"This doesn't appear to be a business card QR code." : {
|
||||
|
||||
@ -791,6 +830,13 @@
|
||||
},
|
||||
"Title (optional)" : {
|
||||
|
||||
},
|
||||
"Upload failed: %@" : {
|
||||
"comment" : "A description of an error that might occur when uploading a shared card. The argument is text describing the underlying error.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Uploading card" : {
|
||||
|
||||
},
|
||||
"URL" : {
|
||||
|
||||
|
||||
52
BusinessCard/State/SettingsState.swift
Normal file
52
BusinessCard/State/SettingsState.swift
Normal file
@ -0,0 +1,52 @@
|
||||
//
|
||||
// SettingsState.swift
|
||||
// BusinessCard
|
||||
//
|
||||
// Observable state for app settings.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Observation
|
||||
|
||||
@Observable
|
||||
@MainActor
|
||||
final class SettingsState {
|
||||
|
||||
// MARK: - App Info
|
||||
|
||||
/// The app's display name.
|
||||
var appName: String {
|
||||
Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
|
||||
?? Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String
|
||||
?? "BusinessCard"
|
||||
}
|
||||
|
||||
/// The app's version string (e.g., "1.0.0").
|
||||
var appVersion: String {
|
||||
Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "1.0"
|
||||
}
|
||||
|
||||
/// The app's build number.
|
||||
var buildNumber: String {
|
||||
Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "1"
|
||||
}
|
||||
|
||||
/// Combined version and build string for display.
|
||||
var versionString: String {
|
||||
"Version \(appVersion) (\(buildNumber))"
|
||||
}
|
||||
|
||||
// MARK: - Debug Settings
|
||||
|
||||
#if DEBUG
|
||||
/// Debug-only: Simulates premium being unlocked for testing.
|
||||
var isDebugPremiumEnabled: Bool {
|
||||
get { UserDefaults.standard.bool(forKey: "debugPremiumEnabled") }
|
||||
set { UserDefaults.standard.set(newValue, forKey: "debugPremiumEnabled") }
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
init() {}
|
||||
}
|
||||
@ -31,6 +31,10 @@ struct RootTabView: View {
|
||||
Tab(String.localized("Widgets"), systemImage: "square.grid.2x2", value: AppTab.widgets) {
|
||||
WidgetsView()
|
||||
}
|
||||
|
||||
Tab(String.localized("Settings"), systemImage: "gearshape", value: AppTab.settings) {
|
||||
SettingsView()
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showingShareSheet) {
|
||||
ShareCardView()
|
||||
|
||||
150
BusinessCard/Views/SettingsView.swift
Normal file
150
BusinessCard/Views/SettingsView.swift
Normal file
@ -0,0 +1,150 @@
|
||||
//
|
||||
// SettingsView.swift
|
||||
// BusinessCard
|
||||
//
|
||||
// App settings screen using Bedrock components.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Bedrock
|
||||
|
||||
struct SettingsView: View {
|
||||
@State private var settingsState = SettingsState()
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
VStack(spacing: Design.Spacing.large) {
|
||||
|
||||
// MARK: - About Section
|
||||
|
||||
aboutSection
|
||||
|
||||
// MARK: - Debug Section
|
||||
|
||||
#if DEBUG
|
||||
debugSection
|
||||
#endif
|
||||
|
||||
Spacer(minLength: Design.Spacing.xxxLarge)
|
||||
}
|
||||
.padding(.horizontal, Design.Spacing.large)
|
||||
.padding(.top, Design.Spacing.medium)
|
||||
}
|
||||
.background(Color(.systemGroupedBackground))
|
||||
.navigationTitle(String.localized("Settings"))
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - About Section
|
||||
|
||||
private var aboutSection: some View {
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
||||
SettingsSectionHeader(
|
||||
title: String.localized("About"),
|
||||
systemImage: "info.circle",
|
||||
accentColor: AppThemeAccent.primary
|
||||
)
|
||||
|
||||
SettingsCard(
|
||||
backgroundColor: Color(.secondarySystemGroupedBackground),
|
||||
borderColor: .clear
|
||||
) {
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
||||
// App Name
|
||||
HStack {
|
||||
Text(settingsState.appName)
|
||||
.font(.system(size: Design.BaseFontSize.title, weight: .semibold))
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
||||
// Version
|
||||
HStack {
|
||||
Text(String.localized("Version"))
|
||||
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
Text(settingsState.versionString)
|
||||
.font(.system(size: Design.BaseFontSize.body, design: .monospaced))
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
// Developer
|
||||
HStack {
|
||||
Text(String.localized("Developer"))
|
||||
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("Matt Bruce")
|
||||
.font(.system(size: Design.BaseFontSize.body))
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Debug Section
|
||||
|
||||
#if DEBUG
|
||||
private var debugSection: some View {
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
||||
SettingsSectionHeader(
|
||||
title: "Debug",
|
||||
systemImage: "ant.fill",
|
||||
accentColor: AppStatus.error
|
||||
)
|
||||
|
||||
SettingsCard(
|
||||
backgroundColor: Color(.secondarySystemGroupedBackground),
|
||||
borderColor: .clear
|
||||
) {
|
||||
VStack(spacing: Design.Spacing.medium) {
|
||||
SettingsToggle(
|
||||
title: "Enable Debug Premium",
|
||||
subtitle: "Unlock all premium features for testing",
|
||||
isOn: Binding(
|
||||
get: { settingsState.isDebugPremiumEnabled },
|
||||
set: { settingsState.isDebugPremiumEnabled = $0 }
|
||||
),
|
||||
accentColor: AppStatus.warning
|
||||
)
|
||||
|
||||
SettingsNavigationRow(
|
||||
title: "Icon Generator",
|
||||
subtitle: "Generate and save app icon to Files",
|
||||
backgroundColor: Color(.tertiarySystemGroupedBackground)
|
||||
) {
|
||||
IconGeneratorView(config: .businessCard, appName: "BusinessCard")
|
||||
}
|
||||
|
||||
SettingsNavigationRow(
|
||||
title: "Branding Preview",
|
||||
subtitle: "Preview app icon and launch screen",
|
||||
backgroundColor: Color(.tertiarySystemGroupedBackground)
|
||||
) {
|
||||
BrandingPreviewView(
|
||||
iconConfig: .businessCard,
|
||||
launchConfig: .businessCard,
|
||||
appName: "BusinessCard"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// MARK: - Preview
|
||||
|
||||
#Preview {
|
||||
SettingsView()
|
||||
}
|
||||
@ -6,17 +6,9 @@
|
||||
<array>
|
||||
<string>appclips:$(APPCLIP_DOMAIN)</string>
|
||||
</array>
|
||||
<key>com.apple.developer.icloud-container-identifiers</key>
|
||||
<array>
|
||||
<string>$(CLOUDKIT_CONTAINER_IDENTIFIER)</string>
|
||||
</array>
|
||||
<key>com.apple.developer.icloud-services</key>
|
||||
<array>
|
||||
<string>CloudKit</string>
|
||||
</array>
|
||||
<key>com.apple.developer.parent-application-identifiers</key>
|
||||
<array>
|
||||
<string>$(TeamIdentifierPrefix)$(APP_BUNDLE_IDENTIFIER)</string>
|
||||
<string>$(AppIdentifierPrefix)$(APP_BUNDLE_IDENTIFIER)</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user