Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
1689e7cec2
commit
f1cbd5f6a1
@ -7,12 +7,12 @@
|
||||
<key>Andromida.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>AndromidaWidgetExtension.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
|
||||
@ -1921,6 +1921,14 @@
|
||||
"comment" : "Habit title for reading a physical book as part of a RitualPreset.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Real" : {
|
||||
"comment" : "The text for the \"Real\" option in the time of day picker.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Real Time (%@)" : {
|
||||
"comment" : "Text displayed in the debug picker to indicate whether it is showing the real time or a simulated time.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Reflect" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@ -2221,6 +2229,10 @@
|
||||
"comment" : "Title of a settings option that simulates a foreground refresh of the app.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Simulate Time of Day" : {
|
||||
"comment" : "A label for the time of day picker.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Skip" : {
|
||||
"comment" : "Button label to skip onboarding.",
|
||||
"isCommentAutoGenerated" : true,
|
||||
|
||||
@ -24,83 +24,6 @@ struct AndromidaWidgetView: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct LargeWidgetView: View {
|
||||
let entry: WidgetEntry
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.large) {
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
||||
Text(String(localized: "Today's Progress"))
|
||||
.styled(.heading, emphasis: .custom(AppTextColors.primary))
|
||||
Text("\(entry.currentStreak) day streak")
|
||||
.styled(.subheading, emphasis: .custom(AppAccent.primary))
|
||||
}
|
||||
Spacer()
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(AppTextColors.primary.opacity(0.1), lineWidth: 6)
|
||||
Circle()
|
||||
.trim(from: 0, to: entry.completionRate)
|
||||
.stroke(AppAccent.primary, style: StrokeStyle(lineWidth: 6, lineCap: .round))
|
||||
.rotationEffect(.degrees(-90))
|
||||
Text("\(Int(entry.completionRate * 100))%")
|
||||
.styled(.captionEmphasis, emphasis: .custom(AppTextColors.primary))
|
||||
}
|
||||
.frame(width: 50, height: 50)
|
||||
}
|
||||
.padding(.top, Design.Spacing.small)
|
||||
|
||||
Divider()
|
||||
.background(AppTextColors.primary.opacity(0.2))
|
||||
|
||||
if entry.nextHabits.isEmpty {
|
||||
WidgetEmptyStateView(
|
||||
iconSize: .section,
|
||||
title: String(localized: "No rituals scheduled for \(entry.currentTimeOfDay.lowercased())."),
|
||||
subtitle: entry.currentTimeOfDay,
|
||||
symbolName: entry.currentTimeOfDaySymbol,
|
||||
timeRange: entry.currentTimeOfDayRange,
|
||||
nextRitual: entry.nextRitualInfo,
|
||||
isCompact: false
|
||||
)
|
||||
} else {
|
||||
Text(String(localized: "Habits"))
|
||||
.styled(.captionEmphasis, emphasis: .custom(AppTextColors.secondary))
|
||||
|
||||
VStack(spacing: Design.Spacing.medium) {
|
||||
ForEach(entry.nextHabits) { habit in
|
||||
HStack(spacing: Design.Spacing.medium) {
|
||||
Image(systemName: habit.symbolName)
|
||||
.foregroundColor(AppAccent.primary)
|
||||
.font(.system(size: 18))
|
||||
.frame(width: 24)
|
||||
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
||||
Text(habit.title)
|
||||
.styled(.subheading, emphasis: .custom(AppTextColors.primary))
|
||||
Text(habit.ritualTitle)
|
||||
.styled(.caption, emphasis: .custom(AppTextColors.tertiary))
|
||||
}
|
||||
Spacer()
|
||||
|
||||
Image(systemName: habit.isCompleted ? "checkmark.circle.fill" : "circle")
|
||||
.foregroundColor(habit.isCompleted ? .green : AppTextColors.primary.opacity(0.2))
|
||||
.font(.system(size: 20))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(Design.Spacing.large)
|
||||
.containerBackground(for: .widget) {
|
||||
AppSurface.primary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Branding Colors Helper
|
||||
extension Color {
|
||||
static let brandingPrimary = Color(red: 0.12, green: 0.09, blue: 0.08)
|
||||
|
||||
80
AndromidaWidget/Views/Components/LargeWidgetView.swift
Normal file
80
AndromidaWidget/Views/Components/LargeWidgetView.swift
Normal file
@ -0,0 +1,80 @@
|
||||
import SwiftUI
|
||||
import WidgetKit
|
||||
import Bedrock
|
||||
|
||||
struct LargeWidgetView: View {
|
||||
let entry: WidgetEntry
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.large) {
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
||||
Text(String(localized: "Today's Progress"))
|
||||
.styled(.heading, emphasis: .custom(AppTextColors.primary))
|
||||
Text("\(entry.currentStreak) day streak")
|
||||
.styled(.subheading, emphasis: .custom(AppAccent.primary))
|
||||
}
|
||||
Spacer()
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(AppTextColors.primary.opacity(0.1), lineWidth: 6)
|
||||
Circle()
|
||||
.trim(from: 0, to: entry.completionRate)
|
||||
.stroke(AppAccent.primary, style: StrokeStyle(lineWidth: 6, lineCap: .round))
|
||||
.rotationEffect(.degrees(-90))
|
||||
Text("\(Int(entry.completionRate * 100))%")
|
||||
.styled(.captionEmphasis, emphasis: .custom(AppTextColors.primary))
|
||||
}
|
||||
.frame(width: 50, height: 50)
|
||||
}
|
||||
.padding(.top, Design.Spacing.small)
|
||||
|
||||
Divider()
|
||||
.background(AppTextColors.primary.opacity(0.2))
|
||||
|
||||
if entry.nextHabits.isEmpty {
|
||||
WidgetEmptyStateView(
|
||||
iconSize: .section,
|
||||
title: String(localized: "No rituals scheduled for \(entry.currentTimeOfDay.lowercased())."),
|
||||
subtitle: entry.currentTimeOfDay,
|
||||
symbolName: entry.currentTimeOfDaySymbol,
|
||||
timeRange: entry.currentTimeOfDayRange,
|
||||
nextRitual: entry.nextRitualInfo,
|
||||
isCompact: false
|
||||
)
|
||||
} else {
|
||||
Text(String(localized: "Habits"))
|
||||
.styled(.captionEmphasis, emphasis: .custom(AppTextColors.secondary))
|
||||
|
||||
VStack(spacing: Design.Spacing.medium) {
|
||||
ForEach(entry.nextHabits) { habit in
|
||||
HStack(spacing: Design.Spacing.medium) {
|
||||
Image(systemName: habit.symbolName)
|
||||
.foregroundColor(AppAccent.primary)
|
||||
.font(.system(size: 18))
|
||||
.frame(width: 24)
|
||||
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
||||
Text(habit.title)
|
||||
.styled(.subheading, emphasis: .custom(AppTextColors.primary))
|
||||
Text(habit.ritualTitle)
|
||||
.styled(.caption, emphasis: .custom(AppTextColors.tertiary))
|
||||
}
|
||||
Spacer()
|
||||
|
||||
Image(systemName: habit.isCompleted ? "checkmark.circle.fill" : "circle")
|
||||
.foregroundColor(habit.isCompleted ? .green : AppTextColors.primary.opacity(0.2))
|
||||
.font(.system(size: 20))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(Design.Spacing.large)
|
||||
.containerBackground(for: .widget) {
|
||||
AppSurface.primary
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user