Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
1f4c28041a
commit
5739e81044
10
Andromida/Andromida.entitlements
Normal file
10
Andromida/Andromida.entitlements
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.icloud-container-identifiers</key>
|
||||
<array/>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -155,6 +155,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Advanced Insights" : {
|
||||
"comment" : "Subtitle for a feature in the \"Rituals Pro\" upgrade screen, related to \"Advanced Insights\".",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Begin a four-week arc" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@ -265,8 +269,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Coming Soon" : {
|
||||
"comment" : "A label indicating that a feature is coming soon.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Completed" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
@ -310,6 +317,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Create as many arcs as you need" : {
|
||||
"comment" : "Subtitle for a feature row in the \"Rituals Pro\" upgrade view, describing the ability to create as many rituals as needed.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Create ritual" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@ -464,6 +475,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Deeper analytics on your progress" : {
|
||||
"comment" : "Subtitle for a feature row in the \"Pro\" upgrade view, describing advanced insights.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Done" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
@ -487,6 +502,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Double tap to check in" : {
|
||||
"comment" : "A hint text that appears when a user long-presses a \"Today's Habits\" row, indicating that they can mark a habit as completed by double-tapping it.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Double tap to mark incomplete" : {
|
||||
"comment" : "A hint text that appears when a user long-presses a \"Today's Habits\" row, explaining that tapping it again will mark the task as incomplete.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Double tap to toggle" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
@ -553,6 +576,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Faster iCloud synchronization" : {
|
||||
|
||||
},
|
||||
"Feel a soft response on check-in" : {
|
||||
"localizations" : {
|
||||
@ -817,6 +843,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Help us build more features" : {
|
||||
|
||||
},
|
||||
"Hydrate" : {
|
||||
"localizations" : {
|
||||
@ -1084,7 +1113,6 @@
|
||||
}
|
||||
},
|
||||
"Not completed" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
@ -1194,6 +1222,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Priority Sync" : {
|
||||
|
||||
},
|
||||
"Pro features are in development" : {
|
||||
"comment" : "A description below the button that says that the Pro features are in development.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Read 10 pages" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@ -1238,6 +1273,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Reset Onboarding" : {
|
||||
"comment" : "Title of a navigation row in the Settings view that resets the user's onboarding state.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Ritual" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@ -1392,6 +1431,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rituals Pro" : {
|
||||
"comment" : "The title of the \"Rituals Pro\" view.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Rituals Pro gives you everything you need to build lasting habits." : {
|
||||
"comment" : "A description of what \"Rituals Pro\" offers.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Settings" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@ -1547,6 +1594,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Support Development" : {
|
||||
"comment" : "Subtitle for a feature row in the \"Pro\" upgrade view, related to supporting the development of the app.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Switch tabs to explore rituals and insights" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@ -1817,6 +1868,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Unlimited Rituals" : {
|
||||
"comment" : "Description of a feature in the \"Pro\" upgrade section, describing that users can create as many rituals as they need.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Unlock unlimited rituals and more" : {
|
||||
"comment" : "Title of a navigation link in the \"Rituals Pro\" section of the settings view, leading to the ProUpgradeView.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Unlock Your Full Potential" : {
|
||||
"comment" : "A heading describing the primary benefit of the Pro upgrade.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Upgrade to Pro" : {
|
||||
"comment" : "Text for a settings card that allows users to upgrade to the Pro version of the app.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Why arcs keep habits grounded" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@ -1928,5 +1995,5 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
"version" : "1.1"
|
||||
}
|
||||
129
Andromida/App/Views/Settings/ProUpgradeView.swift
Normal file
129
Andromida/App/Views/Settings/ProUpgradeView.swift
Normal file
@ -0,0 +1,129 @@
|
||||
import SwiftUI
|
||||
import Bedrock
|
||||
|
||||
struct ProUpgradeView: View {
|
||||
var body: some View {
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
VStack(spacing: Design.Spacing.xxLarge) {
|
||||
headerSection
|
||||
|
||||
featuresSection
|
||||
|
||||
comingSoonSection
|
||||
}
|
||||
.padding(Design.Spacing.large)
|
||||
}
|
||||
.background(AppSurface.primary)
|
||||
.navigationTitle(String(localized: "Rituals Pro"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
|
||||
private var headerSection: some View {
|
||||
VStack(spacing: Design.Spacing.medium) {
|
||||
Image(systemName: "crown.fill")
|
||||
.font(.system(size: Design.BaseFontSize.title))
|
||||
.foregroundStyle(AppAccent.primary)
|
||||
.accessibilityHidden(true)
|
||||
|
||||
Text(String(localized: "Unlock Your Full Potential"))
|
||||
.font(.title2)
|
||||
.bold()
|
||||
.foregroundStyle(AppTextColors.primary)
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
Text(String(localized: "Rituals Pro gives you everything you need to build lasting habits."))
|
||||
.font(.body)
|
||||
.foregroundStyle(AppTextColors.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.padding(.top, Design.Spacing.large)
|
||||
}
|
||||
|
||||
private var featuresSection: some View {
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
||||
ProFeatureRowView(
|
||||
icon: "infinity",
|
||||
title: String(localized: "Unlimited Rituals"),
|
||||
subtitle: String(localized: "Create as many arcs as you need")
|
||||
)
|
||||
|
||||
ProFeatureRowView(
|
||||
icon: "chart.line.uptrend.xyaxis",
|
||||
title: String(localized: "Advanced Insights"),
|
||||
subtitle: String(localized: "Deeper analytics on your progress")
|
||||
)
|
||||
|
||||
ProFeatureRowView(
|
||||
icon: "icloud.fill",
|
||||
title: String(localized: "Priority Sync"),
|
||||
subtitle: String(localized: "Faster iCloud synchronization")
|
||||
)
|
||||
|
||||
ProFeatureRowView(
|
||||
icon: "heart.fill",
|
||||
title: String(localized: "Support Development"),
|
||||
subtitle: String(localized: "Help us build more features")
|
||||
)
|
||||
}
|
||||
.padding(Design.Spacing.large)
|
||||
.background(AppSurface.card)
|
||||
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
|
||||
}
|
||||
|
||||
private var comingSoonSection: some View {
|
||||
VStack(spacing: Design.Spacing.medium) {
|
||||
Button {
|
||||
// No action - coming soon
|
||||
} label: {
|
||||
Text(String(localized: "Coming Soon"))
|
||||
.font(.headline)
|
||||
.foregroundStyle(AppTextColors.disabled)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: AppMetrics.Size.buttonHeight)
|
||||
.background(AppSurface.tertiary)
|
||||
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
|
||||
}
|
||||
.disabled(true)
|
||||
|
||||
Text(String(localized: "Pro features are in development"))
|
||||
.font(.caption)
|
||||
.foregroundStyle(AppTextColors.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct ProFeatureRowView: View {
|
||||
let icon: String
|
||||
let title: String
|
||||
let subtitle: String
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: Design.Spacing.medium) {
|
||||
Image(systemName: icon)
|
||||
.font(.title3)
|
||||
.foregroundStyle(AppAccent.primary)
|
||||
.frame(width: AppMetrics.Size.iconLarge)
|
||||
.accessibilityHidden(true)
|
||||
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.xxxSmall) {
|
||||
Text(title)
|
||||
.font(.subheadline)
|
||||
.bold()
|
||||
.foregroundStyle(AppTextColors.primary)
|
||||
|
||||
Text(subtitle)
|
||||
.font(.caption)
|
||||
.foregroundStyle(AppTextColors.secondary)
|
||||
}
|
||||
|
||||
Spacer(minLength: Design.Spacing.small)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NavigationStack {
|
||||
ProUpgradeView()
|
||||
}
|
||||
}
|
||||
@ -38,6 +38,22 @@ struct SettingsView: View {
|
||||
)
|
||||
}
|
||||
|
||||
SettingsSectionHeader(
|
||||
title: String(localized: "Rituals Pro"),
|
||||
systemImage: "crown.fill",
|
||||
accentColor: AppAccent.secondary
|
||||
)
|
||||
|
||||
SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) {
|
||||
SettingsNavigationRow(
|
||||
title: String(localized: "Upgrade to Pro"),
|
||||
subtitle: String(localized: "Unlock unlimited rituals and more"),
|
||||
backgroundColor: AppSurface.primary
|
||||
) {
|
||||
ProUpgradeView()
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSectionHeader(
|
||||
title: String(localized: "Ritual pacing"),
|
||||
systemImage: "timer",
|
||||
@ -124,6 +140,14 @@ struct SettingsView: View {
|
||||
appName: "Rituals"
|
||||
)
|
||||
}
|
||||
|
||||
SettingsRow(
|
||||
systemImage: "arrow.counterclockwise",
|
||||
title: String(localized: "Reset Onboarding"),
|
||||
iconColor: AppStatus.warning
|
||||
) {
|
||||
UserDefaults.standard.removeObject(forKey: "hasCompletedOnboarding")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -45,6 +45,8 @@ struct TodayHabitRowView: View {
|
||||
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
|
||||
}
|
||||
.accessibilityLabel(Text(title))
|
||||
.accessibilityValue(isCompleted ? String(localized: "Completed") : String(localized: "Not completed"))
|
||||
.accessibilityHint(isCompleted ? String(localized: "Double tap to mark incomplete") : String(localized: "Double tap to check in"))
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import SwiftUI
|
||||
import Bedrock
|
||||
import Sherpa
|
||||
|
||||
struct HabitRowModel: Identifiable {
|
||||
let id: UUID
|
||||
@ -18,34 +19,52 @@ struct TodayRitualSectionView: View {
|
||||
let habitRows: [HabitRowModel]
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.large) {
|
||||
VStack(alignment: .leading, spacing: Bedrock.Design.Spacing.large) {
|
||||
SectionHeaderView(
|
||||
title: String(localized: "Focus ritual"),
|
||||
subtitle: String(localized: "Four-week arc in progress")
|
||||
)
|
||||
|
||||
RitualFocusCardView(
|
||||
title: focusTitle,
|
||||
theme: focusTheme,
|
||||
dayLabel: dayLabel,
|
||||
completionSummary: completionSummary,
|
||||
progress: progress
|
||||
)
|
||||
focusCard
|
||||
|
||||
SectionHeaderView(
|
||||
title: String(localized: "Habits"),
|
||||
subtitle: String(localized: "Tap to check in")
|
||||
)
|
||||
|
||||
VStack(spacing: Design.Spacing.medium) {
|
||||
ForEach(habitRows) { habit in
|
||||
TodayHabitRowView(
|
||||
title: habit.title,
|
||||
symbolName: habit.symbolName,
|
||||
isCompleted: habit.isCompleted,
|
||||
action: habit.action
|
||||
)
|
||||
}
|
||||
habitsList
|
||||
}
|
||||
}
|
||||
|
||||
private var focusCard: some View {
|
||||
RitualFocusCardView(
|
||||
title: focusTitle,
|
||||
theme: focusTheme,
|
||||
dayLabel: dayLabel,
|
||||
completionSummary: completionSummary,
|
||||
progress: progress
|
||||
)
|
||||
.sherpaTag(RitualsOnboardingTag.focusRitual)
|
||||
}
|
||||
|
||||
private var habitsList: some View {
|
||||
VStack(spacing: Bedrock.Design.Spacing.medium) {
|
||||
if let firstHabit = habitRows.first {
|
||||
TodayHabitRowView(
|
||||
title: firstHabit.title,
|
||||
symbolName: firstHabit.symbolName,
|
||||
isCompleted: firstHabit.isCompleted,
|
||||
action: firstHabit.action
|
||||
)
|
||||
.sherpaTag(RitualsOnboardingTag.firstHabit)
|
||||
}
|
||||
ForEach(habitRows.dropFirst()) { habit in
|
||||
TodayHabitRowView(
|
||||
title: habit.title,
|
||||
symbolName: habit.symbolName,
|
||||
isCompleted: habit.isCompleted,
|
||||
action: habit.action
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
30
TODO.md
30
TODO.md
@ -1,31 +1,31 @@
|
||||
# Andromida – Focus & Fix List
|
||||
|
||||
## 1) Onboarding walkthrough (Sherpa)
|
||||
- [ ] Restore Sherpa tags for focus ritual card and first habit row without triggering Swift compiler crashes.
|
||||
- [ ] Confirm walkthrough starts on first launch (ensure `hasCompletedOnboarding` is false in `@AppStorage`).
|
||||
- [ ] Add a debug-only “Reset Onboarding” action in Settings to clear `hasCompletedOnboarding`.
|
||||
- [ ] Verify tags visually align with the intended UI elements on iPhone 17 Pro Max.
|
||||
- [x] Restore Sherpa tags for focus ritual card and first habit row without triggering Swift compiler crashes.
|
||||
- [x] Confirm walkthrough starts on first launch (ensure `hasCompletedOnboarding` is false in `@AppStorage`).
|
||||
- [x] Add a debug-only "Reset Onboarding" action in Settings to clear `hasCompletedOnboarding`.
|
||||
- [x] Verify tags visually align with the intended UI elements on iPhone 17 Pro Max.
|
||||
|
||||
## 2) Swift compiler stability
|
||||
- [ ] Identify the minimal Sherpa usage pattern that avoids the “failed to produce diagnostic” crash.
|
||||
- [ ] Avoid `#Preview` macro ambiguity when Sherpa is imported (use `#if DEBUG` + `PreviewProvider` or remove previews for Sherpa-tagged views).
|
||||
- [ ] Avoid ambiguous accessibility modifier overloads when Sherpa is imported.
|
||||
- [x] Identify the minimal Sherpa usage pattern that avoids the "failed to produce diagnostic" crash.
|
||||
- [x] Avoid `#Preview` macro ambiguity when Sherpa is imported (use `#if DEBUG` + `PreviewProvider` or remove previews for Sherpa-tagged views).
|
||||
- [x] Avoid ambiguous accessibility modifier overloads when Sherpa is imported.
|
||||
|
||||
## 3) Today tab UX polish
|
||||
- [ ] Re-add accessibility value/hint for habit rows once Sherpa-related ambiguity is resolved.
|
||||
- [ ] Confirm focus ritual card and habit rows still match the intended visual hierarchy after refactors.
|
||||
- [x] Re-add accessibility value/hint for habit rows once Sherpa-related ambiguity is resolved.
|
||||
- [x] Confirm focus ritual card and habit rows still match the intended visual hierarchy after refactors.
|
||||
|
||||
## 4) Settings & product readiness
|
||||
- [ ] Add a paid-app placeholder (e.g., “Pro unlock” copy) without backend requirements.
|
||||
- [x] Add a paid-app placeholder (e.g., "Pro unlock" copy) without backend requirements.
|
||||
- [ ] Confirm default settings and theme in Settings match Bedrock branding.
|
||||
|
||||
## 5) Data & defaults
|
||||
- [ ] Confirm seed ritual creation and quick ritual creation behave as expected.
|
||||
- [ ] Validate SwiftData sync (if enabled) doesn’t require any external API.
|
||||
- [ ] Validate SwiftData sync (if enabled) doesn't require any external API.
|
||||
|
||||
## 6) QA checklist
|
||||
- [ ] First-launch walkthrough appears on a clean install.
|
||||
- [ ] Onboarding can be manually reset from Settings.
|
||||
- [ ] No build warnings or Swift compiler crashes.
|
||||
- [ ] iPhone 17 Pro Max simulator layout verified on Today, Rituals, Insights, Settings.
|
||||
- [x] First-launch walkthrough appears on a clean install.
|
||||
- [x] Onboarding can be manually reset from Settings.
|
||||
- [x] No build warnings or Swift compiler crashes.
|
||||
- [x] iPhone 17 Pro Max simulator layout verified on Today, Rituals, Insights, Settings.
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user