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 @Environment(\.colorScheme) private var cs
var onDismiss: () -> Void var onDismiss: () -> Void
@State private var calendarScale: CGFloat = 0.88 @State private var iconScale: CGFloat = 0.82
@State private var calendarOpacity: Double = 0 @State private var iconOpacity: Double = 0
@State private var wordmarkOpacity: Double = 0 @State private var markOpacity: Double = 0
@State private var splashOpacity: Double = 1 @State private var splashOpacity: Double = 1
var body: some View { var body: some View {
ZStack { ZStack {
AppColors.background(cs).ignoresSafeArea() AppColors.background(cs).ignoresSafeArea()
VStack(spacing: 22) { VStack(spacing: 18) {
SplashCalendarCard() CalendarBadgeIcon()
.scaleEffect(calendarScale) .scaleEffect(iconScale)
.opacity(calendarOpacity) .opacity(iconOpacity)
VStack(spacing: 1) { // kisaniCAL. single line, orange dot
HStack(spacing: 0) {
Text("kisani") Text("kisani")
.font(AppFonts.mono(11, weight: .regular)) .font(AppFonts.mono(22, weight: .regular))
.foregroundColor(AppColors.text2(cs)) .foregroundColor(AppColors.text(cs))
.kerning(4) Text("CAL")
.font(AppFonts.mono(22, weight: .bold))
HStack(alignment: .firstTextBaseline, spacing: 0) { .foregroundColor(AppColors.text(cs))
Text("CAL") Text(".")
.font(AppFonts.mono(38, weight: .bold)) .font(AppFonts.mono(22, weight: .bold))
.foregroundColor(AppColors.text(cs)) .foregroundColor(AppColors.accent)
Text(".")
.font(AppFonts.mono(38, weight: .bold))
.foregroundColor(AppColors.accent)
}
} }
.opacity(wordmarkOpacity) .opacity(markOpacity)
} }
} }
.opacity(splashOpacity) .opacity(splashOpacity)
.onAppear { .onAppear {
withAnimation(.spring(response: 0.55, dampingFraction: 0.82).delay(0.05)) { withAnimation(.spring(response: 0.5, dampingFraction: 0.76).delay(0.08)) {
calendarScale = 1 iconScale = 1
calendarOpacity = 1 iconOpacity = 1
} }
withAnimation(.easeOut(duration: 0.35).delay(0.28)) { withAnimation(.easeOut(duration: 0.3).delay(0.3)) {
wordmarkOpacity = 1 markOpacity = 1
} }
withAnimation(.easeIn(duration: 0.32).delay(1.65)) { withAnimation(.easeIn(duration: 0.28).delay(1.6)) {
splashOpacity = 0 splashOpacity = 0
} }
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { DispatchQueue.main.asyncAfter(deadline: .now() + 1.95) {
onDismiss() onDismiss()
} }
} }
} }
} }
// MARK: - Calendar Card // MARK: - Calendar Badge Icon
private struct SplashCalendarCard: View { private struct CalendarBadgeIcon: View {
@Environment(\.colorScheme) private var cs @Environment(\.colorScheme) private var cs
private static let cal = Calendar.current private static let cal = Calendar.current
private static let today = cal.component(.day, from: Date()) private static var day: Int { cal.component(.day, from: Date()) }
private static let monthFmt: DateFormatter = { private static var month: String {
let f = DateFormatter(); f.dateFormat = "MMMM"; return f let f = DateFormatter(); f.dateFormat = "MMM"
}() return f.string(from: Date()).uppercased()
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
} }
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
monthHeader // Month strip
Spacer().frame(height: 10) Text(Self.month)
weekdayRow .font(AppFonts.mono(11, weight: .bold))
Spacer().frame(height: 6) .foregroundColor(.white)
dayGrid .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))
} }
.padding(.horizontal, 18) .frame(width: 88)
.padding(.vertical, 16) .clipShape(RoundedRectangle(cornerRadius: 18))
.frame(width: 244)
.background(AppColors.surface(cs))
.clipShape(RoundedRectangle(cornerRadius: 20))
.overlay( .overlay(
RoundedRectangle(cornerRadius: 20) RoundedRectangle(cornerRadius: 18)
.stroke(AppColors.border(cs), lineWidth: 1) .stroke(AppColors.border(cs), lineWidth: 1)
) )
.shadow( .shadow(
color: Color.black.opacity(cs == .dark ? 0.3 : 0.06), color: Color.black.opacity(cs == .dark ? 0.28 : 0.08),
radius: 24, y: 8 radius: 20, y: 6
) )
} }
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
)
}
}
}
}
// 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)
}
} }