This commit is contained in:
Vladimir Dubovik 2025-02-21 14:07:01 +03:00
parent bb268cc6ad
commit 9c6515a2f5
10 changed files with 225 additions and 95 deletions

View File

@ -41,6 +41,10 @@ struct ContentView: View {
vm.group = nameGroup vm.group = nameGroup
vm.fetchWeekSchedule(group: nameGroup) vm.fetchWeekSchedule(group: nameGroup)
} }
if let vpkStr = UserDefaults.standard.string(forKey: "vpk") {
vm.fetchWeekVPK(vpk: vpkStr)
}
print(vm.vpks)
} }
} }
} }

View File

@ -13,7 +13,7 @@ struct LoadingScheduleView: View {
ZStack { ZStack {
ScrollView(.vertical, showsIndicators: false) { ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 20) { VStack(spacing: 20) {
ForEach(0..<3, id: \.self) { _ in ForEach(0..<6, id: \.self) { _ in
RoundedRectangle(cornerRadius: 20) RoundedRectangle(cornerRadius: 20)
.fill( .fill(
LinearGradient( LinearGradient(

View File

@ -13,12 +13,14 @@ struct CreatedClassView: View {
var body: some View { var body: some View {
let existingCopy = try? provider.viewContext.existingObject(with: _class.objectID) let existingCopy = try? provider.viewContext.existingObject(with: _class.objectID)
if existingCopy != nil { if existingCopy != nil {
HStack(spacing: 10) { HStack(spacing: 15) {
VStack { VStack {
Text(getTimeString(_class.starttime)) Text(getTimeString(_class.starttime))
.font(.custom("Montserrat-Regular", size: 15)) .font(.custom("Montserrat-Regular", size: 15))
.padding(.bottom, 1)
Text(getTimeString(_class.endtime)) Text(getTimeString(_class.endtime))
.font(.custom("Montserrat-Regular", size: 15)) .font(.custom("Montserrat-Regular", size: 15))
.padding(.top, 1)
} }
.frame(width: 48) .frame(width: 48)
.padding(.top, 7) .padding(.top, 7)
@ -32,8 +34,9 @@ struct CreatedClassView: View {
.foregroundColor(_class.important ? Color("redForImportant") : onlineOrNot(_class.online)) .foregroundColor(_class.important ? Color("redForImportant") : onlineOrNot(_class.online))
Text(getSubjectName(_class.subject, _class.professor, _class.auditory)) Text(getSubjectName(_class.subject, _class.professor, _class.auditory))
.font(.custom("Montserrat-Medium", size: 15)) .font(.custom("Montserrat-Medium", size: 15))
.padding(.top, 7) .lineSpacing(3)
.padding(.bottom, 7) .padding(.top, 9)
.padding(.bottom, 9)
Spacer() Spacer()
} }
.frame(maxWidth: UIScreen.main.bounds.width - 40, maxHeight: 230) .frame(maxWidth: UIScreen.main.bounds.width - 40, maxHeight: 230)

View File

@ -28,9 +28,6 @@ struct MainView: View {
} }
else { else {
ScheduleView(vm: vm, isScrolling: $isScrolling) ScheduleView(vm: vm, isScrolling: $isScrolling)
.onTapGesture {
isFocusedSearchBar = false
}
} }
} }
.alert(isPresented: $vm.isShowingAlertForIncorrectGroup, error: vm.errorInNetwork) { error in .alert(isPresented: $vm.isShowingAlertForIncorrectGroup, error: vm.errorInNetwork) { error in

View File

@ -13,6 +13,7 @@ struct ScheduleView: View {
@State private var selectedClass: ClassModel? = nil @State private var selectedClass: ClassModel? = nil
@State private var lastOffset: CGFloat = 0 @State private var lastOffset: CGFloat = 0
@State private var scrollTimer: Timer? = nil @State private var scrollTimer: Timer? = nil
@State private var isShowingMyPairs = false
@Binding var isScrolling: Bool @Binding var isScrolling: Bool
var provider = ClassProvider.shared var provider = ClassProvider.shared
var body: some View { var body: some View {
@ -23,59 +24,115 @@ struct ScheduleView: View {
if vm.errorInNetwork != .invalidResponse { if vm.errorInNetwork != .invalidResponse {
ZStack (alignment: .top) { ZStack (alignment: .top) {
ScrollView(.vertical, showsIndicators: false) { ScrollView(.vertical, showsIndicators: false) {
VStack (spacing: 20) { VStack (spacing: 30) {
ForEach(vm.classes.indices, id: \.self) { index in VStack (alignment: .leading, spacing: 20 ) {
if index != 0 && index != 1 && index == vm.selectedIndex { Text("Учебное расписание")
let daySchedule = vm.classes[index] // Это массив строк для дня .font(.custom("Montserrat-Bold", size: 20))
ForEach(daySchedule.indices.dropFirst(), id: \.self) { lessonIndex in ForEach(vm.classes.indices, id: \.self) { index in
let lesson = daySchedule[lessonIndex] // Это строка с расписанием одной пары if index != 0 && index != 1 && index == vm.selectedIndex {
if !lesson.isEmpty { let daySchedule = vm.classes[index] // Это массив строк для дня
HStack(spacing: 10) { ForEach(daySchedule.indices.dropFirst(), id: \.self) { lessonIndex in
VStack { let lesson = daySchedule[lessonIndex] // Это строка с расписанием одной пары
Text(convertTimeString(vm.classes[1][lessonIndex])[0]) if !lesson.isEmpty {
.font(.custom("Montserrat-Regular", size: 15)) HStack(spacing: 15) {
.padding(.bottom, 1) VStack {
Text(convertTimeString(vm.classes[1][lessonIndex])[1]) Text(convertTimeString(vm.classes[1][lessonIndex])[0])
.font(.custom("Montserrat-Regular", size: 15)) .font(.custom("Montserrat-Regular", size: 15))
.padding(.top, 1) .padding(.bottom, 1)
} Text(convertTimeString(vm.classes[1][lessonIndex])[1])
.frame(width: 48) .font(.custom("Montserrat-Regular", size: 15))
.padding(.top, 7) .padding(.top, 1)
.padding(.bottom, 7) }
.padding(.leading, 10) .frame(width: 48)
Rectangle()
.frame(width: 2)
.frame(maxHeight: UIScreen.main.bounds.height - 18)
.padding(.top, 7) .padding(.top, 7)
.padding(.bottom, 7) .padding(.bottom, 7)
.foregroundColor(getColorForClass(lesson)) .padding(.leading, 10)
Text(lesson) Rectangle()
.font(.custom("Montserrat-Medium", size: 15)) .frame(width: 2)
.lineSpacing(3) .frame(maxHeight: UIScreen.main.bounds.height - 18)
.padding(.top, 9) .padding(.top, 7)
.padding(.bottom, 9) .padding(.bottom, 7)
Spacer() .foregroundColor(getColorForClass(lesson))
Text(lesson)
.font(.custom("Montserrat-Medium", size: 15))
.lineSpacing(3)
.padding(.top, 9)
.padding(.bottom, 9)
Spacer()
}
.frame(maxWidth: UIScreen.main.bounds.width - 40, maxHeight: 230)
.background(Color.white)
.cornerRadius(20)
.shadow(color: .black.opacity(0.25), radius: 4, x: 2, y: 2)
} }
.frame(maxWidth: UIScreen.main.bounds.width - 40, maxHeight: 230)
.background(Color.white)
.cornerRadius(20)
.shadow(color: .black.opacity(0.25), radius: 4, x: 2, y: 2)
} }
} }
} }
} }
ForEach(classes) { _class in if classes.contains(where: { daysAreEqual($0.day, vm.selectedDay) }) {
if daysAreEqual(_class.day, vm.selectedDay) { VStack(alignment: .leading, spacing: 20) {
CreatedClassView(_class: _class) Text("Мои пары")
.onTapGesture { .font(.custom("Montserrat-Bold", size: 20))
selectedClass = _class ForEach(classes) { _class in
if daysAreEqual(_class.day, vm.selectedDay) {
CreatedClassView(_class: _class)
.onTapGesture {
selectedClass = _class
}
} }
}
}
}
if UserDefaults.standard.string(forKey: "vpk") != nil {
VStack (alignment: .leading, spacing: 20 ) {
Text("ВПК")
.font(.custom("Montserrat-Bold", size: 20))
ForEach(vm.vpks.indices, id: \.self) { index in
if index != 0 && index != 1 && index == vm.selectedIndex {
let dayVPK = vm.vpks[index] // Это массив строк для дня
ForEach(dayVPK.indices.dropFirst(), id: \.self) { lessonIndex in
let lesson = dayVPK[lessonIndex] // Это строка с расписанием одной пары
if !lesson.isEmpty {
HStack(spacing: 15) {
VStack {
Text(convertTimeString(vm.vpks[1][lessonIndex])[0])
.font(.custom("Montserrat-Regular", size: 15))
.padding(.bottom, 1)
Text(convertTimeString(vm.vpks[1][lessonIndex])[1])
.font(.custom("Montserrat-Regular", size: 15))
.padding(.top, 1)
}
.frame(width: 48)
.padding(.top, 7)
.padding(.bottom, 7)
.padding(.leading, 10)
Rectangle()
.frame(width: 2)
.frame(maxHeight: UIScreen.main.bounds.height - 18)
.padding(.top, 7)
.padding(.bottom, 7)
.foregroundColor(getColorForClass(lesson))
Text(lesson)
.font(.custom("Montserrat-Medium", size: 15))
.lineSpacing(3)
.padding(.top, 9)
.padding(.bottom, 9)
Spacer()
}
.frame(maxWidth: UIScreen.main.bounds.width - 40, maxHeight: 230)
.background(Color.white)
.cornerRadius(20)
.shadow(color: .black.opacity(0.25), radius: 4, x: 2, y: 2)
}
}
}
}
} }
} }
} }
.frame(width: UIScreen.main.bounds.width) .frame(width: UIScreen.main.bounds.width)
.padding(.bottom, 100) .padding(.bottom, 100)
.padding(.top, 30) .padding(.top, 10)
.background(GeometryReader { geometry in .background(GeometryReader { geometry in
Color.clear.preference(key: ViewOffsetKey.self, value: geometry.frame(in: .global).minY) Color.clear.preference(key: ViewOffsetKey.self, value: geometry.frame(in: .global).minY)
}) })

View File

@ -28,7 +28,6 @@ struct SearchBarView: View {
.onSubmit { .onSubmit {
self.isFocused = false self.isFocused = false
if (!text.isEmpty) { if (!text.isEmpty) {
print(vm.errorInNetwork)
vm.fetchWeekSchedule(group: text) vm.fetchWeekSchedule(group: text)
} }
self.text = "" self.text = ""

View File

@ -84,27 +84,29 @@ struct SelectingGroupView: View {
if isFocused { if isFocused {
ScrollView(.vertical, showsIndicators: true) { ScrollView(.vertical, showsIndicators: true) {
ForEach(vm.groups) { item in ForEach(vm.groups) { item in
VStack { if item.name.starts(with: "КТ") { //Отображаем только группы(без аудиторий и преподавателей)
Rectangle() VStack {
.frame(height: 1) Rectangle()
.foregroundColor(Color("customGray1")) .frame(height: 1)
.foregroundColor(Color("customGray1"))
.padding(.horizontal, 10)
HStack {
Text(item.name)
.foregroundColor(.black)
.font(.custom("Montserrat-SemiBold", size: 15))
Spacer()
}
.padding(.horizontal, 10) .padding(.horizontal, 10)
HStack { .padding(.top, 2)
Text(item.name) .padding(.bottom, 2)
.foregroundColor(.black) .frame(width: UIScreen.main.bounds.width, height: 30)
.font(.custom("Montserrat-SemiBold", size: 15)) .background(Color("background"))
Spacer() .onTapGesture {
} UserDefaults.standard.set(item.name, forKey: "group")
.padding(.horizontal, 10) vm.group = item.name
.padding(.top, 2) vm.fetchWeekSchedule(group: item.name)
.padding(.bottom, 2) dismiss()
.frame(width: UIScreen.main.bounds.width, height: 30) }
.background(Color("background"))
.onTapGesture {
UserDefaults.standard.set(item.name, forKey: "group")
vm.group = item.name
vm.fetchWeekSchedule(group: item.name)
dismiss()
} }
} }
} }

View File

@ -38,7 +38,7 @@ struct SelectingVPKView: View {
.onSubmit { .onSubmit {
self.isFocused = false self.isFocused = false
if (!text.isEmpty) { if (!text.isEmpty) {
vm.fetchWeekSchedule(group: text) vm.fetchWeekVPK(vpk: UserDefaults.standard.string(forKey: "vpk"))
self.isLoading = true self.isLoading = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.isLoading = false self.isLoading = false
@ -84,27 +84,29 @@ struct SelectingVPKView: View {
if isFocused { if isFocused {
ScrollView(.vertical, showsIndicators: true) { ScrollView(.vertical, showsIndicators: true) {
ForEach(vm.groups) { item in ForEach(vm.groups) { item in
VStack { if item.name.starts(with: "ВП") || item.name.starts(with: "мВ") {
Rectangle() VStack {
.frame(height: 1) Rectangle()
.foregroundColor(Color("customGray1")) .frame(height: 1)
.foregroundColor(Color("customGray1"))
.padding(.horizontal, 10)
HStack {
Text(item.name)
.foregroundColor(.black)
.font(.custom("Montserrat-SemiBold", size: 15))
Spacer()
}
.padding(.horizontal, 10) .padding(.horizontal, 10)
HStack { .padding(.top, 2)
Text(item.name) .padding(.bottom, 2)
.foregroundColor(.black) .frame(width: UIScreen.main.bounds.width, height: 30)
.font(.custom("Montserrat-SemiBold", size: 15)) .background(Color("background"))
Spacer() .onTapGesture {
} UserDefaults.standard.set(item.name, forKey: "vpk")
.padding(.horizontal, 10) vm.vpk = item.name
.padding(.top, 2) vm.fetchWeekVPK(vpk: item.name)
.padding(.bottom, 2) dismiss()
.frame(width: UIScreen.main.bounds.width, height: 30) }
.background(Color("background"))
.onTapGesture {
UserDefaults.standard.set(item.name, forKey: "vpk")
vm.group = item.name
vm.fetchWeekSchedule(group: item.name)
dismiss()
} }
} }
} }

View File

@ -190,6 +190,9 @@ extension WeekViewForWeek {
if vm.group != "" { if vm.group != "" {
vm.fetchWeekSchedule(isOtherWeek: true) vm.fetchWeekSchedule(isOtherWeek: true)
} }
if UserDefaults.standard.string(forKey: "vpk") != nil {
vm.fetchWeekVPK(isOtherWeek: true, vpk: UserDefaults.standard.string(forKey: "vpk"))
}
weekSlider.insert(firstDate.createPrevioustWeek(), at: 0) weekSlider.insert(firstDate.createPrevioustWeek(), at: 0)
weekSlider.removeLast() weekSlider.removeLast()
currentWeekIndex = 1 currentWeekIndex = 1
@ -203,6 +206,9 @@ extension WeekViewForWeek {
if vm.group != "" { if vm.group != "" {
vm.fetchWeekSchedule(isOtherWeek: true) vm.fetchWeekSchedule(isOtherWeek: true)
} }
if UserDefaults.standard.string(forKey: "vpk") != nil {
vm.fetchWeekVPK(isOtherWeek: true, vpk: UserDefaults.standard.string(forKey: "vpk"))
}
weekSlider.append(lastDate.createNextWeek()) weekSlider.append(lastDate.createNextWeek())
weekSlider.removeFirst() weekSlider.removeFirst()
currentWeekIndex = weekSlider.count - 2 currentWeekIndex = weekSlider.count - 2
@ -249,6 +255,9 @@ extension WeekViewForMonth {
if vm.group != "" { if vm.group != "" {
vm.fetchWeekSchedule(isOtherWeek: true) vm.fetchWeekSchedule(isOtherWeek: true)
} }
if UserDefaults.standard.string(forKey: "vpk") != nil {
vm.fetchWeekVPK(isOtherWeek: true, vpk: UserDefaults.standard.string(forKey: "vpk"))
}
} }
vm.selectedDay = day.date vm.selectedDay = day.date
vm.updateSelectedDayIndex() vm.updateSelectedDayIndex()
@ -287,6 +296,9 @@ extension MonthTabView {
if vm.group != "" { if vm.group != "" {
vm.fetchWeekSchedule(isOtherWeek: true) vm.fetchWeekSchedule(isOtherWeek: true)
} }
if let vpkStr = UserDefaults.standard.string(forKey: "vpk") {
vm.fetchWeekVPK(vpk: vpkStr)
}
} }
if let lastDate = monthSlider[currentMonthIndex].last?.week[6].date, if let lastDate = monthSlider[currentMonthIndex].last?.week[6].date,
@ -300,6 +312,9 @@ extension MonthTabView {
if vm.group != "" { if vm.group != "" {
vm.fetchWeekSchedule(isOtherWeek: true) vm.fetchWeekSchedule(isOtherWeek: true)
} }
if let vpkStr = UserDefaults.standard.string(forKey: "vpk") {
vm.fetchWeekVPK(vpk: vpkStr)
}
} }
} }
} }

View File

@ -11,7 +11,7 @@ import Foundation
final class ScheduleViewModel: ObservableObject { final class ScheduleViewModel: ObservableObject {
//MARK: Properties //MARK: Properties
//Schedule //Schedule
@Published var weekSchedule: Table = Table( @Published var weekScheduleGroup: Table = Table(
type: "", type: "",
name: "", name: "",
week: 0, week: 0,
@ -34,7 +34,17 @@ final class ScheduleViewModel: ObservableObject {
//Groups //Groups
@Published var groups: [Choice] = [] @Published var groups: [Choice] = []
//VPK //VPK
@Published var vpk: [[String]] = [] @Published var vpks: [[String]] = []
@Published var vpkHTML: String = ""
@Published var vpk: String = ""
@Published var weekScheduleVPK: Table = Table(
type: "",
name: "",
week: 0,
group: "",
table: [[]],
link: ""
)
//MARK: Methods //MARK: Methods
@ -56,10 +66,10 @@ final class ScheduleViewModel: ObservableObject {
self.isNewGroup = true self.isNewGroup = true
self.selectedDay = .init() self.selectedDay = .init()
} }
self.weekSchedule = schedule.table self.weekScheduleGroup = schedule.table
self.week = weekSchedule.week self.week = weekScheduleGroup.week
self.numOfGroup = weekSchedule.group self.numOfGroup = weekScheduleGroup.group
self.classes = weekSchedule.table self.classes = weekScheduleGroup.table
self.isFirstStartOffApp = false self.isFirstStartOffApp = false
self.isShowingAlertForIncorrectGroup = false self.isShowingAlertForIncorrectGroup = false
self.isLoading = false self.isLoading = false
@ -84,6 +94,47 @@ final class ScheduleViewModel: ObservableObject {
} }
} }
func fetchWeekVPK(isOtherWeek: Bool = false, vpk: String? = "default") {
isLoading = true
Task {
do {
var tempVPKS: Schedule
// В этот if мы заходим только если пользователь перелистывает недели и нам известы номер ВПК(в html формате) и номер недели, которая показывается пользователю
if isOtherWeek && vpk != nil {
tempVPKS = try await NetworkManager.shared.getScheduleForOtherWeek(self.week, self.vpkHTML)
}
// В else мы заходим в том случае, если не знаем номер недели, которую нужно отобразить и номер группы(в html формате)
else {
tempVPKS = try await NetworkManager.shared.getSchedule(vpk!)
self.vpk = vpk!
self.selectedDay = .init()
}
self.weekScheduleVPK = tempVPKS.table
self.vpkHTML = weekScheduleVPK.group
self.vpks = weekScheduleVPK.table
print(self.vpk)
self.isShowingAlertForIncorrectGroup = false
self.isLoading = false
self.errorInNetwork = .noError
}
catch {
if let error = error as? NetworkError {
switch (error) {
case .invalidResponse:
errorInNetwork = .invalidResponse
case .invalidData:
errorInNetwork = .invalidData
self.isShowingAlertForIncorrectGroup = true
default:
print("Неизвестная ошибка: \(error)")
}
isLoading = false
print("Есть ошибка: \(error)")
}
}
}
}
func fetchGroups(group: String) { func fetchGroups(group: String) {
Task { Task {
do { do {