import SwiftUI struct TimelineRow: View { let entry: TimelineEntry var body: some View { HStack(spacing: 12) { ZStack { RoundedRectangle(cornerRadius: 8, style: .continuous) .fill(entry.kind.accent.opacity(0.12)) Image(systemName: entry.kind.icon) .font(.system(size: 14, weight: .semibold)) .foregroundStyle(entry.kind.accent) } .frame(width: 36, height: 36) .overlay(alignment: .topTrailing) { if entry.isOngoing { Circle() .fill(Tj.Palette.brick) .frame(width: 7, height: 7) .overlay(Circle().strokeBorder(Tj.Palette.sand, lineWidth: 1.5)) .offset(x: 3, y: -3) } } VStack(alignment: .leading, spacing: 2) { Text("\(entry.date.timelineLabel) · \(entry.subtitle)") .font(.system(size: 11)) .tracking(0.3) .foregroundStyle(Tj.Palette.text3) .lineLimit(1) Text(entry.title) .font(.system(size: 14, weight: .medium)) .foregroundStyle(Tj.Palette.text) .lineLimit(1) .truncationMode(.tail) } Spacer(minLength: 8) if let trailing = entry.trailing { Text(trailing) .font(.system(size: 12, weight: .semibold, design: .monospaced)) .foregroundStyle(entry.trailingIsAlert ? Tj.Palette.brick : Tj.Palette.text2) .lineLimit(1) .fixedSize() } } .padding(12) .tjCard(bordered: true) } } extension Date { var timelineLabel: String { let cal = Calendar.current if cal.isDateInToday(self) { return self.formatted(date: .omitted, time: .shortened) } if cal.isDateInYesterday(self) { return String(appLoc: "昨天") + " " + self.formatted(date: .omitted, time: .shortened) } let now = Date.now if cal.isDate(self, equalTo: now, toGranularity: .year) { return self.formatted(.dateTime.month().day()) } return self.formatted(.dateTime.year().month().day()) } }