- Today ⋯ menu: implemented the placeholder actions.
- Background: 5 selectable themes (Default/Warm/Cool/Mono/Paper), persisted.
- Group & Sort: group by Date (default)/Priority/Category/Quadrant and sort by
Smart/Due/Priority/Title; non-date groupings render via UpcomingSection.
- Select: multi-select sheet with Select All/Deselect All and bulk
Complete/Priority/Move/Delete.
New isolated file TodayMenuFeatures.swift; added TaskViewModel.allActiveRows.
- Matrix "Move" submenu now lists only the other three quadrants (excludes the
current one) using clear matrix labels.
- Event Countdown (Bars) widget auto-advances: each timeline entry re-resolves
"nearest to finish" as of its own time and refreshes at the moment the current
event expires, so it rolls to the next event on expiry/completion.
- Splash wordmark rebranded to "wenza." (regular-weight typewriter + orange dot).
- ISSUES.md: logged KC-23 (Pick Date + time-based reminders), KC-24 (Wenza
rebrand), KC-25 (Move trim), KC-26 (widget auto-advance).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
97 lines
3.0 KiB
Swift
97 lines
3.0 KiB
Swift
import SwiftUI
|
|
|
|
// MARK: - Splash View
|
|
|
|
struct SplashView: View {
|
|
@Environment(\.colorScheme) private var cs
|
|
var onDismiss: () -> Void
|
|
|
|
@State private var iconScale: CGFloat = 0.82
|
|
@State private var iconOpacity: Double = 0
|
|
@State private var markOpacity: Double = 0
|
|
@State private var splashOpacity: Double = 1
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
AppColors.background(cs).ignoresSafeArea()
|
|
|
|
VStack(spacing: 18) {
|
|
CalendarBadgeIcon()
|
|
.scaleEffect(iconScale)
|
|
.opacity(iconOpacity)
|
|
|
|
// wenza. — single line, orange dot
|
|
HStack(spacing: 0) {
|
|
Text("wenza")
|
|
.font(AppFonts.mono(22, weight: .regular))
|
|
.foregroundColor(AppColors.text(cs))
|
|
Text(".")
|
|
.font(AppFonts.mono(22, weight: .bold))
|
|
.foregroundColor(AppColors.accent)
|
|
}
|
|
.opacity(markOpacity)
|
|
}
|
|
}
|
|
.opacity(splashOpacity)
|
|
.onAppear {
|
|
withAnimation(.spring(response: 0.5, dampingFraction: 0.76).delay(0.08)) {
|
|
iconScale = 1
|
|
iconOpacity = 1
|
|
}
|
|
withAnimation(.easeOut(duration: 0.3).delay(0.3)) {
|
|
markOpacity = 1
|
|
}
|
|
withAnimation(.easeIn(duration: 0.28).delay(1.6)) {
|
|
splashOpacity = 0
|
|
}
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.95) {
|
|
onDismiss()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Calendar Badge Icon
|
|
|
|
private struct CalendarBadgeIcon: View {
|
|
@Environment(\.colorScheme) private var cs
|
|
|
|
private static let cal = Calendar.current
|
|
private static var day: Int { cal.component(.day, from: Date()) }
|
|
private static var month: String {
|
|
let f = DateFormatter(); f.dateFormat = "MMM"
|
|
return f.string(from: Date()).uppercased()
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(spacing: 0) {
|
|
// Month strip
|
|
Text(Self.month)
|
|
.font(AppFonts.mono(11, weight: .bold))
|
|
.foregroundColor(.white)
|
|
.kerning(1.5)
|
|
.frame(maxWidth: .infinity)
|
|
.frame(height: 28)
|
|
.background(AppColors.accent)
|
|
|
|
// Day number
|
|
Text("\(Self.day)")
|
|
.font(AppFonts.mono(42, weight: .bold))
|
|
.foregroundColor(AppColors.text(cs))
|
|
.frame(maxWidth: .infinity)
|
|
.frame(height: 56)
|
|
.background(AppColors.surface(cs))
|
|
}
|
|
.frame(width: 88)
|
|
.clipShape(RoundedRectangle(cornerRadius: 18))
|
|
.overlay(
|
|
RoundedRectangle(cornerRadius: 18)
|
|
.stroke(AppColors.border(cs), lineWidth: 1)
|
|
)
|
|
.shadow(
|
|
color: Color.black.opacity(cs == .dark ? 0.28 : 0.08),
|
|
radius: 20, y: 6
|
|
)
|
|
}
|
|
}
|