Add widget exports, promo assets, and screenshot resources

- Add ExportableWidgetViews for widget screenshot generation
- Update WidgetExporter and WidgetSharedViews with layout fixes
- Add promo video assets (activity, month, year videos)
- Add LiveActivityAnimation and BackgroundStill components
- Add widget export screenshots and voting images
- Update localizations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-31 09:02:05 -06:00
parent 5d1b0b60fa
commit 70451804ba
4 changed files with 12987 additions and 12560 deletions

View File

@@ -53,20 +53,23 @@ struct VotingView: View {
}
}
// MARK: - Medium Widget: Vertical split - text top, voting bottom
// MARK: - Medium Widget: 50/50 split, both centered
private var mediumLayout: some View {
VStack(spacing: 0) {
// Top: Text left-aligned, centered horizontally
Text(hasSubscription ? promptText : "Subscribe to track your mood")
.font(.headline)
.foregroundStyle(.primary)
.multilineTextAlignment(.leading)
.lineLimit(2)
.minimumScaleFactor(0.8)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.padding(.horizontal, 16)
// Top 50%: Text left-aligned, vertically centered
HStack {
Text(hasSubscription ? promptText : "Subscribe to track your mood")
.font(.system(size: 20, weight: .semibold))
.foregroundStyle(.primary)
.multilineTextAlignment(.leading)
.lineLimit(2)
.minimumScaleFactor(0.8)
Spacer()
}
.padding(.horizontal, 16)
.frame(maxWidth: .infinity, maxHeight: .infinity)
// Bottom: Voting buttons with equal spacing, centered
// Bottom 50%: Voting buttons centered
HStack(spacing: 0) {
ForEach([Mood.great, .good, .average, .bad, .horrible], id: \.rawValue) { mood in
moodButtonMedium(for: mood)
@@ -106,7 +109,7 @@ struct VotingView: View {
let content = moodImages.icon(forMood: mood)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 36, height: 36)
.frame(width: 54, height: 54)
.foregroundColor(moodTint.color(forMood: mood))
if hasSubscription {
@@ -151,20 +154,19 @@ struct LargeVotingView: View {
var body: some View {
GeometryReader { geo in
VStack(spacing: 0) {
// Top 25%: Title centered x,y
// Top 33%: Title centered
Text(hasSubscription ? promptText : "Subscribe to track your mood")
.font(.title3.weight(.semibold))
.font(.system(size: 24, weight: .semibold))
.foregroundStyle(.primary)
.multilineTextAlignment(.center)
.lineLimit(2)
.minimumScaleFactor(0.8)
.padding(.horizontal, 12)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.frame(height: geo.size.height * 0.25)
.frame(width: geo.size.width, height: geo.size.height * 0.33)
// Bottom 75%: Voting buttons in two rows
// Bottom 66%: Voting buttons in two rows
VStack(spacing: 0) {
// Top row at 33%: Great, Good, Average
// Top row: Great, Good, Average
HStack(spacing: 16) {
ForEach([Mood.great, .good, .average], id: \.rawValue) { mood in
moodButton(for: mood)
@@ -172,7 +174,7 @@ struct LargeVotingView: View {
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
// Bottom row at 66%: Bad, Horrible
// Bottom row: Bad, Horrible
HStack(spacing: 16) {
ForEach([Mood.bad, .horrible], id: \.rawValue) { mood in
moodButton(for: mood)
@@ -180,7 +182,7 @@ struct LargeVotingView: View {
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
.frame(height: geo.size.height * 0.75)
.frame(width: geo.size.width, height: geo.size.height * 0.67)
}
}
}
@@ -208,11 +210,11 @@ struct LargeVotingView: View {
moodImages.icon(forMood: mood)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 44, height: 44)
.frame(width: 53, height: 53)
.foregroundColor(moodTint.color(forMood: mood))
.padding(10)
.padding(12)
.background(
RoundedRectangle(cornerRadius: 12)
RoundedRectangle(cornerRadius: 14)
.fill(moodTint.color(forMood: mood).opacity(0.15))
)
}