import SwiftUI struct SettingsView: View { @Environment(AppModel.self) private var model @Environment(\.dismiss) private var dismiss @State private var changes: ScheduleChanges? var body: some View { let s = model.settings let d = s.display NavigationStack { Form { Section("Тема") { Picker("Тема", selection: Binding(get: { s.theme }, set: { model.setTheme($0) })) { Text("Авто").tag(ThemeMode.system) Text("Светлая").tag(ThemeMode.light) Text("Тёмная").tag(ThemeMode.dark) } .pickerStyle(.segmented) } Section { toggle("Цвета предметов", "Каждый предмет своим цветом в карточках, сетке и обзоре", d.colors) { var x = d; x.colors = $0; model.setDisplay(x) } toggle("Преподаватели", "Фамилии в карточках занятий", d.teachers) { var x = d; x.teachers = $0; model.setDisplay(x) } toggle("Аудитории", "Номера аудиторий в карточках и в сетке", d.rooms) { var x = d; x.rooms = $0; model.setDisplay(x) } toggle("Номера пар", "«2 пара» рядом со временем", d.pairs) { var x = d; x.pairs = $0; model.setDisplay(x) } toggle("Метки занятий", "Лекция или практика, белые и зелёные недели, общее занятие", d.tags) { var x = d; x.tags = $0; model.setDisplay(x) } toggle("Пометки из Word", "Например, «3-13н.» — даты по ним уже рассчитаны", d.notes) { var x = d; x.notes = $0; model.setDisplay(x) } } header: { Text("Расписание") } footer: { Text("Полные сведения о занятии всегда открываются по нажатию на карточку.") } if !s.hidden.isEmpty { Section("Скрытые занятия") { ForEach(s.hidden.sorted(), id: \.self) { key in let parts = key.split(separator: "|", maxSplits: 1).map(String.init) HStack { Text((parts.first ?? key).prefix(1).uppercased() + (parts.first ?? key).dropFirst() + (parts.count > 1 && !parts[1].isEmpty ? " · " + parts[1] : "")) Spacer() Button("Показать") { model.setHidden(key, false) } } } } } Section { Toggle(isOn: Binding(get: { s.reminders }, set: { on in if on { Task { model.setReminders(await Reminders.authorize()) } } else { model.setReminders(false) } })) { VStack(alignment: .leading, spacing: 2) { Text("Перед парой") Text("Уведомление с предметом и аудиторией. Работает без интернета, по сохранённому расписанию.") .font(.footnote).foregroundStyle(.secondary) } } if s.reminders { Picker("За сколько минут", selection: Binding(get: { s.reminderMinutes }, set: { model.setReminderMinutes($0) })) { ForEach([5, 10, 15, 30], id: \.self) { Text("\($0)").tag($0) } } .pickerStyle(.segmented) } } header: { Text("Напоминания") } Section { LabeledContent("Документ", value: model.timetable?.document.name ?? "Группа не выбрана") if let sem = model.timetable?.semester { LabeledContent("Семестр", value: sem.label) } // The last republication of the document, also after its card was closed. if let record = model.changes { Button { changes = record } label: { LabeledContent("Последние изменения", value: changesLabel(record.count) + " · " + CivilDate(record.at).longLabel) .foregroundStyle(.primary) } } LabeledContent("Сервер", value: model.api.base.absoluteString) } header: { Text("О расписании") } footer: { Text("Расписание берётся из публичного каталога ПГУ и пересчитывается по таблице белых и зелёных недель. Приложение неофициальное: при сомнениях сверяйтесь с документом Word.") } } .navigationTitle("Настройки") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .confirmationAction) { Button("Готово", systemImage: "checkmark") { dismiss() } } } .sheet(item: $changes) { ChangesSheet(record: $0).presentationDetents([.medium, .large]) } } } private func toggle(_ title: String, _ text: String, _ on: Bool, _ set: @escaping (Bool) -> Void) -> some View { Toggle(isOn: Binding(get: { on }, set: set)) { VStack(alignment: .leading, spacing: 2) { Text(title) Text(text).font(.footnote).foregroundStyle(.secondary) } } } }