Files
psu-schedule/ios/PSUSchedule/Views/SessionMark.swift
Zakhar Timoshenko 15851b26cd feat: Enhance session handling in timetable and UI
- 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.
2026-10-19 20:49:13 +03:00

91 lines
3.1 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}
}