redesign: replace full calendar grid with compact date badge on splash

88pt badge: accent month strip on top, large day number below.
Wordmark collapsed to single line — kisaniCAL. with orange dot.
This commit is contained in:
kutesir
2026-05-28 00:43:14 +03:00
parent d33a3d0168
commit ad8b6574fb

View File

@@ -6,173 +6,94 @@ struct SplashView: View {
@Environment(\.colorScheme) private var cs
var onDismiss: () -> Void
@State private var calendarScale: CGFloat = 0.88
@State private var calendarOpacity: Double = 0
@State private var wordmarkOpacity: Double = 0
@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: 22) {
SplashCalendarCard()
.scaleEffect(calendarScale)
.opacity(calendarOpacity)
VStack(spacing: 18) {
CalendarBadgeIcon()
.scaleEffect(iconScale)
.opacity(iconOpacity)
VStack(spacing: 1) {
// kisaniCAL. single line, orange dot
HStack(spacing: 0) {
Text("kisani")
.font(AppFonts.mono(11, weight: .regular))
.foregroundColor(AppColors.text2(cs))
.kerning(4)
HStack(alignment: .firstTextBaseline, spacing: 0) {
.font(AppFonts.mono(22, weight: .regular))
.foregroundColor(AppColors.text(cs))
Text("CAL")
.font(AppFonts.mono(38, weight: .bold))
.font(AppFonts.mono(22, weight: .bold))
.foregroundColor(AppColors.text(cs))
Text(".")
.font(AppFonts.mono(38, weight: .bold))
.font(AppFonts.mono(22, weight: .bold))
.foregroundColor(AppColors.accent)
}
}
.opacity(wordmarkOpacity)
.opacity(markOpacity)
}
}
.opacity(splashOpacity)
.onAppear {
withAnimation(.spring(response: 0.55, dampingFraction: 0.82).delay(0.05)) {
calendarScale = 1
calendarOpacity = 1
withAnimation(.spring(response: 0.5, dampingFraction: 0.76).delay(0.08)) {
iconScale = 1
iconOpacity = 1
}
withAnimation(.easeOut(duration: 0.35).delay(0.28)) {
wordmarkOpacity = 1
withAnimation(.easeOut(duration: 0.3).delay(0.3)) {
markOpacity = 1
}
withAnimation(.easeIn(duration: 0.32).delay(1.65)) {
withAnimation(.easeIn(duration: 0.28).delay(1.6)) {
splashOpacity = 0
}
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.95) {
onDismiss()
}
}
}
}
// MARK: - Calendar Card
// MARK: - Calendar Badge Icon
private struct SplashCalendarCard: View {
private struct CalendarBadgeIcon: View {
@Environment(\.colorScheme) private var cs
private static let cal = Calendar.current
private static let today = cal.component(.day, from: Date())
private static let monthFmt: DateFormatter = {
let f = DateFormatter(); f.dateFormat = "MMMM"; return f
}()
private static var days: [Int?] {
let now = Date()
let count = cal.range(of: .day, in: .month, for: now)!.count
let first = cal.date(from: cal.dateComponents([.year, .month], from: now))!
let lead = cal.component(.weekday, from: first) - 1
var grid: [Int?] = Array(repeating: nil, count: lead)
grid += (1...count).map { Optional($0) }
while grid.count % 7 != 0 { grid.append(nil) }
return grid
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) {
monthHeader
Spacer().frame(height: 10)
weekdayRow
Spacer().frame(height: 6)
dayGrid
}
.padding(.horizontal, 18)
.padding(.vertical, 16)
.frame(width: 244)
// 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))
.clipShape(RoundedRectangle(cornerRadius: 20))
}
.frame(width: 88)
.clipShape(RoundedRectangle(cornerRadius: 18))
.overlay(
RoundedRectangle(cornerRadius: 20)
RoundedRectangle(cornerRadius: 18)
.stroke(AppColors.border(cs), lineWidth: 1)
)
.shadow(
color: Color.black.opacity(cs == .dark ? 0.3 : 0.06),
radius: 24, y: 8
)
}
private var monthHeader: some View {
Text(Self.monthFmt.string(from: Date()).uppercased())
.font(AppFonts.mono(9, weight: .bold))
.foregroundColor(AppColors.text3(cs))
.kerning(2)
}
private var weekdayRow: some View {
let letters = ["S","M","T","W","T","F","S"]
return HStack(spacing: 0) {
ForEach(0..<7, id: \.self) { i in
Text(letters[i])
.font(AppFonts.mono(7.5, weight: .bold))
.foregroundColor(AppColors.text3(cs))
.frame(maxWidth: .infinity)
}
}
}
private var dayGrid: some View {
let rows = Self.days.count / 7
return VStack(spacing: 0) {
ForEach(0..<rows, id: \.self) { row in
SplashWeekRow(
days: Array(Self.days[(row * 7)..<(row * 7 + 7)]),
today: Self.today,
cs: cs
color: Color.black.opacity(cs == .dark ? 0.28 : 0.08),
radius: 20, y: 6
)
}
}
}
}
// MARK: - Week Row
private struct SplashWeekRow: View {
let days: [Int?]
let today: Int
let cs: ColorScheme
var body: some View {
HStack(spacing: 0) {
ForEach(0..<7, id: \.self) { col in
SplashDayCell(day: days[col], isToday: days[col] == today, cs: cs)
}
}
}
}
// MARK: - Day Cell
private struct SplashDayCell: View {
let day: Int?
let isToday: Bool
let cs: ColorScheme
var body: some View {
ZStack {
if isToday {
Circle()
.fill(AppColors.accent)
.frame(width: 26, height: 26)
}
if let d = day {
Text("\(d)")
.font(AppFonts.mono(isToday ? 10 : 9.5,
weight: isToday ? .bold : .regular))
.foregroundColor(isToday ? .white : AppColors.text(cs).opacity(0.72))
}
}
.frame(maxWidth: .infinity)
.frame(height: 28)
}
}