Add debug widget/live activity export and competitor research

Debug features (DEBUG builds only):
- WidgetExporter: Export all widget variants to PNG (light/dark modes)
- Live Activity lock screen export with configurable streak
- Test notifications button to preview all personality packs
- Settings buttons for export and notification testing

Research:
- Competitor analysis docs (Daylio, Bearable, Reflectly, etc.)
- App Store screenshot reference materials

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-03 09:46:03 -06:00
parent e5656f47fd
commit 406d9ee4fd
42 changed files with 2810 additions and 1 deletions

View File

@@ -19,6 +19,8 @@ struct SettingsContentView: View {
@State private var showReminderTimePicker = false
@State private var showSubscriptionStore = false
@State private var showTrialDatePicker = false
@State private var isExportingWidgets = false
@State private var widgetExportPath: URL?
@StateObject private var healthService = HealthService.shared
@AppStorage(UserDefaultsStore.Keys.firstLaunchDate.rawValue, store: GroupUserDefaults.groupDefaults)
@@ -58,7 +60,9 @@ struct SettingsContentView: View {
animationLabButton
paywallPreviewButton
tipsPreviewButton
testNotificationsButton
exportWidgetsButton
clearDataButton
#endif
@@ -382,6 +386,95 @@ struct SettingsContentView: View {
}
}
private var testNotificationsButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button {
LocalNotification.sendAllPersonalityNotificationsForScreenshot()
} label: {
HStack(spacing: 12) {
Image(systemName: "bell.badge.fill")
.font(.title2)
.foregroundColor(.red)
.frame(width: 32)
VStack(alignment: .leading, spacing: 2) {
Text("Test All Notifications")
.foregroundColor(textColor)
Text("Send 5 personality pack notifications")
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer()
Image(systemName: "arrow.up.right")
.font(.caption)
.foregroundStyle(.tertiary)
}
.padding()
}
}
.fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
}
private var exportWidgetsButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor
Button {
isExportingWidgets = true
Task {
widgetExportPath = await WidgetExporter.exportAllWidgets()
isExportingWidgets = false
// Open Files app to the export location
if let path = widgetExportPath {
// Show share sheet or alert with path
print("📸 Widgets exported to: \(path.path)")
}
}
} label: {
HStack(spacing: 12) {
if isExportingWidgets {
ProgressView()
.frame(width: 32)
} else {
Image(systemName: "square.grid.2x2.fill")
.font(.title2)
.foregroundColor(.purple)
.frame(width: 32)
}
VStack(alignment: .leading, spacing: 2) {
Text("Export Widget Screenshots")
.foregroundColor(textColor)
if let path = widgetExportPath {
Text("Saved to Documents/WidgetExports")
.font(.caption)
.foregroundColor(.green)
} else {
Text("Light & dark mode PNGs")
.font(.caption)
.foregroundStyle(.secondary)
}
}
Spacer()
Image(systemName: "arrow.down.doc.fill")
.font(.caption)
.foregroundStyle(.tertiary)
}
.padding()
}
.disabled(isExportingWidgets)
}
.fixedSize(horizontal: false, vertical: true)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
}
private var clearDataButton: some View {
ZStack {
theme.currentTheme.secondaryBGColor