- Introduced session types (exams, credits, consultations, coursework defenses) with distinct icons and colors for better visibility in the UI. - Updated PlansScreen, ScheduleScreen, and WeekGrid to display session information appropriately. - Modified CalendarView and PlansView to include session indicators in the calendar and plans. - Enhanced LessonCard and CompactLesson to show session details and adjust styling based on session presence. - Added new SessionMark component for consistent session representation across Android and iOS. - Updated server logic to handle session entries from external documents, ensuring accurate timetable representation. - Improved date parsing to accommodate session-related dates in various formats. - Added tests to verify session handling and date parsing functionality.
91 lines
3.1 KiB
Swift
91 lines
3.1 KiB
Swift
import SwiftUI
|
||
import UIKit
|
||
|
||
/// The session's events: exams, credits, consultations and coursework defences. They wear an
|
||
/// amber of their own — red would read as an error — and each kind its own symbol, so they
|
||
/// stand out from ordinary lessons in the schedule, the calendar and the plans.
|
||
enum SessionKind {
|
||
case exam, credit, defence, consultation
|
||
|
||
init?(_ type: String) {
|
||
switch type {
|
||
case "Экзамен": self = .exam
|
||
case "Зачёт": self = .credit
|
||
case "Защита курсовой работы": self = .defence
|
||
case "Консультация": self = .consultation
|
||
default: return nil
|
||
}
|
||
}
|
||
|
||
var label: String {
|
||
switch self {
|
||
case .exam: "Экзамен"
|
||
case .credit: "Зачёт"
|
||
case .defence: "Защита курсовой"
|
||
case .consultation: "Консультация"
|
||
}
|
||
}
|
||
|
||
var symbol: String {
|
||
switch self {
|
||
case .exam: "pencil.and.list.clipboard"
|
||
case .credit: "checkmark.seal.fill"
|
||
case .defence: "graduationcap.fill"
|
||
case .consultation: "bubble.left.and.bubble.right.fill"
|
||
}
|
||
}
|
||
|
||
/// Exams, credits and defences are filled; consultations are tonal.
|
||
var strong: Bool { self != .consultation }
|
||
|
||
var fill: Color { strong ? .sessionStrong : .sessionSoft }
|
||
var content: Color { strong ? .onSessionStrong : .onSessionSoft }
|
||
}
|
||
|
||
extension Lesson {
|
||
var session: SessionKind? { SessionKind(type) }
|
||
}
|
||
|
||
private func dynamic(_ light: UInt32, _ dark: UInt32) -> Color {
|
||
func ui(_ hex: UInt32) -> UIColor {
|
||
UIColor(red: CGFloat((hex >> 16) & 0xFF) / 255, green: CGFloat((hex >> 8) & 0xFF) / 255, blue: CGFloat(hex & 0xFF) / 255, alpha: 1)
|
||
}
|
||
return Color(UIColor { $0.userInterfaceStyle == .dark ? ui(dark) : ui(light) })
|
||
}
|
||
|
||
extension Color {
|
||
/// The session amber (an M3 tonal palette from #8A5100): text, marks and outlines.
|
||
static let sessionAccent = dynamic(0x8A5100, 0xFFB95C)
|
||
static let sessionStrong = dynamic(0x8A5100, 0xFFB95C)
|
||
static let onSessionStrong = dynamic(0xFFFFFF, 0x482900)
|
||
static let sessionSoft = dynamic(0xFFDDB8, 0x693C00)
|
||
static let onSessionSoft = dynamic(0x2C1600, 0xFFDDB8)
|
||
}
|
||
|
||
/// "Зачёт" with its symbol, as a capsule above the subject.
|
||
struct SessionBadge: View {
|
||
var kind: SessionKind
|
||
var body: some View {
|
||
Label(kind.label, systemImage: kind.symbol)
|
||
.font(.footnote.weight(.semibold))
|
||
.padding(.horizontal, 10)
|
||
.padding(.vertical, 4)
|
||
.foregroundStyle(kind.content)
|
||
.background(kind.fill, in: .capsule)
|
||
}
|
||
}
|
||
|
||
/// A round marker with the kind's symbol, in place of a subject's dot.
|
||
struct SessionDot: View {
|
||
var kind: SessionKind
|
||
var size: CGFloat = 20
|
||
var body: some View {
|
||
Image(systemName: kind.symbol)
|
||
.font(.system(size: size * 0.5, weight: .bold))
|
||
.foregroundStyle(kind.content)
|
||
.frame(width: size, height: size)
|
||
.background(kind.fill, in: .circle)
|
||
.accessibilityHidden(true)
|
||
}
|
||
}
|