Compare commits

..

No commits in common. "9bfd85ec3dbeb87dcfb720fef6e684f6cdd26286" and "13de6fa302c2d514b6496d9a08995e491a7efaee" have entirely different histories.

14 changed files with 443 additions and 690 deletions

View File

@ -36,20 +36,15 @@ struct ContentView: View {
} }
.accentColor(Color("blueColor")) .accentColor(Color("blueColor"))
.onAppear { .onAppear {
let group1 = UserDefaults.standard.string(forKey: "group") let group = UserDefaults.standard.string(forKey: "group")
let group2 = UserDefaults.standard.string(forKey: "group2") if let nameGroup = group {
let group3 = UserDefaults.standard.string(forKey: "group3") vm.group = nameGroup
if let nameGroup1 = group1, nameGroup1 != "" { vm.fetchWeekSchedule(group: nameGroup)
vm.nameGroups.append(nameGroup1)
} }
if let nameGroup2 = group2, nameGroup2 != "" { if let vpkStr = UserDefaults.standard.string(forKey: "vpk") {
vm.nameGroups.append(nameGroup2) vm.fetchWeekVPK(vpk: vpkStr)
} }
if let nameGroup3 = group3, nameGroup3 != "" { print(vm.vpks)
vm.nameGroups.append(nameGroup3)
}
print("\(group1) - \(group2) - \(group3)")
vm.fetchWeekSchedule()
} }
} }
} }

View File

@ -16,6 +16,10 @@ struct ScheduleView: View {
@State private var isShowingMyPairs = false @State private var isShowingMyPairs = false
@Binding var isScrolling: Bool @Binding var isScrolling: Bool
var provider = ClassProvider.shared var provider = ClassProvider.shared
var hasLessons: Bool {
return vm.classes.indices.contains(vm.selectedIndex) &&
vm.classes[vm.selectedIndex].dropFirst().contains { !$0.isEmpty }
}
var hasVPK: Bool { var hasVPK: Bool {
return vm.vpks.indices.contains(vm.selectedIndex) && vm.vpks[vm.selectedIndex].dropFirst().contains { !$0.isEmpty } return vm.vpks.indices.contains(vm.selectedIndex) && vm.vpks[vm.selectedIndex].dropFirst().contains { !$0.isEmpty }
} }
@ -28,20 +32,23 @@ struct ScheduleView: View {
ZStack (alignment: .top) { ZStack (alignment: .top) {
ScrollView(.vertical, showsIndicators: false) { ScrollView(.vertical, showsIndicators: false) {
VStack (spacing: 30) { VStack (spacing: 30) {
VStack (alignment: .leading, spacing: 10) { VStack (alignment: .leading, spacing: 20 ) {
ForEach(0..<vm.classesGroups.count, id: \.self) { dayIndex in if hasLessons {
if dayIndex == vm.selectedIndex { Text("Учебное расписание")
ForEach(vm.classesGroups[dayIndex]) { info in .font(.custom("Montserrat-Bold", fixedSize: 20))
VStack (alignment: .trailing) { }
Text(info.group) ForEach(vm.classes.indices, id: \.self) { index in
.font(.custom("Montserrat-Regular", fixedSize: 11)) if index != 0 && index != 1 && index == vm.selectedIndex {
.foregroundColor(Color("grayForNameGroup")) let daySchedule = vm.classes[index] // Это массив строк для дня
ForEach(daySchedule.indices.dropFirst(), id: \.self) { lessonIndex in
let lesson = daySchedule[lessonIndex] // Это строка с расписанием одной пары
if !lesson.isEmpty {
HStack(spacing: 15) { HStack(spacing: 15) {
VStack { VStack {
Text(convertTimeString(info.time)[0]) Text(convertTimeString(vm.classes[1][lessonIndex])[0])
.font(.custom("Montserrat-Regular", fixedSize: 15)) .font(.custom("Montserrat-Regular", fixedSize: 15))
.padding(.bottom, 1) .padding(.bottom, 1)
Text(convertTimeString(info.time)[1]) Text(convertTimeString(vm.classes[1][lessonIndex])[1])
.font(.custom("Montserrat-Regular", fixedSize: 15)) .font(.custom("Montserrat-Regular", fixedSize: 15))
.padding(.top, 1) .padding(.top, 1)
} }
@ -54,8 +61,8 @@ struct ScheduleView: View {
.frame(maxHeight: UIScreen.main.bounds.height - 18) .frame(maxHeight: UIScreen.main.bounds.height - 18)
.padding(.top, 7) .padding(.top, 7)
.padding(.bottom, 7) .padding(.bottom, 7)
.foregroundColor(getColorForClass(info.subject)) .foregroundColor(getColorForClass(lesson))
Text(info.subject) Text(lesson)
.font(.custom("Montserrat-Medium", fixedSize: 16)) .font(.custom("Montserrat-Medium", fixedSize: 16))
.lineSpacing(3) .lineSpacing(3)
.padding(.top, 9) .padding(.top, 9)
@ -85,6 +92,54 @@ struct ScheduleView: View {
} }
} }
} }
if UserDefaults.standard.string(forKey: "vpk") != nil {
VStack (alignment: .leading, spacing: 20 ) {
if hasVPK {
Text("ВПК")
.font(.custom("Montserrat-Bold", fixedSize: 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", fixedSize: 15))
.padding(.bottom, 1)
Text(convertTimeString(vm.vpks[1][lessonIndex])[1])
.font(.custom("Montserrat-Regular", fixedSize: 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", fixedSize: 16))
.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)

View File

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

View File

@ -21,10 +21,3 @@ struct Table: Decodable {
let table: [[String]] let table: [[String]]
let link: String let link: String
} }
struct ClassInfo: Identifiable {
let id = UUID()
let subject: String
let group: String
let time: String
}

View File

@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x89",
"green" : "0x89",
"red" : "0x89"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x89",
"green" : "0x89",
"red" : "0x89"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,102 +0,0 @@
//
// FavGroupsView.swift
// Schedule ICTIS
//
// Created by G412 on 05.03.2025.
//
import SwiftUI
struct FavGroupsView: View {
@ObservedObject var vm: ScheduleViewModel
var firstFavGroup = (UserDefaults.standard.string(forKey: "group") ?? "")
var secondFavGroup = (UserDefaults.standard.string(forKey: "group2") ?? "")
var thirdFavGroup = (UserDefaults.standard.string(forKey: "group3") ?? "")
var body: some View {
VStack (spacing: 0) {
List {
if firstFavGroup != "" {
HStack {
Text(firstFavGroup)
.font(.custom("Montserrat-Medium", fixedSize: 17))
Spacer()
}
.background(Color.white)
.cornerRadius(10)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
UserDefaults.standard.set("", forKey: "group")
vm.updateArrayOfGroups()
vm.fetchWeekSchedule()
} label: {
Label("Удалить", systemImage: "trash")
}
}
}
if secondFavGroup != "" {
HStack {
Text(secondFavGroup)
.font(.custom("Montserrat-Medium", fixedSize: 17))
Spacer()
}
.background(Color.white)
.cornerRadius(10)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
UserDefaults.standard.set("", forKey: "group2")
vm.updateArrayOfGroups()
vm.fetchWeekSchedule()
} label: {
Label("Удалить", systemImage: "trash")
}
}
}
if thirdFavGroup != "" {
HStack {
Text(thirdFavGroup)
.font(.custom("Montserrat-Medium", fixedSize: 17))
Spacer()
}
.background(Color.white)
.cornerRadius(10)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
UserDefaults.standard.set("", forKey: "group3")
vm.updateArrayOfGroups()
vm.fetchWeekSchedule()
} label: {
Label("Удалить", systemImage: "trash")
}
}
}
}
.frame(maxHeight: 400)
Spacer()
HStack {
Spacer()
if firstFavGroup == "" || secondFavGroup == "" || thirdFavGroup == "" {
NavigationLink(destination: SelectingGroupView(vm: vm, firstFavGroup: firstFavGroup, secondFavGroup: secondFavGroup, thirdFavGroup: thirdFavGroup)) {
HStack {
Image(systemName: "plus")
.foregroundColor(.white)
.font(.system(size: 22))
.padding(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12))
}
.background(Color("blueColor"))
.cornerRadius(10)
.padding(.trailing, 20)
}
}
}
.padding(.bottom, 50)
}
.background(Color("background"))
}
}
#Preview {
@Previewable @StateObject var vm = ScheduleViewModel()
FavGroupsView(vm: vm)
}

View File

@ -1,102 +0,0 @@
//
// FavGroupsView.swift
// Schedule ICTIS
//
// Created by G412 on 05.03.2025.
//
import SwiftUI
struct FavVPKView: View {
@ObservedObject var vm: ScheduleViewModel
var firstFavVPK = (UserDefaults.standard.string(forKey: "vpk1") ?? "")
var secondFavVPK = (UserDefaults.standard.string(forKey: "vpk2") ?? "")
var thirdFavVPK = (UserDefaults.standard.string(forKey: "vpk3") ?? "")
var body: some View {
VStack (spacing: 0) {
List {
if firstFavVPK != "" {
HStack {
Text(firstFavVPK)
.font(.custom("Montserrat-Medium", fixedSize: 17))
Spacer()
}
.background(Color.white)
.cornerRadius(10)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
UserDefaults.standard.set("", forKey: "vpk1")
vm.updateArrayOfGroups()
vm.fetchWeekSchedule()
} label: {
Label("Удалить", systemImage: "trash")
}
}
}
if secondFavVPK != "" {
HStack {
Text(secondFavVPK)
.font(.custom("Montserrat-Medium", fixedSize: 17))
Spacer()
}
.background(Color.white)
.cornerRadius(10)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
UserDefaults.standard.set("", forKey: "vpk2")
vm.updateArrayOfGroups()
vm.fetchWeekSchedule()
} label: {
Label("Удалить", systemImage: "trash")
}
}
}
if thirdFavVPK != "" {
HStack {
Text(thirdFavVPK)
.font(.custom("Montserrat-Medium", fixedSize: 17))
Spacer()
}
.background(Color.white)
.cornerRadius(10)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
UserDefaults.standard.set("", forKey: "vpk3")
vm.updateArrayOfGroups()
vm.fetchWeekSchedule()
} label: {
Label("Удалить", systemImage: "trash")
}
}
}
}
.frame(maxHeight: 400)
Spacer()
HStack {
Spacer()
if firstFavVPK == "" || secondFavVPK == "" || thirdFavVPK == "" {
NavigationLink(destination: SelectingVPKView(vm: vm, firstFavVPK: firstFavVPK, secondFavVPK: secondFavVPK, thirdFavVPK: thirdFavVPK)) {
HStack {
Image(systemName: "plus")
.foregroundColor(.white)
.font(.system(size: 22))
.padding(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12))
}
.background(Color("blueColor"))
.cornerRadius(10)
.padding(.trailing, 20)
}
}
}
.padding(.bottom, 50)
}
.background(Color("background"))
}
}
#Preview {
@Previewable @StateObject var vm = ScheduleViewModel()
FavVPKView(vm: vm)
}

View File

@ -8,15 +8,20 @@
import SwiftUI import SwiftUI
struct ScheduleGroupSettings: View { struct ScheduleGroupSettings: View {
@AppStorage("group") private var favGroup = ""
@AppStorage("vpk") private var favVPK = ""
@ObservedObject var vm: ScheduleViewModel @ObservedObject var vm: ScheduleViewModel
var body: some View { var body: some View {
VStack { VStack {
NavigationLink(destination: FavGroupsView(vm: vm)) { NavigationLink(destination: SelectingGroupView(vm: vm)) {
HStack { HStack {
Text("Избранное расписание") Text("Избранное расписание")
.font(.custom("Montserrat-Medium", fixedSize: 17)) .font(.custom("Montserrat-Medium", fixedSize: 17))
.foregroundColor(.black) .foregroundColor(.black)
Spacer() Spacer()
Text(favGroup)
.font(.custom("Montserrat-Medium", fixedSize: 17))
.foregroundColor(Color("customGray3"))
Image("arrowRight") Image("arrowRight")
} }
.padding(.horizontal) .padding(.horizontal)
@ -27,12 +32,15 @@ struct ScheduleGroupSettings: View {
.foregroundColor(Color("customGray1")) .foregroundColor(Color("customGray1"))
.frame(height: 1) .frame(height: 1)
.padding(.horizontal) .padding(.horizontal)
NavigationLink(destination: FavVPKView(vm: vm)) { NavigationLink(destination: SelectingVPKView(vm: vm)) {
HStack { HStack {
Text("ВПК") Text("ВПК")
.font(.custom("Montserrat-Medium", fixedSize: 17)) .font(.custom("Montserrat-Medium", fixedSize: 17))
.foregroundColor(.black) .foregroundColor(.black)
Spacer() Spacer()
Text(favVPK)
.font(.custom("Montserrat-Medium", fixedSize: 17))
.foregroundColor(Color("customGray3"))
Image("arrowRight") Image("arrowRight")
} }
.padding(.horizontal) .padding(.horizontal)

View File

@ -14,132 +14,142 @@ struct SelectingGroupView: View {
@ObservedObject var vm: ScheduleViewModel @ObservedObject var vm: ScheduleViewModel
@State private var isLoading = false @State private var isLoading = false
@State private var searchTask: DispatchWorkItem? @State private var searchTask: DispatchWorkItem?
@StateObject private var serchGroupsVM = SearchGroupsViewModel() @AppStorage("group") private var favGroup = ""
var firstFavGroup: String
var secondFavGroup: String
var thirdFavGroup: String
var body: some View { var body: some View {
VStack { NavigationView {
HStack (spacing: 0) { VStack {
Image(systemName: "magnifyingglass") HStack (spacing: 0) {
.foregroundColor(Color.gray) Image(systemName: "magnifyingglass")
.padding(.leading, 12) .foregroundColor(Color.gray)
.padding(.trailing, 7) .padding(.leading, 12)
TextField("Поиск группы", text: $text) .padding(.trailing, 7)
.disableAutocorrection(true) TextField("Поиск группы", text: $text)
.focused($isFocused) .disableAutocorrection(true)
.onChange(of: text) { oldValue, newValue in .focused($isFocused)
searchTask?.cancel() .onChange(of: text) { oldValue, newValue in
let task = DispatchWorkItem { searchTask?.cancel()
if !text.isEmpty { let task = DispatchWorkItem {
serchGroupsVM.fetchGroups(group: text) if !text.isEmpty {
} vm.fetchGroups(group: text)
else {
serchGroupsVM.fetchGroups(group: "кт")
}
}
searchTask = task
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: task)
}
.onSubmit {
self.isFocused = false
if (!text.isEmpty) {
vm.fetchWeekSchedule(isOtherWeek: false)
self.isLoading = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if vm.errorInNetwork == .noError {
vm.errorInNetwork = nil
if firstFavGroup == "" {
UserDefaults.standard.set(text, forKey: "group")
} else if secondFavGroup == "" {
UserDefaults.standard.set(text, forKey: "group2")
} else {
UserDefaults.standard.set(text, forKey: "group3")
}
vm.updateArrayOfGroups()
vm.fetchWeekSchedule()
self.isLoading = false
self.text = ""
dismiss()
} }
else { else {
vm.isShowingAlertForIncorrectGroup = true vm.fetchGroups(group: "кт")
vm.errorInNetwork = .invalidResponse
} }
} }
searchTask = task
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: task)
} }
} .onSubmit {
.submitLabel(.done) self.isFocused = false
if isFocused { if (!text.isEmpty) {
Button { vm.fetchWeekSchedule(group: text)
self.text = "" self.isLoading = true
self.isFocused = false DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
} label: { self.isLoading = false
Image(systemName: "xmark.circle.fill") if vm.errorInNetwork == .noError {
.padding(.trailing, 20) vm.errorInNetwork = nil
.offset(x: 10) print("Зашел")
.foregroundColor(.gray) UserDefaults.standard.set(text, forKey: "group")
.background( vm.group = text
) self.text = ""
} dismiss()
} }
} else {
.frame(height: 40) vm.isShowingAlertForIncorrectGroup = true
.background( vm.errorInNetwork = .invalidResponse
RoundedRectangle(cornerRadius: 15) }
.fill(.white) }
) }
Spacer() }
if isLoading { .submitLabel(.done)
LoadingView(isLoading: $isLoading) if isFocused {
} Button {
if isFocused { self.text = ""
ScrollView(.vertical, showsIndicators: true) { self.isFocused = false
ForEach(serchGroupsVM.groups) { item in } label: {
if item.name.starts(with: "КТ") { //Отображаем только группы(без аудиторий и преподавателей) Image(systemName: "xmark.circle.fill")
VStack { .padding(.trailing, 20)
Rectangle() .offset(x: 10)
.frame(height: 1) .foregroundColor(.gray)
.foregroundColor(Color("customGray1")) .background(
.padding(.horizontal, 10) )
HStack { }
Text(item.name) }
.foregroundColor(.black) }
.font(.custom("Montserrat-SemiBold", fixedSize: 15)) .frame(height: 40)
Spacer() .background(
} RoundedRectangle(cornerRadius: 15)
.padding(.horizontal, 10) .fill(.white)
.padding(.top, 2) )
.padding(.bottom, 2) Spacer()
.frame(width: UIScreen.main.bounds.width, height: 30) if isLoading {
.background(Color("background")) LoadingView(isLoading: $isLoading)
.onTapGesture { }
if firstFavGroup == "" { if isFocused {
UserDefaults.standard.set(item.name, forKey: "group") ScrollView(.vertical, showsIndicators: true) {
} else if secondFavGroup == "" { ForEach(vm.groups) { item in
UserDefaults.standard.set(item.name, forKey: "group2") if item.name.starts(with: "КТ") { //Отображаем только группы(без аудиторий и преподавателей)
} else { VStack {
UserDefaults.standard.set(item.name, forKey: "group3") Rectangle()
.frame(height: 1)
.foregroundColor(Color("customGray1"))
.padding(.horizontal, 10)
HStack {
Text(item.name)
.foregroundColor(.black)
.font(.custom("Montserrat-SemiBold", fixedSize: 15))
Spacer()
}
.padding(.horizontal, 10)
.padding(.top, 2)
.padding(.bottom, 2)
.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()
} }
vm.updateArrayOfGroups()
vm.fetchWeekSchedule()
dismiss()
} }
} }
} }
} }
} else {
if favGroup != "" {
Button {
UserDefaults.standard.removeObject(forKey: "group")
vm.classes.removeAll()
vm.group = ""
vm.numOfGroup = ""
dismiss()
} label: {
HStack {
Spacer()
Image(systemName: "trash")
Text("Удалить группу")
.font(.custom("Montserrat-Medium", fixedSize: 17))
Spacer()
}
.frame(height: 40)
.background(Color.white)
.foregroundColor(Color.red)
.cornerRadius(10)
.padding(.bottom, 50)
}
}
} }
} }
.padding(.horizontal, 10)
.background(Color("background"))
} }
.padding(.horizontal, 10)
.background(Color("background"))
.onAppear { .onAppear {
serchGroupsVM.fetchGroups(group: "кт") vm.fetchGroups(group: "кт")
} }
} }
} }
#Preview { #Preview {
@Previewable @StateObject var vm = ScheduleViewModel() @Previewable @StateObject var vm = ScheduleViewModel()
SelectingGroupView(vm: vm, firstFavGroup: "", secondFavGroup: "", thirdFavGroup: "") SelectingGroupView(vm: vm)
} }

View File

@ -1,5 +1,5 @@
// //
// SelectedGroupView.swift // SelectedVPKView.swift
// Schedule ICTIS // Schedule ICTIS
// //
// Created by Mironov Egor on 30.01.2025. // Created by Mironov Egor on 30.01.2025.
@ -14,133 +14,143 @@ struct SelectingVPKView: View {
@ObservedObject var vm: ScheduleViewModel @ObservedObject var vm: ScheduleViewModel
@State private var isLoading = false @State private var isLoading = false
@State private var searchTask: DispatchWorkItem? @State private var searchTask: DispatchWorkItem?
@StateObject private var serchGroupsVM = SearchGroupsViewModel() @AppStorage("vpk") private var favVPK = ""
var firstFavVPK: String
var secondFavVPK: String
var thirdFavVPK: String
var body: some View { var body: some View {
VStack { NavigationView {
HStack (spacing: 0) { VStack {
Image(systemName: "magnifyingglass") HStack (spacing: 0) {
.foregroundColor(Color.gray) Image(systemName: "magnifyingglass")
.padding(.leading, 12) .foregroundColor(Color.gray)
.padding(.trailing, 7) .padding(.leading, 12)
TextField("Поиск ВПК", text: $text) .padding(.trailing, 7)
.disableAutocorrection(true) TextField("Поиск ВПК", text: $text)
.focused($isFocused) .disableAutocorrection(true)
.onChange(of: text) { oldValue, newValue in .focused($isFocused)
searchTask?.cancel() .onChange(of: text) { oldValue, newValue in
let task = DispatchWorkItem { searchTask?.cancel()
if !text.isEmpty { let task = DispatchWorkItem {
serchGroupsVM.fetchGroups(group: text) if !text.isEmpty {
} vm.fetchGroups(group: text)
else {
serchGroupsVM.fetchGroups(group: "ВПК")
}
}
searchTask = task
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: task)
}
.onSubmit {
self.isFocused = false
if (!text.isEmpty) {
vm.fetchWeekSchedule(isOtherWeek: false)
self.isLoading = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if vm.errorInNetwork == .noError {
vm.errorInNetwork = nil
if firstFavVPK == "" {
UserDefaults.standard.set(text, forKey: "vpk1")
} else if secondFavVPK == "" {
UserDefaults.standard.set(text, forKey: "vpk2")
} else {
UserDefaults.standard.set(text, forKey: "vpk3")
}
vm.updateArrayOfGroups()
vm.fetchWeekSchedule()
self.isLoading = false
self.text = ""
print("✅ - Избранный ВПК был установлен")
dismiss()
} }
else { else {
vm.isShowingAlertForIncorrectGroup = true vm.fetchGroups(group: "впк")
vm.errorInNetwork = .invalidResponse
} }
} }
searchTask = task
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: task)
} }
} .onSubmit {
.submitLabel(.done) self.isFocused = false
if isFocused { if (!text.isEmpty) {
Button { vm.fetchWeekVPK(vpk: UserDefaults.standard.string(forKey: "vpk"))
self.text = "" self.isLoading = true
self.isFocused = false DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
} label: { self.isLoading = false
Image(systemName: "xmark.circle.fill") if vm.errorInNetwork == .noError {
.padding(.trailing, 20) vm.errorInNetwork = nil
.offset(x: 10) print("Зашел")
.foregroundColor(.gray) UserDefaults.standard.set(text, forKey: "vpk")
.background( vm.group = text
) self.text = ""
} dismiss()
} }
} else {
.frame(height: 40) vm.isShowingAlertForIncorrectGroup = true
.background( vm.errorInNetwork = .invalidResponse
RoundedRectangle(cornerRadius: 15) }
.fill(.white) }
) }
Spacer() }
if isLoading { .submitLabel(.done)
LoadingView(isLoading: $isLoading) if isFocused {
} Button {
if isFocused { self.text = ""
ScrollView(.vertical, showsIndicators: true) { self.isFocused = false
ForEach(serchGroupsVM.groups) { item in } label: {
if item.name.starts(with: "ВПК") { Image(systemName: "xmark.circle.fill")
VStack { .padding(.trailing, 20)
Rectangle() .offset(x: 10)
.frame(height: 1) .foregroundColor(.gray)
.foregroundColor(Color("customGray1")) .background(
.padding(.horizontal, 10) )
HStack { }
Text(item.name) }
.foregroundColor(.black) }
.font(.custom("Montserrat-SemiBold", fixedSize: 15)) .frame(height: 40)
Spacer() .background(
} RoundedRectangle(cornerRadius: 15)
.padding(.horizontal, 10) .fill(.white)
.padding(.top, 2) )
.padding(.bottom, 2) Spacer()
.frame(width: UIScreen.main.bounds.width, height: 30) if isLoading {
.background(Color("background")) LoadingView(isLoading: $isLoading)
.onTapGesture { }
if firstFavVPK == "" { if isFocused {
UserDefaults.standard.set(item.name, forKey: "vpk1") ScrollView(.vertical, showsIndicators: true) {
} else if secondFavVPK == "" { ForEach(vm.groups) { item in
UserDefaults.standard.set(item.name, forKey: "vpk2") if item.name.starts(with: "ВП") || item.name.starts(with: "мВ") {
} else { VStack {
UserDefaults.standard.set(item.name, forKey: "vpk3") Rectangle()
.frame(height: 1)
.foregroundColor(Color("customGray1"))
.padding(.horizontal, 10)
HStack {
Text(item.name)
.foregroundColor(.black)
.font(.custom("Montserrat-SemiBold", fixedSize: 15))
Spacer()
}
.padding(.horizontal, 10)
.padding(.top, 2)
.padding(.bottom, 2)
.frame(width: UIScreen.main.bounds.width, height: 30)
.background(Color("background"))
.onTapGesture {
UserDefaults.standard.set(item.name, forKey: "vpk")
vm.vpk = item.name
vm.fetchWeekVPK(vpk: item.name)
dismiss()
} }
vm.updateArrayOfGroups()
vm.fetchWeekSchedule()
dismiss()
} }
} }
} }
} }
} }
if !isFocused {
if favVPK != "" {
Button {
UserDefaults.standard.removeObject(forKey: "vpk")
vm.vpks.removeAll()
vm.vpk = ""
vm.vpkHTML = ""
dismiss()
} label: {
HStack {
Spacer()
Image(systemName: "trash")
Text("Удалить ВПК")
.font(.custom("Montserrat-Medium", fixedSize: 17))
Spacer()
}
.frame(height: 40)
.background(Color.white)
.foregroundColor(Color.red)
.cornerRadius(10)
.padding(.bottom, 50)
}
}
}
} }
.padding(.horizontal, 10)
.background(Color("background"))
} }
.padding(.horizontal, 10)
.background(Color("background"))
.onAppear { .onAppear {
serchGroupsVM.fetchGroups(group: "ВПК") vm.fetchGroups(group: "впк")
} }
} }
} }
#Preview { #Preview {
@Previewable @StateObject var vm = ScheduleViewModel() @Previewable @StateObject var vm = ScheduleViewModel()
SelectingVPKView(vm: vm, firstFavVPK: "", secondFavVPK: "", thirdFavVPK: "") SelectingVPKView(vm: vm)
} }

View File

@ -1,28 +0,0 @@
//
// TestingView.swift
// Schedule ICTIS
//
// Created by G412 on 05.03.2025.
//
import SwiftUI
struct TestingView: View {
var body: some View {
VStack {
Text("Hello")
Text("Hello")
Text("Hello")
Text("Hello")
Text("Hello")
Text("Hello")
Text("Hello")
Text("Hello")
Text("Hello")
}
}
}
#Preview {
TestingView()
}

View File

@ -187,11 +187,11 @@ extension WeekViewForWeek {
if let firstDate = weekSlider[currentWeekIndex].first?.date, if let firstDate = weekSlider[currentWeekIndex].first?.date,
currentWeekIndex == 0 { currentWeekIndex == 0 {
vm.week -= 1 vm.week -= 1
if !vm.nameGroups.isEmpty { if vm.group != "" {
vm.fetchWeekSchedule(isOtherWeek: true) vm.fetchWeekSchedule(isOtherWeek: true)
} }
if UserDefaults.standard.string(forKey: "vpk") != nil { if UserDefaults.standard.string(forKey: "vpk") != nil {
//vm.fetchWeekVPK(isOtherWeek: true, vpk: UserDefaults.standard.string(forKey: "vpk")) 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()
@ -203,9 +203,12 @@ extension WeekViewForWeek {
if let lastDate = weekSlider[currentWeekIndex].last?.date, if let lastDate = weekSlider[currentWeekIndex].last?.date,
currentWeekIndex == (weekSlider.count - 1) { currentWeekIndex == (weekSlider.count - 1) {
vm.week += 1 vm.week += 1
if !vm.nameGroups.isEmpty { 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,11 +252,11 @@ extension WeekViewForMonth {
} }
print(difBetweenWeeks) print(difBetweenWeeks)
vm.week += difBetweenWeeks vm.week += difBetweenWeeks
if !vm.nameGroups.isEmpty { if vm.group != "" {
vm.fetchWeekSchedule(isOtherWeek: true) vm.fetchWeekSchedule(isOtherWeek: true)
} }
if UserDefaults.standard.string(forKey: "vpk") != nil { if UserDefaults.standard.string(forKey: "vpk") != nil {
//vm.fetchWeekVPK(isOtherWeek: true, vpk: UserDefaults.standard.string(forKey: "vpk")) vm.fetchWeekVPK(isOtherWeek: true, vpk: UserDefaults.standard.string(forKey: "vpk"))
} }
} }
vm.selectedDay = day.date vm.selectedDay = day.date
@ -290,9 +293,12 @@ extension MonthTabView {
vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: -5, to: vm.selectedDay) ?? Date.init() vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: -5, to: vm.selectedDay) ?? Date.init()
vm.updateSelectedDayIndex() vm.updateSelectedDayIndex()
vm.week -= 5 vm.week -= 5
if !vm.nameGroups.isEmpty { 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,
@ -303,9 +309,12 @@ extension MonthTabView {
vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: 5, to: vm.selectedDay) ?? Date.init() vm.selectedDay = calendar.date(byAdding: .weekOfYear, value: 5, to: vm.selectedDay) ?? Date.init()
vm.updateSelectedDayIndex() vm.updateSelectedDayIndex()
vm.week += 5 vm.week += 5
if !vm.nameGroups.isEmpty { 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

@ -6,16 +6,10 @@
// //
import Foundation import Foundation
import SwiftUICore
@MainActor @MainActor
final class ScheduleViewModel: ObservableObject { final class ScheduleViewModel: ObservableObject {
//MARK: Properties //MARK: Properties
@Published var nameGroups: [String] = []
@Published var numbersNTMLGroups: [String] = []
@Published var classesGroups: [[ClassInfo]] = []
//Schedule //Schedule
@Published var weekScheduleGroup: Table = Table( @Published var weekScheduleGroup: Table = Table(
type: "", type: "",
@ -27,12 +21,14 @@ final class ScheduleViewModel: ObservableObject {
) )
@Published var selectedDay: Date = .init() @Published var selectedDay: Date = .init()
@Published var selectedIndex: Int = 1 @Published var selectedIndex: Int = 1
@Published var classes: [[String]] = []
@Published var week: Int = 0 @Published var week: Int = 0
@Published var numOfGroup: String = ""
@Published var isFirstStartOffApp = true @Published var isFirstStartOffApp = true
@Published var isShowingAlertForIncorrectGroup: Bool = false @Published var isShowingAlertForIncorrectGroup: Bool = false
@Published var errorInNetwork: NetworkError? @Published var errorInNetwork: NetworkError?
@Published var isLoading: Bool = false @Published var isLoading: Bool = false
@Published var group: String = ""
@Published var isNewGroup: Bool = false @Published var isNewGroup: Bool = false
//Groups //Groups
@ -50,63 +46,39 @@ final class ScheduleViewModel: ObservableObject {
link: "" link: ""
) )
//MARK: Methods //MARK: Methods
func fetchWeekSchedule(isOtherWeek: Bool = false) { func fetchWeekSchedule(group: String = "default", isOtherWeek: Bool = false) {
isLoading = true isLoading = true
Task { Task {
do { do {
var updatedClassesGroups: [[ClassInfo]] = Array(repeating: [], count: 6) // 6 дней (пн-сб) var schedule: Schedule
// В этот if мы заходим только если пользователь перелистывает недели и нам ИЗВЕСТНЫ номер группы(в html формате) и номер недели, которая показывается пользователю
if isOtherWeek { if (isOtherWeek || !isFirstStartOffApp) && (group == "default") {
for groupHTML in numbersNTMLGroups { schedule = try await NetworkManager.shared.getScheduleForOtherWeek(self.week, self.numOfGroup)
let schedule = try await NetworkManager.shared.getScheduleForOtherWeek(self.week, groupHTML)
let table = schedule.table.table
let nameOfGroup = schedule.table.name
// Преобразуем данные в формат ClassInfo
for (dayIndex, day) in table[2...].enumerated() { // Пропускаем первые две строки (заголовки)
for (timeIndex, subject) in day.enumerated() {
if !subject.isEmpty && timeIndex > 0 { // Пропускаем первый столбец (день и дату)
let time = table[1][timeIndex] // Время берем из второй строки
let classInfo = ClassInfo(subject: subject, group: nameOfGroup, time: time)
updatedClassesGroups[dayIndex].append(classInfo)
}
}
}
}
} else {
for groupName in nameGroups {
let schedule = try await NetworkManager.shared.getSchedule(groupName)
let numberHTML = schedule.table.group
self.numbersNTMLGroups.append(numberHTML)
let table = schedule.table.table
self.week = schedule.table.week
// Преобразуем данные в формат ClassInfo
for (dayIndex, day) in table[2...].enumerated() { // Пропускаем первые две строки (заголовки)
for (timeIndex, subject) in day.enumerated() {
if !subject.isEmpty && timeIndex > 0 { // Пропускаем первый столбец (день и дату)
let time = table[1][timeIndex] // Время берем из второй строки
let classInfo = ClassInfo(subject: subject, group: groupName, time: time)
updatedClassesGroups[dayIndex].append(classInfo)
}
}
}
}
} }
// В else мы заходим в том случае, если НЕ знаем номер недели, которую нужно отобразить и номер группы(в html формате)
// Обновляем данные else {
self.classesGroups = updatedClassesGroups print("Отладка 1")
schedule = try await NetworkManager.shared.getSchedule(group)
print("Отладка 2")
self.group = group
self.isNewGroup = true
self.selectedDay = .init()
}
self.weekScheduleGroup = schedule.table
self.week = weekScheduleGroup.week
self.numOfGroup = weekScheduleGroup.group
self.classes = weekScheduleGroup.table
self.isFirstStartOffApp = false self.isFirstStartOffApp = false
self.isShowingAlertForIncorrectGroup = false self.isShowingAlertForIncorrectGroup = false
self.isLoading = false self.isLoading = false
self.errorInNetwork = .noError self.errorInNetwork = .noError
print("Отладка 4")
// Сортируем по времени }
self.sortClassesByTime() catch {
} catch {
if let error = error as? NetworkError { if let error = error as? NetworkError {
switch error { switch (error) {
case .invalidResponse: case .invalidResponse:
errorInNetwork = .invalidResponse errorInNetwork = .invalidResponse
case .invalidData: case .invalidData:
@ -122,80 +94,87 @@ final class ScheduleViewModel: ObservableObject {
} }
} }
func updateSelectedDayIndex() { func fetchWeekVPK(isOtherWeek: Bool = false, vpk: String? = "default") {
switch selectedDay.format("E") { isLoading = true
case "Пн": Task {
selectedIndex = 0 do {
case "Вт": var tempVPKS: Schedule
selectedIndex = 1 // В этот if мы заходим только если пользователь перелистывает недели и нам известы номер ВПК(в html формате) и номер недели, которая показывается пользователю
case "Ср": if isOtherWeek && vpk != nil {
selectedIndex = 2 tempVPKS = try await NetworkManager.shared.getScheduleForOtherWeek(self.week, self.vpkHTML)
case "Чт": }
selectedIndex = 3 // В else мы заходим в том случае, если не знаем номер недели, которую нужно отобразить и номер группы(в html формате)
case "Пт": else {
selectedIndex = 4 tempVPKS = try await NetworkManager.shared.getSchedule(vpk!)
case "Сб": self.vpk = vpk!
selectedIndex = 5 self.selectedDay = .init()
default: }
selectedIndex = 6 self.weekScheduleVPK = tempVPKS.table
} self.vpkHTML = weekScheduleVPK.group
} self.vpks = weekScheduleVPK.table
print(self.vpk)
private func parseTime(_ timeString: String) -> Int { self.isShowingAlertForIncorrectGroup = false
// Разделяем строку по дефису и берем первую часть (время начала) self.isLoading = false
let startTimeString = timeString.components(separatedBy: "-").first ?? "" self.errorInNetwork = .noError
}
// Разделяем время на часы и минуты catch {
let components = startTimeString.components(separatedBy: ":") if let error = error as? NetworkError {
guard components.count == 2, switch (error) {
let hours = Int(components[0]), case .invalidResponse:
let minutes = Int(components[1]) else { errorInNetwork = .invalidResponse
return 0 // В случае ошибки возвращаем 0 case .invalidData:
} errorInNetwork = .invalidData
self.isShowingAlertForIncorrectGroup = true
// Преобразуем время в минуты с начала дня default:
return hours * 60 + minutes print("Неизвестная ошибка: \(error)")
} }
isLoading = false
// Method for sorting classes by time print("Есть ошибка: \(error)")
private func sortClassesByTime() { }
// Проходим по каждому дню (подмассиву) в classesGroups
for dayIndex in 0..<classesGroups.count {
// Сортируем подмассив по времени начала пары
classesGroups[dayIndex].sort { class1, class2 in
let time1 = parseTime(class1.time) // Время начала первой пары
let time2 = parseTime(class2.time) // Время начала второй пары
return time1 < time2 // Сортируем по возрастанию
} }
} }
} }
func updateArrayOfGroups() { func fetchGroups(group: String) {
self.nameGroups.removeAll() Task {
self.numbersNTMLGroups.removeAll() do {
let group1 = UserDefaults.standard.string(forKey: "group") var groups: Welcome
let group2 = UserDefaults.standard.string(forKey: "group2") groups = try await NetworkManager.shared.getGroups(group: group)
let group3 = UserDefaults.standard.string(forKey: "group3") self.groups = groups.choices
let vpk1 = UserDefaults.standard.string(forKey: "vpk1")
let vpk2 = UserDefaults.standard.string(forKey: "vpk2") }
let vpk3 = UserDefaults.standard.string(forKey: "vpk3") catch {
if let nameGroup1 = group1, nameGroup1 != "" { if let error = error as? NetworkError {
self.nameGroups.append(nameGroup1) switch (error) {
} case .invalidData:
if let nameGroup2 = group2, nameGroup2 != "" { self.groups.removeAll()
self.nameGroups.append(nameGroup2) default:
} self.groups.removeAll()
if let nameGroup3 = group3, nameGroup3 != "" { print("Неизвестная ошибка: \(error)")
self.nameGroups.append(nameGroup3) }
} print("Есть ошибка: \(error)")
if let nameVPK1 = vpk1, nameVPK1 != "" { }
self.nameGroups.append(nameVPK1) }
}
if let nameVPK2 = vpk2, nameVPK2 != "" {
self.nameGroups.append(nameVPK2)
}
if let nameVPK3 = vpk3, nameVPK3 != "" {
self.nameGroups.append(nameVPK3)
} }
} }
func updateSelectedDayIndex() {
switch selectedDay.format("E") {
case "Пн":
selectedIndex = 2
case "Вт":
selectedIndex = 3
case "Ср":
selectedIndex = 4
case "Чт":
selectedIndex = 5
case "Пт":
selectedIndex = 6
case "Сб":
selectedIndex = 7
default:
selectedIndex = 8
}
}
} }

View File

@ -1,36 +0,0 @@
//
// SearchGroupsViewModel.swift
// Schedule ICTIS
//
// Created by G412 on 06.03.2025.
//
import Foundation
@MainActor
final class SearchGroupsViewModel: ObservableObject {
@Published var groups: [Choice] = []
func fetchGroups(group: String) {
Task {
do {
var groups: Welcome
groups = try await NetworkManager.shared.getGroups(group: group)
self.groups = groups.choices
}
catch {
if let error = error as? NetworkError {
switch (error) {
case .invalidData:
self.groups.removeAll()
default:
self.groups.removeAll()
print("Неизвестная ошибка: \(error)")
}
print("Есть ошибка: \(error)")
}
}
}
}
}