129 lines
5.4 KiB
Swift
129 lines
5.4 KiB
Swift
import SwiftUI
|
|
import Bedrock
|
|
|
|
/// Shown when there are active rituals but none scheduled for the current time of day.
|
|
struct TodayNoRitualsForTimeView: View {
|
|
@Bindable var store: RitualStore
|
|
|
|
private var currentTimePeriod: TimeOfDay {
|
|
let hour = Calendar.current.component(.hour, from: Date())
|
|
switch hour {
|
|
case 0..<11: return .morning
|
|
case 11..<14: return .midday
|
|
case 14..<17: return .afternoon
|
|
case 17..<21: return .evening
|
|
default: return .night
|
|
}
|
|
}
|
|
|
|
private var nextRituals: [Ritual] {
|
|
// Find rituals scheduled for later time periods
|
|
store.currentRituals.filter { ritual in
|
|
guard let arc = ritual.currentArc, arc.contains(date: Date()) else { return false }
|
|
return ritual.timeOfDay != .anytime && ritual.timeOfDay > currentTimePeriod
|
|
}
|
|
}
|
|
|
|
private var nextRitualTomorrow: Ritual? {
|
|
let calendar = Calendar.current
|
|
let tomorrowDate = calendar.startOfDay(
|
|
for: calendar.date(byAdding: .day, value: 1, to: Date()) ?? Date()
|
|
)
|
|
|
|
return store.currentRituals
|
|
.filter { ritual in
|
|
guard let arc = ritual.currentArc, arc.contains(date: tomorrowDate) else { return false }
|
|
return ritual.timeOfDay != .anytime
|
|
}
|
|
.sorted { $0.timeOfDay < $1.timeOfDay }
|
|
.first
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: Design.Spacing.large) {
|
|
SectionHeaderView(
|
|
title: String(localized: "All caught up"),
|
|
subtitle: currentTimePeriod.displayName
|
|
)
|
|
|
|
VStack(spacing: Design.Spacing.large) {
|
|
// Icon
|
|
Image(systemName: currentTimePeriod.symbolName)
|
|
.font(.system(size: Design.IconSize.hero))
|
|
.foregroundStyle(AppAccent.primary.opacity(0.6))
|
|
.padding(.top, Design.Spacing.large)
|
|
|
|
VStack(spacing: Design.Spacing.xSmall) {
|
|
Text(String(localized: "No rituals scheduled for \(currentTimePeriod.displayName.lowercased())."))
|
|
.font(.subheadline)
|
|
.foregroundStyle(AppTextColors.secondary)
|
|
.multilineTextAlignment(.center)
|
|
|
|
Text(currentTimePeriod.timeRange)
|
|
.font(.caption)
|
|
.foregroundStyle(AppTextColors.tertiary)
|
|
}
|
|
|
|
// Show upcoming rituals if any
|
|
if !nextRituals.isEmpty {
|
|
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
|
Text(String(localized: "Coming up later:"))
|
|
.font(.caption)
|
|
.foregroundStyle(AppTextColors.tertiary)
|
|
|
|
ForEach(nextRituals) { ritual in
|
|
HStack(spacing: Design.Spacing.small) {
|
|
Image(systemName: ritual.timeOfDay.symbolName)
|
|
.font(.caption)
|
|
.foregroundStyle(AppTextColors.secondary)
|
|
|
|
Text(ritual.title)
|
|
.font(.subheadline)
|
|
.foregroundStyle(AppTextColors.primary)
|
|
|
|
Spacer()
|
|
|
|
Text(ritual.timeOfDay.displayName)
|
|
.font(.caption)
|
|
.foregroundStyle(AppTextColors.tertiary)
|
|
}
|
|
.padding(Design.Spacing.small)
|
|
.background(AppSurface.secondary)
|
|
.clipShape(.rect(cornerRadius: Design.CornerRadius.small))
|
|
}
|
|
}
|
|
.padding(.top, Design.Spacing.small)
|
|
} else if let tomorrowRitual = nextRitualTomorrow {
|
|
let format = String(localized: "Next ritual: Tomorrow %@ (%@)")
|
|
Text(String.localizedStringWithFormat(
|
|
format,
|
|
tomorrowRitual.timeOfDay.displayName,
|
|
tomorrowRitual.timeOfDay.timeRange
|
|
))
|
|
.font(.caption)
|
|
.foregroundStyle(AppTextColors.tertiary)
|
|
.multilineTextAlignment(.center)
|
|
.padding(.top, Design.Spacing.small)
|
|
}
|
|
|
|
// Motivational message
|
|
Text(String(localized: "Enjoy this moment. Your next ritual will appear when it's time."))
|
|
.font(.caption)
|
|
.foregroundStyle(AppTextColors.tertiary)
|
|
.multilineTextAlignment(.center)
|
|
.padding(.top, Design.Spacing.small)
|
|
}
|
|
.padding(Design.Spacing.large)
|
|
.frame(maxWidth: .infinity)
|
|
.background(AppSurface.card)
|
|
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
TodayNoRitualsForTimeView(store: RitualStore.preview)
|
|
.padding()
|
|
.background(AppSurface.primary)
|
|
}
|