Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2026-01-27 20:23:34 -06:00
parent 6f146fbeff
commit 6c524ba855
5 changed files with 394 additions and 1 deletions

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EAC04A972F26BAE8007F87EA"
BuildableName = "Andromida.app"
BlueprintName = "Andromida"
ReferencedContainer = "container:Andromida.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EAC04A972F26BAE8007F87EA"
BuildableName = "Andromida.app"
BlueprintName = "Andromida"
ReferencedContainer = "container:Andromida.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EAC04A972F26BAE8007F87EA"
BuildableName = "Andromida.app"
BlueprintName = "Andromida"
ReferencedContainer = "container:Andromida.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EAC04D2E2F298D9B007F87EA"
BuildableName = "AndromidaWidgetExtension.appex"
BlueprintName = "AndromidaWidgetExtension"
ReferencedContainer = "container:Andromida.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EAC04A972F26BAE8007F87EA"
BuildableName = "Andromida.app"
BlueprintName = "Andromida"
ReferencedContainer = "container:Andromida.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.springboard">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EAC04D2E2F298D9B007F87EA"
BuildableName = "AndromidaWidgetExtension.appex"
BlueprintName = "AndromidaWidgetExtension"
ReferencedContainer = "container:Andromida.xcodeproj">
</BuildableReference>
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EAC04A972F26BAE8007F87EA"
BuildableName = "Andromida.app"
BlueprintName = "Andromida"
ReferencedContainer = "container:Andromida.xcodeproj">
</BuildableReference>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "_XCWidgetKind"
value = ""
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "_XCWidgetDefaultView"
value = "timeline"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "_XCWidgetFamily"
value = "systemMedium"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EAC04A972F26BAE8007F87EA"
BuildableName = "Andromida.app"
BlueprintName = "Andromida"
ReferencedContainer = "container:Andromida.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -7,7 +7,7 @@
<key>Andromida.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
<integer>2</integer>
</dict>
<key>AndromidaWidgetExtension.xcscheme_^#shared#^_</key>
<dict>
@ -15,5 +15,18 @@
<integer>1</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>EAC04A972F26BAE8007F87EA</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>EAC04D2E2F298D9B007F87EA</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -2734,6 +2734,94 @@
}
}
}
},
"Widgets" : {
"comment" : "Title for the widgets discovery card in onboarding.",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Widgets"
}
}
}
},
"Add Andromida to your Home Screen for quick check-ins." : {
"comment" : "Description on the widget discovery card in onboarding.",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Add Andromida to your Home Screen for quick check-ins."
}
}
}
},
"How to add" : {
"comment" : "CTA button label to show widget setup steps.",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "How to add"
}
}
}
},
"Add the widget" : {
"comment" : "Title for the widget setup sheet in onboarding.",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Add the widget"
}
}
}
},
"Keep your rituals visible at a glance." : {
"comment" : "Subtitle for the widget setup sheet in onboarding.",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Keep your rituals visible at a glance."
}
}
}
},
"Touch and hold your Home Screen." : {
"comment" : "Widget setup step: long-press Home Screen.",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Touch and hold your Home Screen."
}
}
}
},
"Tap Edit, then Add Widget." : {
"comment" : "Widget setup step: tap Edit and Add Widget.",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Tap Edit, then Add Widget."
}
}
}
},
"Search for Andromida and pick a size." : {
"comment" : "Widget setup step: search for the app and choose size.",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Search for Andromida and pick a size."
}
}
}
}
},
"version" : "1.1"

View File

@ -6,6 +6,7 @@ struct WhatsNextStepView: View {
let onComplete: () -> Void
@State private var animateContent = false
@State private var isShowingWidgetHelp = false
var body: some View {
VStack(spacing: Design.Spacing.xxLarge) {
@ -55,6 +56,11 @@ struct WhatsNextStepView: View {
.opacity(animateContent ? 1 : 0)
.offset(y: animateContent ? 0 : 20)
.animation(.easeOut(duration: 0.4).delay(0.3), value: animateContent)
WidgetDiscoveryCard(onLearnMore: { isShowingWidgetHelp = true })
.opacity(animateContent ? 1 : 0)
.offset(y: animateContent ? 0 : 20)
.animation(.easeOut(duration: 0.4).delay(0.4), value: animateContent)
}
.padding(.horizontal, Design.Spacing.large)
@ -80,6 +86,9 @@ struct WhatsNextStepView: View {
animateContent = true
}
}
.sheet(isPresented: $isShowingWidgetHelp) {
WidgetSetupSheet()
}
}
}
@ -112,6 +121,87 @@ private struct FeatureHelpCard: View {
}
}
/// A feature card with a CTA to learn how to add widgets.
private struct WidgetDiscoveryCard: View {
let onLearnMore: () -> Void
var body: some View {
VStack(alignment: .leading, spacing: Design.Spacing.small) {
HStack(spacing: Design.Spacing.medium) {
SymbolIcon("square.grid.2x2.fill", size: .row, color: AppAccent.primary)
.frame(width: 44, height: 44)
.background(AppAccent.primary.opacity(0.15))
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
Text(String(localized: "Widgets")).styled(.heading, emphasis: .primary)
Text(String(localized: "Add Andromida to your Home Screen for quick check-ins."))
.styled(.caption, emphasis: .secondary)
}
Spacer()
}
Button(action: onLearnMore) {
Text(String(localized: "How to add")).styled(.caption, emphasis: .primary)
.padding(.vertical, Design.Spacing.xSmall)
.padding(.horizontal, Design.Spacing.small)
.background(AppAccent.primary.opacity(0.15))
.clipShape(.capsule)
}
.buttonStyle(.plain)
}
.padding(Design.Spacing.medium)
.background(AppSurface.card)
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
}
}
private struct WidgetSetupSheet: View {
var body: some View {
VStack(spacing: Design.Spacing.large) {
VStack(spacing: Design.Spacing.small) {
Text(String(localized: "Add the widget")).styled(.title2Bold, emphasis: .primary)
Text(String(localized: "Keep your rituals visible at a glance."))
.styled(.subheading, emphasis: .secondary)
.multilineTextAlignment(.center)
}
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
WidgetStepRow(number: "1", text: String(localized: "Touch and hold your Home Screen."))
WidgetStepRow(number: "2", text: String(localized: "Tap Edit, then Add Widget."))
WidgetStepRow(number: "3", text: String(localized: "Search for Andromida and pick a size."))
}
.padding(.horizontal, Design.Spacing.large)
Spacer()
}
.padding(.top, Design.Spacing.xxLarge)
.presentationDetents([.medium])
}
}
private struct WidgetStepRow: View {
let number: String
let text: String
var body: some View {
HStack(alignment: .top, spacing: Design.Spacing.medium) {
Text(number).styled(.caption, emphasis: .inverse)
.frame(width: 28, height: 28)
.background(AppAccent.primary)
.clipShape(.circle)
Text(text).styled(.body, emphasis: .primary)
Spacer()
}
.padding(Design.Spacing.medium)
.background(AppSurface.card)
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
}
}
#Preview {
ZStack {
LinearGradient(